Passed
Push — master ( ac7cac...af560c )
by Sébastien
02:21
created

ConversionService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools\Video;
6
7
use Soluble\MediaTools\Common\Assert\PathAssertionsTrait;
8
use Soluble\MediaTools\Common\Exception\FileNotFoundException;
9
use Soluble\MediaTools\Common\Exception\UnsupportedParamException;
10
use Soluble\MediaTools\Common\Exception\UnsupportedParamValueException;
11
use Soluble\MediaTools\Common\Process\ProcessParamsInterface;
12
use Soluble\MediaTools\Config\FFMpegConfigInterface;
13
use Soluble\MediaTools\Video\Adapter\FFMpegAdapter;
14
use Soluble\MediaTools\Video\Exception\ConversionExceptionInterface;
15
use Soluble\MediaTools\Video\Exception\ConversionProcessExceptionInterface;
16
use Soluble\MediaTools\Video\Exception\InvalidParamException;
17
use Soluble\MediaTools\Video\Exception\MissingInputFileException;
18
use Soluble\MediaTools\Video\Exception\ProcessFailedException;
19
use Soluble\MediaTools\Video\Exception\ProcessSignaledException;
20
use Soluble\MediaTools\Video\Exception\ProcessTimedOutException;
21
use Soluble\MediaTools\Video\Exception\RuntimeException;
22
use Symfony\Component\Process\Exception as SPException;
23
use Symfony\Component\Process\Process;
24
25
class ConversionService implements ConversionServiceInterface
26
{
27
    use PathAssertionsTrait;
28
29
    /** @var ProcessParamsInterface */
30
    protected $processParams;
31
32
    /** @var FFMpegAdapter */
33
    protected $adapter;
34
35 10
    public function __construct(FFMpegConfigInterface $ffmpegConfig)
36
    {
37 10
        $this->adapter       = new FFMpegAdapter($ffmpegConfig);
38 10
        $this->processParams = $ffmpegConfig;
39 10
    }
40
41
    /**
42
     * Return ready-to-run symfony process object that you can use
43
     * to `run()` or `start()` programmatically. Useful if you want to make
44
     * things async...
45
     *
46
     * @see https://symfony.com/doc/current/components/process.html
47
     *
48
     * @throws UnsupportedParamException
49
     * @throws UnsupportedParamValueException
50
     */
51 9
    public function getSymfonyProcess(string $inputFile, string $outputFile, ConversionParamsInterface $convertParams, ?ProcessParamsInterface $processParams = null): Process
52
    {
53 9
        if (!$convertParams->hasParam(ConversionParamsInterface::PARAM_THREADS)
54 9
            && $this->adapter->getDefaultThreads() !== null) {
55 1
            $convertParams = $convertParams->withBuiltInParam(
56 1
                ConversionParamsInterface::PARAM_THREADS,
57 1
                $this->adapter->getDefaultThreads()
58
            );
59
        }
60
61 9
        $arguments = $this->adapter->getMappedConversionParams($convertParams);
62 9
        $ffmpegCmd = $this->adapter->getCliCommand($arguments, $inputFile, $outputFile);
63
64 9
        $pp = $processParams ?? $this->processParams;
65
66 9
        $process = new Process($ffmpegCmd);
67 9
        $process->setTimeout($pp->getTimeout());
68 9
        $process->setIdleTimeout($pp->getIdleTimeout());
69 9
        $process->setEnv($pp->getEnv());
70
71 9
        return $process;
72
    }
73
74
    /**
75
     * Run a conversion, throw exception on error.
76
     *
77
     * @param callable|null $callback A PHP callback to run whenever there is some
78
     *                                tmp available on STDOUT or STDERR
79
     *
80
     * @throws ConversionExceptionInterface        Base exception class for conversion exceptions
81
     * @throws ConversionProcessExceptionInterface Base exception class for process conversion exceptions
82
     * @throws MissingInputFileException
83
     * @throws ProcessTimedOutException
84
     * @throws ProcessFailedException
85
     * @throws ProcessSignaledException
86
     * @throws InvalidParamException
87
     * @throws RuntimeException
88
     */
89 8
    public function convert(string $inputFile, string $outputFile, ConversionParamsInterface $convertParams, ?callable $callback = null, ?ProcessParamsInterface $processParams = null): void
90
    {
91
        try {
92 8
            $this->ensureFileExists($inputFile);
93 7
            $process = $this->getSymfonyProcess($inputFile, $outputFile, $convertParams, $processParams);
94 7
            $process->mustRun($callback);
95 6
        } catch (FileNotFoundException $e) {
96 1
            throw new MissingInputFileException($e->getMessage());
97 5
        } catch (UnsupportedParamValueException | UnsupportedParamException $e) {
98
            throw new InvalidParamException($e->getMessage());
99 5
        } catch (SPException\ProcessTimedOutException $e) {
100 3
            throw new ProcessTimedOutException($e->getProcess(), $e);
101 2
        } catch (SPException\ProcessSignaledException $e) {
102
            throw new ProcessSignaledException($e->getProcess(), $e);
103 2
        } catch (SPException\ProcessFailedException $e) {
104 2
            throw new ProcessFailedException($e->getProcess(), $e);
105
        } catch (SPException\RuntimeException $e) {
106
            throw new RuntimeException($e->getMessage());
107
        }
108 2
    }
109
110
    /*
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...
111
     * FOR LATER REFERENCE !!!
112
    public function convertMultiPass(string $videoFile, string $outputFile, ConversionParams $convertParams, VideoFilterInterface $videoFilter=null): void {
113
114
        $this->ensureFileExists($videoFile);
115
        if ($videoFilter === null) {
116
            $videoFilter = new EmptyVideoFilter();
117
        }
118
119
120
        $threads = $convertParams->getOption(VideoConversionParams::OPTION_THREADS, $this->ffmpegConfig->getThreads());
121
122
        $ffmpegBin = $this->ffmpegConfig->getBinary();
123
124
        $commonArgs = array_merge([
125
                $ffmpegBin,
126
                sprintf('-i %s', escapeshellarg($videoFile)), // input filename
127
                $videoFilter->getFFMpegCliArgument(), // add -vf yadif,nlmeans
128
                ($threads === null) ? '' : sprintf('-threads %s', $threads),
129
        ], $convertParams->getFFMpegArguments());
130
131
        $pass1Cmd = implode(' ', array_merge(
132
            $commonArgs,
133
            [
134
                '-pass 1',
135
                // tells VP9 to encode really fast, sacrificing quality. Useful to speed up the first pass.
136
                '-speed 4',
137
                '-y /dev/null',
138
            ]
139
        ));
140
141
        $pass2Cmd = implode( ' ', array_merge(
142
            $commonArgs,
143
            [
144
                '-pass 2',
145
                // speed 1 is a good speed vs. quality compromise.
146
                // Produces tmp quality typically very close to speed 0, but usually encodes much faster.
147
                '-speed 1',
148
                '-y',
149
                sprintf("%s", escapeshellarg($outputFile))
150
            ]
151
        ));
152
153
154
        $process = new Process($pass1Cmd);
155
        $process->setTimeout(null);
156
        $process->setIdleTimeout(60); // 60 seconds without tmp will stop the process
157
        $process->start();
158
        foreach ($process as $type => $data) {
159
            if ($process::OUT === $type) {
160
                echo "\nRead from stdout: ".$data;
161
            } else { // $process::ERR === $type
162
                echo "\nRead from stderr: ".$data;
163
            }
164
        }
165
166
        $process = new Process($pass2Cmd);
167
        $process->setTimeout(null);
168
        $process->setIdleTimeout(60); // 60 seconds without tmp will stop the process
169
        $process->start();
170
        foreach ($process as $type => $data) {
171
            if ($process::OUT === $type) {
172
                echo "\nRead from stdout: ".$data;
173
            } else { // $process::ERR === $type
174
                echo "\nRead from stderr: ".$data;
175
            }
176
        }
177
178
    }
179
    */
180
}
181