Passed
Push — master ( 756023...fcf7ca )
by Sébastien
03:23
created

ConversionParams::withoutParam()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
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\BitrateAssertionsTrait;
8
use Soluble\MediaTools\Video\Adapter\FFMpegCLIValueInterface;
9
use Soluble\MediaTools\Video\Exception\InvalidArgumentException;
10
use Soluble\MediaTools\Video\Filter\Type\VideoFilterInterface;
11
12
class ConversionParams implements ConversionParamsInterface
13
{
14
    use BitrateAssertionsTrait;
15
16
    /** @var array<string, bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface> */
17
    protected $params = [];
18
19
    /**
20
     * @param array<string, bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface> $params
21
     *
22
     * @throws InvalidArgumentException in case of unsupported option
23
     */
24 32
    public function __construct($params = [])
25
    {
26 32
        $this->ensureSupportedParams($params);
27 31
        $this->params = $params;
28 31
    }
29
30 11
    public function withVideoCodec(string $videoCodec): self
31
    {
32 11
        return new self(array_merge($this->params, [
33 11
            self::PARAM_VIDEO_CODEC => $videoCodec,
34
        ]));
35
    }
36
37 10
    public function withVideoFilter(VideoFilterInterface $videoFilter): self
38
    {
39 10
        return new self(array_merge($this->params, [
40 10
            self::PARAM_VIDEO_FILTER => $videoFilter,
41
        ]));
42
    }
43
44 4
    public function withAudioCodec(string $audioCodec): self
45
    {
46 4
        return new self(array_merge($this->params, [
47 4
            self::PARAM_AUDIO_CODEC => $audioCodec,
48
        ]));
49
    }
50
51
    /**
52
     * Set TileColumns (VP9 - to use in conjunction with FrameParallel).
53
     *
54
     * Tiling splits the video frame into multiple columns,
55
     * which slightly reduces quality but speeds up encoding performance.
56
     * Tiles must be at least 256 pixels wide, so there is a limit to how many tiles can be used.
57
     * Depending upon the number of tiles and the resolution of the tmp frame, more CPU threads may be useful.
58
     *
59
     * Generally speaking, there is limited value to multiple threads when the tmp frame size is very small.
60
     *
61
     * @see self::withFrameParallel()
62
     */
63 7
    public function withTileColumns(int $tileColumns): self
64
    {
65 7
        return new self(array_merge($this->params, [
66 7
            self::PARAM_TILE_COLUMNS => $tileColumns,
67
        ]));
68
    }
69
70
    /**
71
     * Set FrameParallel (VP9 - to use in conjunction with TileColumns).
72
     *
73
     * @see self::withTileColumns()
74
     */
75 4
    public function withFrameParallel(int $frameParallel): self
76
    {
77 4
        return new self(array_merge($this->params, [
78 4
            self::PARAM_FRAME_PARALLEL => $frameParallel,
79
        ]));
80
    }
81
82
    /**
83
     * Set KeyFrameSpacing (VP9).
84
     *
85
     * It is recommended to allow up to 240 frames of video between keyframes (8 seconds for 30fps content).
86
     * Keyframes are video frames which are self-sufficient; they don't rely upon any other frames to render
87
     * but they tend to be larger than other frame types.
88
     *
89
     * For web and mobile playback, generous spacing between keyframes allows the encoder to choose the best
90
     * placement of keyframes to maximize quality.
91
     */
92 4
    public function withKeyframeSpacing(int $keyframeSpacing): self
93
    {
94 4
        return new self(array_merge($this->params, [
95 4
            self::PARAM_KEYFRAME_SPACING => $keyframeSpacing,
96
        ]));
97
    }
98
99
    /**
100
     * Set compression level (Constant Rate Factor).
101
     */
102 4
    public function withCrf(int $crf): self
103
    {
104 4
        return new self(array_merge($this->params, [
105 4
            self::PARAM_CRF => $crf,
106
        ]));
107
    }
108
109 4
    public function withPixFmt(string $pixFmt): self
110
    {
111 4
        return new self(array_merge($this->params, [
112 4
            self::PARAM_PIX_FMT => $pixFmt,
113
        ]));
114
    }
115
116 3
    public function withPreset(string $preset): self
117
    {
118 3
        return new self(array_merge($this->params, [
119 3
            self::PARAM_PRESET => $preset,
120
        ]));
121
    }
122
123 4
    public function withSpeed(int $speed): self
124
    {
125 4
        return new self(array_merge($this->params, [
126 4
            self::PARAM_SPEED => $speed,
127
        ]));
128
    }
129
130 6
    public function withThreads(int $threads): self
131
    {
132 6
        return new self(array_merge($this->params, [
133 6
            self::PARAM_THREADS => $threads,
134
        ]));
135
    }
136
137 4
    public function withTune(string $tune): self
138
    {
139 4
        return new self(array_merge($this->params, [
140 4
            self::PARAM_TUNE => $tune,
141
        ]));
142
    }
143
144
    /**
145
     * If true, add streamable options for mp4 container (-movflags +faststart).
146
     */
147 2
    public function withStreamable(bool $streamable): self
148
    {
149 2
        return new self(array_merge($this->params, [
150 2
            self::PARAM_STREAMABLE => $streamable,
151
        ]));
152
    }
153
154
    /**
155
     * @param string $bitrate Bitrate with optional unit: 1000000, 1000k or 1M
156
     *
157
     * @throws InvalidArgumentException if bitrate value is invalid
158
     */
159 5
    public function withAudioBitrate(string $bitrate): self
160
    {
161 5
        $this->ensureValidBitRateUnit($bitrate);
162
163 4
        return new self(array_merge($this->params, [
164 4
            self::PARAM_AUDIO_BITRATE => $bitrate,
165
        ]));
166
    }
167
168
    /**
169
     * @param string $bitrate Bitrate or target bitrate with optional unit: 1000000, 1000k or 1M
170
     *
171
     * @throws InvalidArgumentException if bitrate value is invalid
172
     */
173 5
    public function withVideoBitrate(string $bitrate): self
174
    {
175 5
        $this->ensureValidBitRateUnit($bitrate);
176
177 4
        return new self(array_merge($this->params, [
178 4
            self::PARAM_VIDEO_BITRATE => $bitrate,
179
        ]));
180
    }
181
182
    /**
183
     * @param string $minBitrate Bitrate with optional unit: 1000000, 1000k or 1M
184
     *
185
     * @throws InvalidArgumentException if bitrate value is invalid
186
     */
187 5
    public function withVideoMinBitrate(string $minBitrate): self
188
    {
189 5
        $this->ensureValidBitRateUnit($minBitrate);
190
191 4
        return new self(array_merge($this->params, [
192 4
            self::PARAM_VIDEO_MIN_BITRATE => $minBitrate,
193
        ]));
194
    }
195
196
    /**
197
     * @param string $maxBitrate Bitrate with optional unit: 1000000, 1000k or 1M
198
     *
199
     * @throws InvalidArgumentException if bitrate value is invalid
200
     */
201 5
    public function withVideoMaxBitrate(string $maxBitrate): self
202
    {
203 5
        $this->ensureValidBitRateUnit($maxBitrate);
204
205 4
        return new self(array_merge($this->params, [
206 4
            self::PARAM_VIDEO_MAX_BITRATE => $maxBitrate,
207
        ]));
208
    }
209
210
    /**
211
     * Add with overwrite option (default).
212
     *
213
     * @see self::withNoOverwrite()
214
     */
215 6
    public function withOverwrite(): self
216
    {
217 6
        return new self(array_merge($this->params, [
218 6
            self::PARAM_OVERWRITE => true
219
        ]));
220
    }
221
222
    /**
223
     * Add protection against output file overwriting.
224
     *
225
     * @see self::witoOverwrite()
226
     */
227 1
    public function withNoOverwrite(): self
228
    {
229 1
        return new self(array_merge($this->params, [
230 1
            self::PARAM_OVERWRITE => false
231
        ]));
232
    }
233
234 2
    public function withQuality(string $quality): self
235
    {
236 2
        return new self(array_merge($this->params, [
237 2
            self::PARAM_QUALITY => $quality,
238
        ]));
239
    }
240
241 6
    public function withOutputFormat(string $outputFormat): self
242
    {
243 6
        return new self(array_merge($this->params, [
244 6
            self::PARAM_OUTPUT_FORMAT => $outputFormat,
245
        ]));
246
    }
247
248 9
    public function withSeekStart(SeekTime $seekTimeStart): self
249
    {
250 9
        return new self(array_merge($this->params, [
251 9
            self::PARAM_SEEK_START => $seekTimeStart,
252
        ]));
253
    }
254
255 3
    public function withSeekEnd(SeekTime $seekTimeEnd): self
256
    {
257 3
        return new self(array_merge($this->params, [
258 3
            self::PARAM_SEEK_END => $seekTimeEnd,
259
        ]));
260
    }
261
262 4
    public function withNoAudio(): self
263
    {
264 4
        return new self(array_merge($this->params, [
265 4
            self::PARAM_NOAUDIO => true,
266
        ]));
267
    }
268
269 10
    public function withVideoFrames(int $numberOfFrames): self
270
    {
271 10
        return new self(array_merge($this->params, [
272 10
            self::PARAM_VIDEO_FRAMES => $numberOfFrames,
273
        ]));
274
    }
275
276
    /**
277
     * Set the video encoder quality scale. (-qscale:v <int>, alias to -q:v <int>).
278
     *
279
     * @param int $qualityScale a number interpreted by the encoder, generally 1-5
280
     *
281
     * @see self::withQuality()
282
     */
283 4
    public function withVideoQualityScale(int $qualityScale): self
284
    {
285 4
        return new self(array_merge($this->params, [
286 4
            self::PARAM_VIDEO_QUALITY_SCALE => $qualityScale,
287
        ]));
288
    }
289
290
    /**
291
     * @param bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface $paramValue
292
     *
293
     * @throws InvalidArgumentException in case of unsupported builtin param
294
     *
295
     * @return self (For static analysis the trick is to return 'self' instead of interface)
296
     */
297 17
    public function withBuiltInParam(string $paramName, $paramValue): ConversionParamsInterface
298
    {
299 17
        return new self(array_merge($this->params, [
300 17
            $paramName => $paramValue,
301
        ]));
302
    }
303
304
    /**
305
     * @return self (For static analysis the trick is to return 'self' instead of interface)
306
     */
307 1
    public function withoutParam(string $paramName): ConversionParamsInterface
308
    {
309 1
        $ao = (new \ArrayObject($this->params));
310 1
        if ($ao->offsetExists($paramName)) {
311 1
            $ao->offsetUnset($paramName);
312
        }
313
314 1
        return new self($ao->getArrayCopy());
315
    }
316
317
    /**
318
     * Return the internal array holding params.
319
     *
320
     * @return array<string,bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface>
321
     */
322 24
    public function toArray(): array
323
    {
324 24
        return $this->params;
325
    }
326
327 29
    public function isParamValid(string $paramName): bool
328
    {
329 29
        return in_array($paramName, self::BUILTIN_PARAMS, true);
330
    }
331
332
    /**
333
     * @param bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface|null $defaultValue if param does not exists set this one
334
     *
335
     * @return bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface|null
336
     */
337 2
    public function getParam(string $paramName, $defaultValue = null)
338
    {
339 2
        return $this->params[$paramName] ?? $defaultValue;
340
    }
341
342 22
    public function hasParam(string $paramName): bool
343
    {
344 22
        return array_key_exists($paramName, $this->params);
345
    }
346
347
    /**
348
     * Ensure that all params are supported.
349
     *
350
     * @param array<string, bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface> $params
351
     *
352
     * @throws InvalidArgumentException in case of unsupported option
353
     */
354 32
    protected function ensureSupportedParams(array $params): void
355
    {
356 32
        foreach ($params as $paramName => $paramValue) {
357 29
            if (!$this->isParamValid($paramName)) {
358 1
                throw new InvalidArgumentException(
359 29
                    sprintf('Unsupported param "%s" given.', $paramName)
360
                );
361
            }
362
        }
363 31
    }
364
}
365