Completed
Push — master ( ed08d3...1d1353 )
by Pascal
14s queued 13s
created

PHPFFMpeg   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 73
dl 0
loc 262
rs 9.68
c 0
b 0
f 0
wmc 34

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getDurationInSeconds() 0 3 1
A getVideoStream() 0 4 1
A getPendingComplexFilters() 0 3 1
A frame() 0 5 1
A __construct() 0 4 1
A getDurationInMiliseconds() 0 12 3
A addFilter() 0 36 6
A getFilters() 0 3 1
A isFrame() 0 3 1
A addFilterAsComplexFilter() 0 9 1
A __call() 0 5 2
A concatWithoutTranscoding() 0 8 1
A isAdvancedMedia() 0 3 1
A getAudioStream() 0 4 1
A get() 0 3 1
A getMediaCollection() 0 3 1
A isConcat() 0 3 1
A openAdvanced() 0 5 1
A __invoke() 0 3 1
A open() 0 17 4
A fresh() 0 3 1
A getStreams() 0 9 2
1
<?php
2
3
namespace ProtoneMedia\LaravelFFMpeg\Drivers;
4
5
use Closure;
6
use FFMpeg\Coordinate\TimeCode;
7
use FFMpeg\FFMpeg;
8
use FFMpeg\FFProbe\DataMapping\Stream;
9
use FFMpeg\Filters\Audio\SimpleFilter;
10
use FFMpeg\Filters\FilterInterface;
11
use FFMpeg\Media\AbstractMediaType;
12
use FFMpeg\Media\AdvancedMedia;
13
use FFMpeg\Media\Concat;
14
use FFMpeg\Media\Frame;
15
use Illuminate\Support\Arr;
16
use Illuminate\Support\Collection;
17
use Illuminate\Support\Traits\ForwardsCalls;
18
use ProtoneMedia\LaravelFFMpeg\FFMpeg\LegacyFilterMapping;
19
use ProtoneMedia\LaravelFFMpeg\Filesystem\Media;
20
use ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection;
21
22
class PHPFFMpeg
23
{
24
    use ForwardsCalls;
25
26
    /**
27
     * @var \FFMpeg\FFMpeg
28
     */
29
    private $ffmpeg;
30
31
    /**
32
     * @var \ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection
33
     */
34
    private $mediaCollection;
35
36
    /**
37
     * @var boolean
38
     */
39
    private $forceAdvanced = false;
40
41
    /**
42
     * @var \Illuminate\Support\Collection
43
     */
44
    private $pendingComplexFilters;
45
46
    /**
47
     * @var \FFMpeg\Media\AbstractMediaType
48
     */
49
    private $media;
50
51
    public function __construct(FFMpeg $ffmpeg)
52
    {
53
        $this->ffmpeg                = $ffmpeg;
54
        $this->pendingComplexFilters = new Collection;
55
    }
56
57
    /**
58
     * Returns a fresh instance of itself with only the underlying FFMpeg instance.
59
     */
60
    public function fresh(): self
61
    {
62
        return new static($this->ffmpeg);
63
    }
64
65
    public function get(): AbstractMediaType
66
    {
67
        return $this->media;
68
    }
69
70
    private function isAdvancedMedia(): bool
71
    {
72
        return $this->get() instanceof AdvancedMedia;
73
    }
74
75
    public function isFrame(): bool
76
    {
77
        return $this->get() instanceof Frame;
78
    }
79
80
    public function isConcat(): bool
81
    {
82
        return $this->get() instanceof Concat;
83
    }
84
85
    public function getMediaCollection(): MediaCollection
86
    {
87
        return $this->mediaCollection;
88
    }
89
90
    /**
91
     * Opens the MediaCollection if it's not been instanciated yet.
92
     */
93
    public function open(MediaCollection $mediaCollection): self
94
    {
95
        if ($this->media) {
96
            return $this;
97
        }
98
99
        $this->mediaCollection = $mediaCollection;
100
101
        $localPaths = $mediaCollection->getLocalPaths();
102
103
        if (count($localPaths) === 1 && !$this->forceAdvanced) {
104
            $this->media = $this->ffmpeg->open(Arr::first($localPaths));
105
        } else {
106
            $this->media = $this->ffmpeg->openAdvanced($localPaths);
107
        }
108
109
        return $this;
110
    }
111
112
    public function frame(TimeCode $timecode)
113
    {
114
        $this->media = $this->media->frame($timecode);
0 ignored issues
show
Bug introduced by
The method frame() does not exist on FFMpeg\Media\AbstractMediaType. It seems like you code against a sub-type of FFMpeg\Media\AbstractMediaType such as FFMpeg\Media\Video. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

114
        /** @scrutinizer ignore-call */ 
115
        $this->media = $this->media->frame($timecode);
Loading history...
115
116
        return $this;
117
    }
118
119
    public function concatWithoutTranscoding()
120
    {
121
        $localPaths = $this->mediaCollection->getLocalPaths();
122
123
        $this->media = $this->ffmpeg->open(Arr::first($localPaths))
124
            ->concat($localPaths);
125
126
        return $this;
127
    }
128
129
    /**
130
     * Force 'openAdvanced' when opening the MediaCollection
131
     */
132
    public function openAdvanced(MediaCollection $mediaCollection): self
133
    {
134
        $this->forceAdvanced = true;
135
136
        return $this->open($mediaCollection);
137
    }
138
139
    public function getStreams(): array
140
    {
141
        if (!$this->isAdvancedMedia()) {
142
            return iterator_to_array($this->media->getStreams());
0 ignored issues
show
Bug introduced by
The method getStreams() does not exist on FFMpeg\Media\AbstractMediaType. It seems like you code against a sub-type of FFMpeg\Media\AbstractMediaType such as FFMpeg\Media\AbstractStreamableMedia. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

142
            return iterator_to_array($this->media->/** @scrutinizer ignore-call */ getStreams());
Loading history...
143
        }
144
145
        return $this->mediaCollection->map(function (Media $media) {
0 ignored issues
show
Bug introduced by
The method map() does not exist on ProtoneMedia\LaravelFFMp...esystem\MediaCollection. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

145
        return $this->mediaCollection->/** @scrutinizer ignore-call */ map(function (Media $media) {
Loading history...
146
            return $this->fresh()->open(MediaCollection::make([$media]))->getStreams();
147
        })->collapse()->all();
148
    }
149
150
    public function getFilters(): array
151
    {
152
        return iterator_to_array($this->media->getFiltersCollection());
0 ignored issues
show
Bug introduced by
$this->media->getFiltersCollection() of type FFMpeg\Media\MediaTypeInterface is incompatible with the type Traversable expected by parameter $iterator of iterator_to_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

152
        return iterator_to_array(/** @scrutinizer ignore-type */ $this->media->getFiltersCollection());
Loading history...
153
    }
154
155
    //
156
157
    public function getDurationInSeconds(): int
158
    {
159
        return round($this->getDurationInMiliseconds() / 1000);
160
    }
161
162
    /**
163
     * Gets the duration of the media from the first stream or from the format.
164
     */
165
    public function getDurationInMiliseconds(): int
166
    {
167
        $stream = Arr::first($this->getStreams());
168
169
        if ($stream->has('duration')) {
170
            return $stream->get('duration') * 1000;
171
        }
172
173
        $format = $this->getFormat();
0 ignored issues
show
Bug introduced by
The method getFormat() does not exist on ProtoneMedia\LaravelFFMpeg\Drivers\PHPFFMpeg. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

173
        /** @scrutinizer ignore-call */ 
174
        $format = $this->getFormat();
Loading history...
174
175
        if ($format->has('duration')) {
0 ignored issues
show
Bug introduced by
The method has() does not exist on ProtoneMedia\LaravelFFMpeg\Drivers\PHPFFMpeg. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

175
        if ($format->/** @scrutinizer ignore-call */ has('duration')) {
Loading history...
176
            return $format->get('duration') * 1000;
0 ignored issues
show
Unused Code introduced by
The call to ProtoneMedia\LaravelFFMp...rivers\PHPFFMpeg::get() has too many arguments starting with 'duration'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
            return $format->/** @scrutinizer ignore-call */ get('duration') * 1000;

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
177
        }
178
    }
179
180
    /**
181
     * Gets the first audio streams of the media.
182
     */
183
    public function getAudioStream(): ?Stream
184
    {
185
        return Arr::first($this->getStreams(), function (Stream $stream) {
186
            return $stream->isAudio();
187
        });
188
    }
189
190
    /**
191
     * Gets the first video streams of the media.
192
     */
193
    public function getVideoStream(): ?Stream
194
    {
195
        return Arr::first($this->getStreams(), function (Stream $stream) {
196
            return $stream->isVideo();
197
        });
198
    }
199
200
    //
201
202
    /**
203
     * Helper method to provide multiple ways to add a filter to the underlying
204
     * media object.
205
     *
206
     * @return self
207
     */
208
    public function addFilter(): self
209
    {
210
        $arguments = func_get_args();
211
212
        // to support '[in]filter[out]' complex filters
213
        if ($this->isAdvancedMedia() && count($arguments) === 3) {
214
            $this->media->filters()->custom(...$arguments);
215
216
            return $this;
217
        }
218
219
        // use a callback to add a filter
220
        if ($arguments[0] instanceof Closure) {
221
            call_user_func_array($arguments[0], [$this->media->filters()]);
222
223
            return $this;
224
        }
225
226
        // use an object to add a filter
227
        if ($arguments[0] instanceof FilterInterface) {
228
            call_user_func_array([$this->media, 'addFilter'], $arguments);
229
230
            return $this;
231
        }
232
233
        // use a single array with parameters to define a filter
234
        if (is_array($arguments[0])) {
235
            $this->media->addFilter(new SimpleFilter($arguments[0]));
0 ignored issues
show
Bug introduced by
The method addFilter() does not exist on FFMpeg\Media\AbstractMediaType. It seems like you code against a sub-type of said class. However, the method does not exist in FFMpeg\Media\AbstractStreamableMedia. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

235
            $this->media->/** @scrutinizer ignore-call */ 
236
                          addFilter(new SimpleFilter($arguments[0]));
Loading history...
236
237
            return $this;
238
        }
239
240
        // use all function arguments as a filter
241
        $this->media->addFilter(new SimpleFilter($arguments));
242
243
        return $this;
244
    }
245
246
    /**
247
     * Maps the arguments into a 'LegacyFilterMapping' instance and
248
     * pushed it to the 'pendingComplexFilters' collection. These
249
     * filters will be applied later on by the MediaExporter.
250
     */
251
    public function addFilterAsComplexFilter($in, $out, ...$arguments): self
252
    {
253
        $this->pendingComplexFilters->push(new LegacyFilterMapping(
254
            $in,
255
            $out,
256
            ...$arguments
257
        ));
258
259
        return $this;
260
    }
261
262
    public function getPendingComplexFilters(): Collection
263
    {
264
        return $this->pendingComplexFilters;
265
    }
266
267
    /**
268
     * Returns the underlying media object itself.
269
     */
270
    public function __invoke(): AbstractMediaType
271
    {
272
        return $this->get();
273
    }
274
275
    /**
276
     * Forwards the call to the underling media object and returns the result
277
     * if it's something different than the media object itself.
278
     */
279
    public function __call($method, $arguments)
280
    {
281
        $result = $this->forwardCallTo($media = $this->get(), $method, $arguments);
282
283
        return ($result === $media) ? $this : $result;
284
    }
285
}
286