Failed Conditions
Push — master ( bd9fc8...d384bc )
by Sébastien
03:00
created

VideoProbe   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Test Coverage

Coverage 8.62%

Importance

Changes 0
Metric Value
wmc 11
dl 0
loc 107
ccs 5
cts 58
cp 0.0862
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
B guessInterlacing() 0 62 8
A getMediaInfo() 0 20 2
A __construct() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools;
6
7
use Soluble\MediaTools\Config\FFMpegConfig;
8
use Soluble\MediaTools\Config\FFProbeConfig;
9
use Soluble\MediaTools\Detection\InterlacementGuess;
10
use Soluble\MediaTools\Util\CommonAssertionsTrait;
11
use Symfony\Component\Process\Process;
12
13
class VideoProbe
14
{
15
    use CommonAssertionsTrait;
16
17
    /** @var FFProbeConfig */
18
    protected $ffprobeConfig;
19
20
    /** @var FFMpegConfig */
21
    protected $ffmpegConfig;
22
23
    /** @var mixed[] */
24
    protected $cache = [];
25
26 1
    public function __construct(FFProbeConfig $ffProbeConfig, FFMpegConfig $ffmpegConfig)
27
    {
28 1
        $this->ffprobeConfig = $ffProbeConfig;
29 1
        $this->ffmpegConfig  = $ffmpegConfig;
30 1
        $this->ffprobeConfig->getProcess()->ensureBinaryExists();
31 1
    }
32
33
    public function getMediaInfo(string $file): VideoInfo
34
    {
35
        $this->ensureFileExists($file);
36
        $process = $this->ffprobeConfig->getProcess();
37
        $cmd     = $process->buildCommand([
38
            '-v quiet',
39
            '-print_format json',
40
            '-show_format',
41
            '-show_streams',
42
            sprintf('-i "%s"', $file),
43
        ]);
44
45
        try {
46
            $jsonOutput = $process->runCommand($cmd);
47
        } catch (\Throwable $e) {
48
            //$msg = $e->getMessage();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
49
            throw $e;
50
        }
51
52
        return VideoInfo::createFromFFProbeJson($file, $jsonOutput);
53
    }
54
55
    /**
56
     * @throws \Throwable
57
     */
58
    public function guessInterlacing(string $file, float $threshold = InterlacementGuess::INTERLACING_DETECTION_THRESHOLD, int $framesToAnalyze = 1000): InterlacementGuess
59
    {
60
        $cache_key = md5(sprintf('%s:%s:%s:%s', __METHOD__, $file, $threshold, $framesToAnalyze));
61
        if (isset($this->cache[$cache_key]) && $this->cache[$cache_key] instanceof InterlacementGuess) {
62
            return $this->cache[$cache_key];
63
        }
64
65
        $nbFrames       = $this->getMediaInfo($file)->getNbFrames();
66
        $analyzedFrames = ($nbFrames > 0)
67
                            ? min($framesToAnalyze, $nbFrames)
68
                            : $framesToAnalyze;
69
70
        // Step 2: Using frame detection
71
        $ffmpegProcess = $this->ffmpegConfig->getProcess();
72
73
        $ffmpegCmd = $ffmpegProcess->buildCommand(
74
            [
75
                sprintf('-i %s', escapeshellarg($file)),
76
                '-filter idet',
77
                sprintf('-frames:v %d', $analyzedFrames),
78
                '-an', // audio can be discarded
79
                '-f rawvideo', // output in raw
80
                '-y /dev/null', // discard the output
81
            ]
82
        );
83
84
        $process = new Process($ffmpegCmd);
85
        $process->mustRun();
86
        $stdErr = preg_split("/(\r\n|\n|\r)/", $process->getErrorOutput());
87
88
        // Counted frames
89
        $interlaced_tff = 0;
90
        $interlaced_bff = 0;
91
        $progressive    = 0;
92
        $undetermined   = 0;
93
        $total_frames   = 0;
94
95
        if ($stdErr !== false) {
96
            foreach ($stdErr as $line) {
97
                if (mb_substr($line, 0, 12) !== '[Parsed_idet') {
98
                    continue;
99
                }
100
101
                $unspaced = preg_replace('/( )+/', '', $line);
102
                $matches  = [];
103
                if (preg_match_all('/TFF:(\d+)BFF:(\d+)Progressive:(\d+)Undetermined:(\d+)/i', $unspaced, $matches) < 1) {
104
                    continue;
105
                }
106
107
                //$type = strpos(strtolower($unspaced), 'single') ? 'single' : 'multi';
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
108
                $interlaced_tff += (int) $matches[1][0];
109
                $interlaced_bff += (int) $matches[2][0];
110
                $progressive += (int) $matches[3][0];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
111
                $undetermined += (int) $matches[4][0];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
112
                $total_frames += ((int) $matches[1][0] + (int) $matches[2][0] + (int) $matches[3][0] + (int) $matches[4][0]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
113
            }
114
        }
115
116
        $guess                   = new InterlacementGuess($interlaced_tff, $interlaced_bff, $progressive, $undetermined);
117
        $this->cache[$cache_key] = $guess;
118
119
        return $guess;
120
    }
121
122
    /*
123
     * Use ffprobe to determine if a video is POTENTIALLY interlaced...
124
     * Warning:
125
     *  - There's no absolute truth in that. but if false it's not
126
     *  - Very slow.
127
     */
128
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
51% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
129
    public function isVideoPotentiallyInterlaced(string $file): bool {
130
131
        $this->ensureFileExists($file);
132
133
        // Step 1: ffprobe to see if interlaced
134
        $cmd = $this->buildFFProbeCommand([
135
            sprintf('-i "%s"', $file),
136
            '-v quiet',             // do not display any messages
137
            '-select_streams v',    // select video stream
138
            '-show_entries "frame=pkt_pts_time,pkt_duration_time,interlaced_frame"',
139
            '-print_format json'
140
        ]);
141
142
        $output = $this->runCommand($cmd);
143
144
        $decoded = json_decode($output, true);
145
        if ($decoded === null || $output === '') {
146
            throw new JsonParseException(sprintf(
147
                'Cannot parse output from ffprobe (%s)', $cmd
148
            ));
149
        }
150
        $frame_stats = [
151
            'nb_frames' => count($decoded['frames']),
152
            'nb_interlaced' => 0,
153
        ];
154
        foreach ($decoded['frames'] as $frame) {
155
            if ((int) $frame['interlaced_frame'] === 1) {
156
                $frame_stats['nb_interlaced']++;
157
            }
158
        }
159
160
161
        if ($frame_stats['nb_interlaced'] === 0 && $frame_stats['nb_frames'] > 0) {
162
            // here we know for sure it is not
163
            return false;
164
        }
165
166
        return true;
167
    }
168
    */
169
}
170