Failed Conditions
Push — master ( 45d676...509703 )
by Sébastien
05:56
created

ConversionService   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 75
Duplicated Lines 0 %

Test Coverage

Coverage 96.3%

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 75
rs 10
c 0
b 0
f 0
ccs 26
cts 27
cp 0.963
wmc 11

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getSymfonyProcess() 0 18 3
B convert() 0 18 7
A __construct() 0 3 1
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\ProcessFactory;
12
use Soluble\MediaTools\Common\Process\ProcessParamsInterface;
13
use Soluble\MediaTools\Video\Config\FFMpegConfigInterface;
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 FFMpegConfigInterface */
30
    protected $ffmpegConfig;
31
32
    public function __construct(FFMpegConfigInterface $ffmpegConfig)
33
    {
34
        $this->ffmpegConfig  = $ffmpegConfig;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

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