Completed
Push — master ( 94616b...d1942c )
by Sébastien
03:09
created

ConverterService::getConversionProcess()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3.0026

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 28
c 0
b 0
f 0
ccs 14
cts 15
cp 0.9333
rs 9.7998
cc 3
nc 2
nop 3
crap 3.0026
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools\Video;
6
7
use Soluble\MediaTools\Config\FFMpegConfig;
8
use Soluble\MediaTools\Exception\FileNotFoundException;
9
use Soluble\MediaTools\Exception\ProcessConversionException;
10
use Soluble\MediaTools\Util\Assert\PathAssertionsTrait;
11
use Soluble\MediaTools\Video\Converter\ParamsInterface;
12
use Soluble\MediaTools\VideoConvertParams;
13
use Symfony\Component\Process\Exception as ProcessException;
14
use Symfony\Component\Process\Process;
15
16
class ConverterService implements ConverterServiceInterface
17
{
18
    use PathAssertionsTrait;
19
20
    /** @var FFMpegConfig */
21
    protected $ffmpegConfig;
22
23
    /** @var ProbeService */
24
    protected $videoProbe;
25
26 5
    public function __construct(FFMpegConfig $ffmpegConfig, ProbeService $videoProbe)
27
    {
28 5
        $this->videoProbe   = $videoProbe;
29 5
        $this->ffmpegConfig = $ffmpegConfig;
30 5
        $this->ffmpegConfig->getProcess()->ensureBinaryExists();
31 5
    }
32
33
    /**
34
     * Return ready-to-run symfony process object that you can use
35
     * to `run()` or `start()` programmatically. Useful if you want to make
36
     * things async...
37
     *
38
     * @see https://symfony.com/doc/current/components/process.html
39
     *
40
     * @throws FileNotFoundException when inputFile does not exists
41
     */
42 5
    public function getConversionProcess(string $inputFile, string $outputFile, VideoConvertParams $convertParams): Process
43
    {
44 5
        $this->ensureFileExists($inputFile);
45
46 4
        $process = $this->ffmpegConfig->getProcess();
47
48 4
        if (!$convertParams->hasOption(ParamsInterface::PARAM_THREADS) && $this->ffmpegConfig->getThreads() !== null) {
49
            $convertParams = $convertParams->withThreads($this->ffmpegConfig->getThreads());
50
        }
51
52 4
        $ffmpegCmd = $process->buildCommand(
53 4
            array_merge(
54
                [
55 4
                    sprintf('-i %s', escapeshellarg($inputFile)), // input filename
56
                ],
57 4
                $convertParams->getFFMpegArguments(),
58
                [
59 4
                    '-y', // tell to overwrite
60 4
                    sprintf('%s', escapeshellarg($outputFile)),
61
                ]
62
            )
63
        );
64
65 4
        $process = new Process($ffmpegCmd);
66 4
        $process->setTimeout($this->ffmpegConfig->getConversionTimeout());
67 4
        $process->setIdleTimeout($this->ffmpegConfig->getConversionIdleTimeout());
68
69 4
        return $process;
70
    }
71
72
    /**
73
     * Run a conversion, throw exception on error.
74
     *
75
     * @param callable|null                 $callback A PHP callback to run whenever there is some
76
     *                                                output available on STDOUT or STDERR
77
     * @param array<string,string|int>|null $env      An array of env vars to set
78
     *                                                when running the process
79
     *
80
     * @throws FileNotFoundException      When inputFile does not exists
81
     * @throws ProcessConversionException When the ffmpeg process conversion failed
82
     */
83 5
    public function convert(string $inputFile, string $outputFile, VideoConvertParams $convertParams, ?callable $callback = null, ?array $env = null): void
84
    {
85 5
        $process = $this->getConversionProcess($inputFile, $outputFile, $convertParams);
86
87
        try {
88 4
            $process->mustRun($callback, (is_array($env) ? $env : $this->ffmpegConfig->getConversionEnv()));
89 2
        } catch (ProcessException\RuntimeException $symfonyProcessException) {
90
            // will include: ProcessFailedException|ProcessTimedOutException|ProcessSignaledException
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% 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...
91 2
            throw new ProcessConversionException($process, $symfonyProcessException);
92
        } catch (FileNotFoundException $e) {
93
            throw $e;
94
        }
95 2
    }
96
97
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
98
     * FOR LATER REFERENCE !!!
99
    public function convertMultiPass(string $videoFile, string $outputFile, VideoConvertParams $convertParams, VideoFilterInterface $videoFilter=null): void {
100
101
        $this->ensureFileExists($videoFile);
102
        if ($videoFilter === null) {
103
            $videoFilter = new EmptyVideoFilter();
104
        }
105
106
107
        $threads = $convertParams->getOption(VideoConvertParams::OPTION_THREADS, $this->ffmpegConfig->getThreads());
108
109
        $ffmpegBin = $this->ffmpegConfig->getBinary();
110
111
        $commonArgs = array_merge([
112
                $ffmpegBin,
113
                sprintf('-i %s', escapeshellarg($videoFile)), // input filename
114
                $videoFilter->getFFMpegCliArgument(), // add -vf yadif,nlmeans
115
                ($threads === null) ? '' : sprintf('-threads %s', $threads),
116
        ], $convertParams->getFFMpegArguments());
117
118
        $pass1Cmd = implode(' ', array_merge(
119
            $commonArgs,
120
            [
121
                '-pass 1',
122
                // tells VP9 to encode really fast, sacrificing quality. Useful to speed up the first pass.
123
                '-speed 4',
124
                '-y /dev/null',
125
            ]
126
        ));
127
128
        $pass2Cmd = implode( ' ', array_merge(
129
            $commonArgs,
130
            [
131
                '-pass 2',
132
                // speed 1 is a good speed vs. quality compromise.
133
                // Produces output quality typically very close to speed 0, but usually encodes much faster.
134
                '-speed 1',
135
                '-y',
136
                sprintf("%s", escapeshellarg($outputFile))
137
            ]
138
        ));
139
140
141
        $process = new Process($pass1Cmd);
142
        $process->setTimeout(null);
143
        $process->setIdleTimeout(60); // 60 seconds without output will stop the process
144
        $process->start();
145
        foreach ($process as $type => $data) {
146
            if ($process::OUT === $type) {
147
                echo "\nRead from stdout: ".$data;
148
            } else { // $process::ERR === $type
149
                echo "\nRead from stderr: ".$data;
150
            }
151
        }
152
153
        $process = new Process($pass2Cmd);
154
        $process->setTimeout(null);
155
        $process->setIdleTimeout(60); // 60 seconds without output will stop the process
156
        $process->start();
157
        foreach ($process as $type => $data) {
158
            if ($process::OUT === $type) {
159
                echo "\nRead from stdout: ".$data;
160
            } else { // $process::ERR === $type
161
                echo "\nRead from stderr: ".$data;
162
            }
163
        }
164
165
    }
166
    */
167
}
168