Passed
Push — master ( e6784e...ebeb6b )
by Sébastien
02:41
created

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