Completed
Push — master ( aea97d...ec8061 )
by Sébastien
04:37
created

VideoConversionService::getSymfonyProcess()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 17
ccs 11
cts 11
cp 1
rs 9.9332
c 0
b 0
f 0
cc 3
nc 2
nop 3
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools;
6
7
use Soluble\MediaTools\Config\FFMpegConfigInterface;
8
use Soluble\MediaTools\Exception\FileNotFoundException;
9
use Soluble\MediaTools\Exception\ProcessConversionException;
10
use Soluble\MediaTools\Util\Assert\PathAssertionsTrait;
11
use Soluble\MediaTools\Video\ConversionParamsInterface;
12
use Soluble\MediaTools\Video\ConversionServiceInterface;
13
use Soluble\MediaTools\Video\Converter\FFMpegAdapter;
14
use Symfony\Component\Process\Exception as SPException;
15
use Symfony\Component\Process\Process;
16
17
class VideoConversionService implements ConversionServiceInterface
18
{
19
    use PathAssertionsTrait;
20
21
    /** @var FFMpegConfigInterface */
22
    protected $ffmpegConfig;
23
24
    /** @var FFMpegAdapter */
25
    protected $converter;
26
27 7
    public function __construct(FFMpegConfigInterface $ffmpegConfig)
28
    {
29 7
        $this->ffmpegConfig = $ffmpegConfig;
30 7
        $this->converter    = new FFMpegAdapter($ffmpegConfig);
31 7
    }
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 7
    public function getSymfonyProcess(string $inputFile, string $outputFile, VideoConversionParams $convertParams): Process
43
    {
44 7
        $this->ensureFileExists($inputFile);
45
46 6
        if (!$convertParams->hasParam(ConversionParamsInterface::PARAM_THREADS) && $this->ffmpegConfig->getThreads() !== null) {
47 1
            $convertParams = $convertParams->withThreads($this->ffmpegConfig->getThreads());
48
        }
49
50 6
        $arguments = $this->converter->getMappedConversionParams($convertParams);
51 6
        $ffmpegCmd = $this->converter->getCliCommand($arguments, $inputFile, $outputFile);
52
53 6
        $process = new Process($ffmpegCmd);
54 6
        $process->setTimeout($this->ffmpegConfig->getTimeout());
55 6
        $process->setIdleTimeout($this->ffmpegConfig->getIdleTimeout());
56 6
        $process->setEnv($this->ffmpegConfig->getEnv());
57
58 6
        return $process;
59
    }
60
61
    /**
62
     * Run a conversion, throw exception on error.
63
     *
64
     * @param callable|null $callback A PHP callback to run whenever there is some
65
     *                                tmp available on STDOUT or STDERR
66
     *
67
     * @throws FileNotFoundException      When inputFile does not exists
68
     * @throws ProcessConversionException When the ffmpeg process conversion failed
69
     */
70 5
    public function convert(string $inputFile, string $outputFile, VideoConversionParams $convertParams, ?callable $callback = null): void
71
    {
72 5
        $process = $this->getSymfonyProcess($inputFile, $outputFile, $convertParams);
73
74
        try {
75 4
            $process->mustRun($callback);
76 2
        } catch (SPException\RuntimeException $symfonyProcessException) {
77
            // 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...
78 2
            throw new ProcessConversionException($process, $symfonyProcessException);
79
        }
80 2
    }
81
82
    /*
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...
83
     * FOR LATER REFERENCE !!!
84
    public function convertMultiPass(string $videoFile, string $outputFile, VideoConversionParams $convertParams, VideoFilterInterface $videoFilter=null): void {
85
86
        $this->ensureFileExists($videoFile);
87
        if ($videoFilter === null) {
88
            $videoFilter = new EmptyVideoFilter();
89
        }
90
91
92
        $threads = $convertParams->getOption(VideoConversionParams::OPTION_THREADS, $this->ffmpegConfig->getThreads());
93
94
        $ffmpegBin = $this->ffmpegConfig->getBinary();
95
96
        $commonArgs = array_merge([
97
                $ffmpegBin,
98
                sprintf('-i %s', escapeshellarg($videoFile)), // input filename
99
                $videoFilter->getFFMpegCliArgument(), // add -vf yadif,nlmeans
100
                ($threads === null) ? '' : sprintf('-threads %s', $threads),
101
        ], $convertParams->getFFMpegArguments());
102
103
        $pass1Cmd = implode(' ', array_merge(
104
            $commonArgs,
105
            [
106
                '-pass 1',
107
                // tells VP9 to encode really fast, sacrificing quality. Useful to speed up the first pass.
108
                '-speed 4',
109
                '-y /dev/null',
110
            ]
111
        ));
112
113
        $pass2Cmd = implode( ' ', array_merge(
114
            $commonArgs,
115
            [
116
                '-pass 2',
117
                // speed 1 is a good speed vs. quality compromise.
118
                // Produces tmp quality typically very close to speed 0, but usually encodes much faster.
119
                '-speed 1',
120
                '-y',
121
                sprintf("%s", escapeshellarg($outputFile))
122
            ]
123
        ));
124
125
126
        $process = new Process($pass1Cmd);
127
        $process->setTimeout(null);
128
        $process->setIdleTimeout(60); // 60 seconds without tmp will stop the process
129
        $process->start();
130
        foreach ($process as $type => $data) {
131
            if ($process::OUT === $type) {
132
                echo "\nRead from stdout: ".$data;
133
            } else { // $process::ERR === $type
134
                echo "\nRead from stderr: ".$data;
135
            }
136
        }
137
138
        $process = new Process($pass2Cmd);
139
        $process->setTimeout(null);
140
        $process->setIdleTimeout(60); // 60 seconds without tmp will stop the process
141
        $process->start();
142
        foreach ($process as $type => $data) {
143
            if ($process::OUT === $type) {
144
                echo "\nRead from stdout: ".$data;
145
            } else { // $process::ERR === $type
146
                echo "\nRead from stderr: ".$data;
147
            }
148
        }
149
150
    }
151
    */
152
}
153