Completed
Push — master ( e68911...94616b )
by Sébastien
05:58
created

Probe::guessInterlacing()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 62
ccs 0
cts 40
cp 0
rs 7.5846
c 0
b 0
f 0
cc 8
nc 5
nop 3
crap 72

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Util\Assert\PathAssertionsTrait;
10
use Soluble\MediaTools\Video\Detection\InterlacementGuess;
11
use Soluble\MediaTools\Video\ProbeServiceInterface;
12
use Symfony\Component\Process\Process;
13
14
class Probe implements ProbeServiceInterface
15
{
16
    use PathAssertionsTrait;
17
18
    /** @var FFProbeConfig */
19
    protected $ffprobeConfig;
20
21
    /** @var FFMpegConfig */
22
    protected $ffmpegConfig;
23
24
    /** @var mixed[] */
25
    protected $cache = [];
26
27 5
    public function __construct(FFProbeConfig $ffProbeConfig, FFMpegConfig $ffmpegConfig)
28
    {
29 5
        $this->ffprobeConfig = $ffProbeConfig;
30 5
        $this->ffmpegConfig  = $ffmpegConfig;
31 5
        $this->ffprobeConfig->getProcess()->ensureBinaryExists();
32 5
    }
33
34
    public function getMediaInfo(string $file): VideoInfo
35
    {
36
        $this->ensureFileExists($file);
37
        $process = $this->ffprobeConfig->getProcess();
38
        $cmd     = $process->buildCommand([
39
            '-v quiet',
40
            '-print_format json',
41
            '-show_format',
42
            '-show_streams',
43
            sprintf('-i "%s"', $file),
44
        ]);
45
46
        try {
47
            $jsonOutput = $process->runCommand($cmd);
48
        } catch (\Throwable $e) {
49
            //$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...
50
            throw $e;
51
        }
52
53
        return VideoInfo::createFromFFProbeJson($file, $jsonOutput);
54
    }
55
56
    /**
57
     * @throws \Throwable
58
     */
59
    public function guessInterlacing(string $file, float $threshold = InterlacementGuess::INTERLACING_DETECTION_THRESHOLD, int $framesToAnalyze = 1000): InterlacementGuess
60
    {
61
        $cache_key = md5(sprintf('%s:%s:%s:%s', __METHOD__, $file, $threshold, $framesToAnalyze));
62
        if (isset($this->cache[$cache_key]) && $this->cache[$cache_key] instanceof InterlacementGuess) {
63
            return $this->cache[$cache_key];
64
        }
65
66
        $nbFrames       = $this->getMediaInfo($file)->getNbFrames();
67
        $analyzedFrames = ($nbFrames > 0)
68
                            ? min($framesToAnalyze, $nbFrames)
69
                            : $framesToAnalyze;
70
71
        // Step 2: Using frame detection
72
        $ffmpegProcess = $this->ffmpegConfig->getProcess();
73
74
        $ffmpegCmd = $ffmpegProcess->buildCommand(
75
            [
76
                sprintf('-i %s', escapeshellarg($file)),
77
                '-filter idet',
78
                sprintf('-frames:v %d', $analyzedFrames),
79
                '-an', // audio can be discarded
80
                '-f rawvideo', // output in raw
81
                '-y /dev/null', // discard the output
82
            ]
83
        );
84
85
        $process = new Process($ffmpegCmd);
86
        $process->mustRun();
87
        $stdErr = preg_split("/(\r\n|\n|\r)/", $process->getErrorOutput());
88
89
        // Counted frames
90
        $interlaced_tff = 0;
91
        $interlaced_bff = 0;
92
        $progressive    = 0;
93
        $undetermined   = 0;
94
        $total_frames   = 0;
95
96
        if ($stdErr !== false) {
97
            foreach ($stdErr as $line) {
98
                if (mb_substr($line, 0, 12) !== '[Parsed_idet') {
99
                    continue;
100
                }
101
102
                $unspaced = preg_replace('/( )+/', '', $line);
103
                $matches  = [];
104
                if (preg_match_all('/TFF:(\d+)BFF:(\d+)Progressive:(\d+)Undetermined:(\d+)/i', $unspaced, $matches) < 1) {
105
                    continue;
106
                }
107
108
                //$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...
109
                $interlaced_tff += (int) $matches[1][0];
110
                $interlaced_bff += (int) $matches[2][0];
111
                $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...
112
                $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...
113
                $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...
114
            }
115
        }
116
117
        $guess                   = new InterlacementGuess($interlaced_tff, $interlaced_bff, $progressive, $undetermined);
118
        $this->cache[$cache_key] = $guess;
119
120
        return $guess;
121
    }
122
123
    /*
124
     * Use ffprobe to determine if a video is POTENTIALLY interlaced...
125
     * Warning:
126
     *  - There's no absolute truth in that. but if false it's not
127
     *  - Very slow.
128
     */
129
    /*
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...
130
    public function isVideoPotentiallyInterlaced(string $file): bool {
131
132
        $this->ensureFileExists($file);
133
134
        // Step 1: ffprobe to see if interlaced
135
        $cmd = $this->buildFFProbeCommand([
136
            sprintf('-i "%s"', $file),
137
            '-v quiet',             // do not display any messages
138
            '-select_streams v',    // select video stream
139
            '-show_entries "frame=pkt_pts_time,pkt_duration_time,interlaced_frame"',
140
            '-print_format json'
141
        ]);
142
143
        $output = $this->runCommand($cmd);
144
145
        $decoded = json_decode($output, true);
146
        if ($decoded === null || $output === '') {
147
            throw new JsonParseException(sprintf(
148
                'Cannot parse output from ffprobe (%s)', $cmd
149
            ));
150
        }
151
        $frame_stats = [
152
            'nb_frames' => count($decoded['frames']),
153
            'nb_interlaced' => 0,
154
        ];
155
        foreach ($decoded['frames'] as $frame) {
156
            if ((int) $frame['interlaced_frame'] === 1) {
157
                $frame_stats['nb_interlaced']++;
158
            }
159
        }
160
161
162
        if ($frame_stats['nb_interlaced'] === 0 && $frame_stats['nb_frames'] > 0) {
163
            // here we know for sure it is not
164
            return false;
165
        }
166
167
        return true;
168
    }
169
    */
170
}
171