InterlaceDetect   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Test Coverage

Coverage 95.35%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 45
c 1
b 0
f 0
dl 0
loc 80
ccs 41
cts 43
cp 0.9535
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B guessInterlacing() 0 59 8
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @see       https://github.com/soluble-io/soluble-mediatools for the canonical repository
7
 *
8
 * @copyright Copyright (c) 2018-2020 Sébastien Vanvelthem. (https://github.com/belgattitude)
9
 * @license   https://github.com/soluble-io/soluble-mediatools/blob/master/LICENSE.md MIT
10
 */
11
12
namespace Soluble\MediaTools\Video\Detection;
13
14
use Soluble\MediaTools\Common\Assert\PathAssertionsTrait;
15
use Soluble\MediaTools\Common\Exception\FileEmptyException;
16
use Soluble\MediaTools\Common\Exception\FileNotFoundException;
17
use Soluble\MediaTools\Common\Exception\FileNotReadableException;
18
use Soluble\MediaTools\Common\IO\PlatformNullFile;
19
use Soluble\MediaTools\Common\Process\ProcessFactory;
20
use Soluble\MediaTools\Common\Process\ProcessParamsInterface;
21
use Soluble\MediaTools\Video\Config\FFMpegConfigInterface;
22
use Soluble\MediaTools\Video\Exception\AnalyzerExceptionInterface;
23
use Soluble\MediaTools\Video\Exception\AnalyzerProcessExceptionInterface;
24
use Soluble\MediaTools\Video\Exception\MissingInputFileException;
25
use Soluble\MediaTools\Video\Exception\ProcessFailedException;
26
use Soluble\MediaTools\Video\Exception\RuntimeReaderException;
27
use Soluble\MediaTools\Video\Filter\IdetVideoFilter;
28
use Soluble\MediaTools\Video\VideoConvertParams;
29
use Symfony\Component\Process\Exception as SPException;
30
31
final class InterlaceDetect
32
{
33
    use PathAssertionsTrait;
34
35
    public const DEFAULT_INTERLACE_MAX_FRAMES = 1000;
36
37
    /** @var FFMpegConfigInterface */
38
    private $ffmpegConfig;
39
40 4
    public function __construct(FFMpegConfigInterface $ffmpegConfig)
41
    {
42 4
        $this->ffmpegConfig = $ffmpegConfig;
43 4
    }
44
45
    /**
46
     * @throws AnalyzerExceptionInterface
47
     * @throws AnalyzerProcessExceptionInterface
48
     * @throws ProcessFailedException
49
     * @throws MissingInputFileException
50
     * @throws RuntimeReaderException
51
     */
52 4
    public function guessInterlacing(string $file, int $maxFramesToAnalyze = self::DEFAULT_INTERLACE_MAX_FRAMES, ?ProcessParamsInterface $processParams = null): InterlaceDetectGuess
53
    {
54 4
        $adapter = $this->ffmpegConfig->getAdapter();
55 4
        $params  = (new VideoConvertParams())
56 4
            ->withVideoFilter(new IdetVideoFilter()) // detect interlaced frames :)
57 4
            ->withVideoFrames($maxFramesToAnalyze)
58 4
            ->withNoAudio() // speed up the thing
59 4
            ->withOutputFormat('rawvideo')
60 4
            ->withOverwrite();
61
62
        try {
63 4
            $this->ensureFileReadable($file, true);
64
65 2
            $arguments = $adapter->getMappedConversionParams($params);
66 2
            $ffmpegCmd = $adapter->getCliCommand($arguments, $file, new PlatformNullFile());
67
68 2
            $pp = $processParams ?? $this->ffmpegConfig->getProcessParams();
69
70 2
            $process = (new ProcessFactory($ffmpegCmd, $pp))->__invoke();
71 2
            $process->mustRun();
72 3
        } catch (FileNotFoundException | FileNotReadableException | FileEmptyException $e) {
73 2
            throw new MissingInputFileException($e->getMessage());
74 1
        } catch (SPException\ProcessFailedException | SPException\ProcessTimedOutException | SPException\ProcessSignaledException $e) {
75 1
            throw new ProcessFailedException($e->getProcess(), $e);
76
        } catch (SPException\RuntimeException $e) {
77
            throw new RuntimeReaderException($e->getMessage());
78
        }
79
80 1
        $stdErr = preg_split("/(\r\n|\n|\r)/", $process->getErrorOutput());
81
82
        // Counted frames
83 1
        $interlaced_tff = 0;
84 1
        $interlaced_bff = 0;
85 1
        $progressive    = 0;
86 1
        $undetermined   = 0;
87 1
        $total_frames   = 0;
88
89 1
        if ($stdErr !== false) {
90 1
            foreach ($stdErr as $line) {
91 1
                if (mb_substr($line, 0, 12) !== '[Parsed_idet') {
92 1
                    continue;
93
                }
94
95 1
                $unspaced = sprintf('%s', preg_replace('/( )+/', '', $line));
96 1
                $matches  = [];
97 1
                if (preg_match_all('/TFF:(\d+)BFF:(\d+)Progressive:(\d+)Undetermined:(\d+)/i', $unspaced, $matches) < 1) {
98 1
                    continue;
99
                }
100
101
                //$type = strpos(strtolower($unspaced), 'single') ? 'single' : 'multi';
102 1
                $interlaced_tff += (int) $matches[1][0];
103 1
                $interlaced_bff += (int) $matches[2][0];
104 1
                $progressive += (int) $matches[3][0];
105 1
                $undetermined += (int) $matches[4][0];
106 1
                $total_frames += ((int) $matches[1][0] + (int) $matches[2][0] + (int) $matches[3][0] + (int) $matches[4][0]);
107
            }
108
        }
109
110 1
        return new InterlaceDetectGuess($interlaced_tff, $interlaced_bff, $progressive, $undetermined);
111
    }
112
}
113