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

src/Video/ConversionService.php (1 issue)

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
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
    /*
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