Failed Conditions
Push — master ( a3d1dc...25914a )
by Sébastien
02:18
created

FFMpegAdapter   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Test Coverage

Coverage 95.45%

Importance

Changes 0
Metric Value
wmc 20
eloc 115
dl 0
loc 212
ccs 84
cts 88
cp 0.9545
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
B getCliCommand() 0 32 6
A __construct() 0 3 1
B getMappedConversionParams() 0 48 11
A getDefaultThreads() 0 3 1
B getParamsOptions() 0 89 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Soluble\MediaTools\Video\Adapter;
6
7
use Soluble\MediaTools\Common\Exception\InvalidArgumentException;
8
use Soluble\MediaTools\Common\Exception\UnsupportedParamException;
9
use Soluble\MediaTools\Common\Exception\UnsupportedParamValueException;
10
use Soluble\MediaTools\Common\IO\UnescapedFileInterface;
11
use Soluble\MediaTools\Video\Adapter\Validator\FFMpegParamValidator;
12
use Soluble\MediaTools\Video\Config\FFMpegConfigInterface;
13
use Soluble\MediaTools\Video\Exception\ParamValidationException;
14
use Soluble\MediaTools\Video\VideoConvertParamsInterface;
15
16
class FFMpegAdapter implements ConverterAdapterInterface
17
{
18
    /** @var FFMpegConfigInterface */
19
    protected $ffmpegConfig;
20
21 31
    public function __construct(FFMpegConfigInterface $ffmpegConfig)
22
    {
23 31
        $this->ffmpegConfig = $ffmpegConfig;
24 31
    }
25
26
    /**
27
     * @return array<string, array<string, string>>
28
     */
29 30
    public function getParamsOptions(): array
30
    {
31
        return [
32 30
            VideoConvertParamsInterface::PARAM_OUTPUT_FORMAT => [
33
                'pattern' => '-f %s',
34
            ],
35 30
            VideoConvertParamsInterface::PARAM_VIDEO_CODEC => [
36
                'pattern' => '-c:v %s',
37
            ],
38 30
            VideoConvertParamsInterface::PARAM_VIDEO_BITRATE => [
39
                'pattern' => '-b:v %s',
40
            ],
41 30
            VideoConvertParamsInterface::PARAM_VIDEO_MIN_BITRATE => [
42
                'pattern' => '-minrate %s',
43
            ],
44 30
            VideoConvertParamsInterface::PARAM_VIDEO_MAX_BITRATE => [
45
                'pattern' => '-maxrate %s',
46
            ],
47 30
            VideoConvertParamsInterface::PARAM_AUDIO_CODEC => [
48
                'pattern' => '-c:a %s',
49
            ],
50 30
            VideoConvertParamsInterface::PARAM_AUDIO_BITRATE => [
51
                'pattern' => '-b:a %s',
52
            ],
53 30
            VideoConvertParamsInterface::PARAM_PIX_FMT => [
54
                'pattern' => '-pix_fmt %s',
55
            ],
56 30
            VideoConvertParamsInterface::PARAM_PRESET => [
57
                'pattern' => '-preset %s',
58
            ],
59 30
            VideoConvertParamsInterface::PARAM_SPEED => [
60
                'pattern' => '-speed %d',
61
            ],
62 30
            VideoConvertParamsInterface::PARAM_THREADS => [
63
                'pattern' => '-threads %d',
64
            ],
65 30
            VideoConvertParamsInterface::PARAM_KEYFRAME_SPACING => [
66
                'pattern' => '-g %d',
67
            ],
68 30
            VideoConvertParamsInterface::PARAM_QUALITY => [
69
                'pattern' => '-quality %s',
70
            ],
71 30
            VideoConvertParamsInterface::PARAM_VIDEO_QUALITY_SCALE => [
72
                'pattern' => '-qscale:v %d',
73
            ],
74 30
            VideoConvertParamsInterface::PARAM_CRF => [
75
                'pattern' => '-crf %d',
76
            ],
77 30
            VideoConvertParamsInterface::PARAM_STREAMABLE => [
78
                'pattern' => '-movflags +faststart',
79
            ],
80 30
            VideoConvertParamsInterface::PARAM_FRAME_PARALLEL => [
81
                'pattern' => '-frame-parallel %s',
82
            ],
83 30
            VideoConvertParamsInterface::PARAM_TILE_COLUMNS => [
84
                'pattern' => '-tile-columns %s',
85
            ],
86 30
            VideoConvertParamsInterface::PARAM_TUNE => [
87
                'pattern' => '-tune %s',
88
            ],
89 30
            VideoConvertParamsInterface::PARAM_VIDEO_FILTER => [
90
                'pattern' => '-filter:v %s',
91
            ],
92 30
            VideoConvertParamsInterface::PARAM_OVERWRITE => [
93
                'pattern' => '-y',
94
            ],
95 30
            VideoConvertParamsInterface::PARAM_VIDEO_FRAMES => [
96
                'pattern' => '-frames:v %d',
97
            ],
98 30
            VideoConvertParamsInterface::PARAM_NOAUDIO => [
99
                'pattern' => '-an',
100
            ],
101 30
            VideoConvertParamsInterface::PARAM_SEEK_START => [
102
                'pattern' => '-ss %s',
103
            ],
104 30
            VideoConvertParamsInterface::PARAM_SEEK_END => [
105
                'pattern' => '-to %s',
106
            ],
107 30
            VideoConvertParamsInterface::PARAM_PASSLOGFILE => [
108
                'pattern' => '-passlogfile %s',
109
            ],
110 30
            VideoConvertParamsInterface::PARAM_PASS => [
111
                'pattern' => '-pass %s',
112
            ],
113 30
            VideoConvertParamsInterface::PARAM_AUTO_ALT_REF => [
114
                'pattern' => '-auto-alt-ref %s',
115
            ],
116 30
            VideoConvertParamsInterface::PARAM_LAG_IN_FRAMES => [
117
                'pattern' => '-lag-in-frames %s',
118
            ],
119
        ];
120
    }
121
122
    /**
123
     * Return an array version of params suitable for ffmpeg cli.
124
     *
125
     * @param bool $validateParams whether to run ffmpeg validation process validation
126
     *
127
     * @return array<string, string>
128
     *
129
     * @throws UnsupportedParamException
130
     * @throws UnsupportedParamValueException
131
     * @throws ParamValidationException
132
     */
133 30
    public function getMappedConversionParams(VideoConvertParamsInterface $conversionParams, bool $validateParams = true): array
134
    {
135 30
        $args             = [];
136 30
        $supportedOptions = $this->getParamsOptions();
137
138
        // Add default overwrite option if not set
139 30
        $overwriteParam = VideoConvertParamsInterface::PARAM_OVERWRITE;
140 30
        if (!$conversionParams->hasParam($overwriteParam)) {
141 26
            $conversionParams = $conversionParams->withBuiltInParam(
142 26
                $overwriteParam,
143 26
                true
144
            );
145
        }
146
147 30
        foreach ($conversionParams->toArray() as $paramName => $value) {
148 30
            if (!array_key_exists($paramName, $supportedOptions)) {
149
                throw new UnsupportedParamException(
150
                    sprintf(
151
                        'FFMpegAdapter does not support param \'%s\'',
152
                        $paramName
153
                    )
154
                );
155
            }
156 30
            $pattern = $supportedOptions[$paramName]['pattern'];
157 30
            if (is_bool($value)) {
158 29
                $args[$paramName] = $value ? $pattern : '';
159 27
            } elseif ($value instanceof FFMpegCLIValueInterface) {
160
                // Will test also FFMpegVideoFilterInterface
161 15
                $args[$paramName] = sprintf($pattern, $value->getFFmpegCLIValue());
162 25
            } elseif (is_string($value) || is_int($value)) {
163 24
                $args[$paramName] = sprintf($pattern, $value);
164
            } else {
165 1
                throw new UnsupportedParamValueException(
166 1
                    sprintf(
167 1
                        'Param \'%s\' has an unsupported type: \'%s\'',
168 1
                        $paramName,
169 30
                        is_object($value) ? get_class($value) : gettype($value)
170
                    )
171
                );
172
            }
173
        }
174
175
        // Validation
176 29
        if ($validateParams) {
177 29
            (new FFMpegParamValidator($conversionParams))->validate();
178
        }
179
180 28
        return $args;
181
    }
182
183
    /**
184
     * @param array<string,string>               $arguments        args that will be added
185
     * @param null|string                        $inputFile
186
     * @param null|string|UnescapedFileInterface $outputFile
187
     * @param array<string,string>               $prependArguments args that must be added at the beginning of the command
188
     *
189
     * @throws InvalidArgumentException
190
     */
191 24
    public function getCliCommand(array $arguments, ?string $inputFile, $outputFile = null, array $prependArguments = []): string
192
    {
193 24
        $inputArg = ($inputFile !== null && $inputFile !== '')
194 24
                        ? sprintf('-i %s', escapeshellarg($inputFile))
195 24
                        : '';
196
197 24
        $outputArg = '';
198 24
        if ($outputFile instanceof UnescapedFileInterface) {
199 5
            $outputArg = $outputFile->getFile();
200 21
        } elseif (is_string($outputFile)) {
201 19
            $outputArg = sprintf('%s', escapeshellarg($outputFile));
202 2
        } elseif ($outputFile !== null) {
0 ignored issues
show
introduced by
The condition $outputFile !== null is always false.
Loading history...
203 2
            throw new InvalidArgumentException(sprintf(
204 2
                'Output file must be either a non empty string, null or PlatformNullFile (type %s)',
205 2
                gettype($outputFile)
206
            ));
207
        }
208
209 22
        $ffmpegCmd = preg_replace(
210 22
            '/(\ ){2,}/',
211 22
            ' ',
212 22
            trim(sprintf(
213 22
                '%s %s %s %s %s',
214 22
                $this->ffmpegConfig->getBinary(),
215 22
                implode(' ', $prependArguments),
216 22
                $inputArg,
217 22
                implode(' ', $arguments),
218 22
                $outputArg
219
            ))
220
        );
221
222 22
        return $ffmpegCmd;
223
    }
224
225 17
    public function getDefaultThreads(): ?int
226
    {
227 17
        return $this->ffmpegConfig->getThreads();
228
    }
229
}
230