Failed Conditions
Push — master ( 85c284...561f65 )
by Sébastien
02:44
created

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