Passed
Push — master ( bf86e6...5ea322 )
by Sébastien
07:21
created

ConversionParams::withNoOverwrite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
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\Common\Exception\InvalidArgumentException;
9
use Soluble\MediaTools\Video\Adapter\FFMpegCLIValueInterface;
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
    public function __construct($params = [])
25
    {
26
        $this->ensureSupportedParams($params);
27
        $this->params = $params;
28
    }
29
30
    public function withVideoCodec(string $videoCodec): self
31
    {
32
        return new self(array_merge($this->params, [
33
            self::PARAM_VIDEO_CODEC => $videoCodec,
34
        ]));
35
    }
36
37
    public function withVideoFilter(VideoFilterInterface $videoFilter): self
38
    {
39
        return new self(array_merge($this->params, [
40
            self::PARAM_VIDEO_FILTER => $videoFilter,
41
        ]));
42
    }
43
44
    public function withAudioCodec(string $audioCodec): self
45
    {
46
        return new self(array_merge($this->params, [
47
            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 VideoConversionParams::withFrameParallel()
62
     */
63
    public function withTileColumns(int $tileColumns): self
64
    {
65
        return new self(array_merge($this->params, [
66
            self::PARAM_TILE_COLUMNS => $tileColumns,
67
        ]));
68
    }
69
70
    /**
71
     * Set FrameParallel (VP9 - to use in conjunction with TileColumns).
72
     *
73
     * @see VideoConversionParams::withTileColumns()
74
     */
75
    public function withFrameParallel(int $frameParallel): self
76
    {
77
        return new self(array_merge($this->params, [
78
            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
    public function withKeyframeSpacing(int $keyframeSpacing): self
93
    {
94
        return new self(array_merge($this->params, [
95
            self::PARAM_KEYFRAME_SPACING => $keyframeSpacing,
96
        ]));
97
    }
98
99
    /**
100
     * Set compression level (Constant Rate Factor).
101
     */
102
    public function withCrf(int $crf): self
103
    {
104
        return new self(array_merge($this->params, [
105
            self::PARAM_CRF => $crf,
106
        ]));
107
    }
108
109
    public function withPixFmt(string $pixFmt): self
110
    {
111
        return new self(array_merge($this->params, [
112
            self::PARAM_PIX_FMT => $pixFmt,
113
        ]));
114
    }
115
116
    public function withPreset(string $preset): self
117
    {
118
        return new self(array_merge($this->params, [
119
            self::PARAM_PRESET => $preset,
120
        ]));
121
    }
122
123
    public function withSpeed(int $speed): self
124
    {
125
        return new self(array_merge($this->params, [
126
            self::PARAM_SPEED => $speed,
127
        ]));
128
    }
129
130
    public function withThreads(int $threads): self
131
    {
132
        return new self(array_merge($this->params, [
133
            self::PARAM_THREADS => $threads,
134
        ]));
135
    }
136
137
    public function withTune(string $tune): self
138
    {
139
        return new self(array_merge($this->params, [
140
            self::PARAM_TUNE => $tune,
141
        ]));
142
    }
143
144
    /**
145
     * If true, add streamable options for mp4 container (-movflags +faststart).
146
     */
147
    public function withStreamable(bool $streamable): self
148
    {
149
        return new self(array_merge($this->params, [
150
            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
    public function withAudioBitrate(string $bitrate): self
160
    {
161
        $this->ensureValidBitRateUnit($bitrate);
162
163
        return new self(array_merge($this->params, [
164
            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
    public function withVideoBitrate(string $bitrate): self
174
    {
175
        $this->ensureValidBitRateUnit($bitrate);
176
177
        return new self(array_merge($this->params, [
178
            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
    public function withVideoMinBitrate(string $minBitrate): self
188
    {
189
        $this->ensureValidBitRateUnit($minBitrate);
190
191
        return new self(array_merge($this->params, [
192
            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
    public function withVideoMaxBitrate(string $maxBitrate): self
202
    {
203
        $this->ensureValidBitRateUnit($maxBitrate);
204
205
        return new self(array_merge($this->params, [
206
            self::PARAM_VIDEO_MAX_BITRATE => $maxBitrate,
207
        ]));
208
    }
209
210
    /**
211
     * Add with overwrite option (default).
212
     *
213
     * @see VideoConversionParams::withNoOverwrite()
214
     */
215
    public function withOverwrite(): self
216
    {
217
        return new self(array_merge($this->params, [
218
            self::PARAM_OVERWRITE => true
219
        ]));
220
    }
221
222
    /**
223
     * Add protection against output file overwriting.
224
     *
225
     * @see VideoConversionParams::witoOverwrite()
226
     */
227
    public function withNoOverwrite(): self
228
    {
229
        return new self(array_merge($this->params, [
230
            self::PARAM_OVERWRITE => false
231
        ]));
232
    }
233
234
    public function withQuality(string $quality): self
235
    {
236
        return new self(array_merge($this->params, [
237
            self::PARAM_QUALITY => $quality,
238
        ]));
239
    }
240
241
    public function withOutputFormat(string $outputFormat): self
242
    {
243
        return new self(array_merge($this->params, [
244
            self::PARAM_OUTPUT_FORMAT => $outputFormat,
245
        ]));
246
    }
247
248
    public function withSeekStart(SeekTime $seekTimeStart): self
249
    {
250
        return new self(array_merge($this->params, [
251
            self::PARAM_SEEK_START => $seekTimeStart,
252
        ]));
253
    }
254
255
    public function withSeekEnd(SeekTime $seekTimeEnd): self
256
    {
257
        return new self(array_merge($this->params, [
258
            self::PARAM_SEEK_END => $seekTimeEnd,
259
        ]));
260
    }
261
262
    public function withNoAudio(): self
263
    {
264
        return new self(array_merge($this->params, [
265
            self::PARAM_NOAUDIO => true,
266
        ]));
267
    }
268
269
    public function withVideoFrames(int $numberOfFrames): self
270
    {
271
        return new self(array_merge($this->params, [
272
            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 VideoConversionParams::withQuality()
282
     */
283
    public function withVideoQualityScale(int $qualityScale): self
284
    {
285
        return new self(array_merge($this->params, [
286
            self::PARAM_VIDEO_QUALITY_SCALE => $qualityScale,
287
        ]));
288
    }
289
290
    /**
291
     * Set a built-in param...
292
     *
293
     * @param bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface $paramValue
294
     *
295
     * @throws InvalidArgumentException in case of unsupported builtin param
296
     */
297
    public function withBuiltInParam(string $paramName, $paramValue): ConversionParamsInterface
298
    {
299
        return new self(array_merge($this->params, [
300
            $paramName => $paramValue,
301
        ]));
302
    }
303
304
    /**
305
     * Return the internal array holding params.
306
     *
307
     * @return array<string,bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface>
308
     */
309
    public function toArray(): array
310
    {
311
        return $this->params;
312
    }
313
314
    public function isParamValid(string $paramName): bool
315
    {
316
        return in_array($paramName, self::BUILTIN_PARAMS, true);
317
    }
318
319
    /**
320
     * @param bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface|null $defaultValue if param does not exists set this one
321
     *
322
     * @return bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface|null
323
     */
324
    public function getParam(string $paramName, $defaultValue = null)
325
    {
326
        return $this->params[$paramName] ?? $defaultValue;
327
    }
328
329
    public function hasParam(string $paramName): bool
330
    {
331
        return array_key_exists($paramName, $this->params);
332
    }
333
334
    /**
335
     * Ensure that all params are supported.
336
     *
337
     * @param array<string, bool|string|int|VideoFilterInterface|FFMpegCLIValueInterface> $params
338
     *
339
     * @throws InvalidArgumentException in case of unsupported option
340
     */
341
    protected function ensureSupportedParams(array $params): void
342
    {
343
        foreach ($params as $paramName => $paramValue) {
344
            if (!$this->isParamValid($paramName)) {
345
                throw new InvalidArgumentException(
346
                    sprintf('Unsupported param "%s" given.', $paramName)
347
                );
348
            }
349
        }
350
    }
351
}
352