Passed
Push — master ( 4422d3...1f3291 )
by Pascal
19:42 queued 15:59
created

PHPFFMpeg::__construct()   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 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
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\MediaCollection;
20
21
class PHPFFMpeg
22
{
23
    use ForwardsCalls;
24
25
    /**
26
     * @var \FFMpeg\FFMpeg
27
     */
28
    private $ffmpeg;
29
30
    /**
31
     * @var \ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection
32
     */
33
    private $mediaCollection;
34
35
    /**
36
     * @var boolean
37
     */
38
    private $forceAdvanced = false;
39
40
    /**
41
     * @var \Illuminate\Support\Collection
42
     */
43
    private $pendingComplexFilters;
44
45
    /**
46
     * @var \FFMpeg\Media\AbstractMediaType
47
     */
48
    private $media;
49
50
    public function __construct(FFMpeg $ffmpeg)
51
    {
52
        $this->ffmpeg                = $ffmpeg;
53
        $this->pendingComplexFilters = new Collection;
54
    }
55
56
    /**
57
     * Returns a fresh instance of itself with only the underlying FFMpeg instance.
58
     */
59
    public function fresh(): self
60
    {
61
        return new static($this->ffmpeg);
62
    }
63
64
    public function get(): AbstractMediaType
65
    {
66
        return $this->media;
67
    }
68
69
    private function isAdvancedMedia(): bool
70
    {
71
        return $this->get() instanceof AdvancedMedia;
72
    }
73
74
    public function isFrame(): bool
75
    {
76
        return $this->get() instanceof Frame;
77
    }
78
79
    public function isConcat(): bool
80
    {
81
        return $this->get() instanceof Concat;
82
    }
83
84
    public function getMediaCollection(): MediaCollection
85
    {
86
        return $this->mediaCollection;
87
    }
88
89
    /**
90
     * Opens the MediaCollection if it's not been instanciated yet.
91
     */
92
    public function open(MediaCollection $mediaCollection): self
93
    {
94
        if ($this->media) {
95
            return $this;
96
        }
97
98
        $this->mediaCollection = $mediaCollection;
99
100
        $localPaths = $mediaCollection->getLocalPaths();
101
102
        if (count($localPaths) === 1 && !$this->forceAdvanced) {
103
            $this->media = $this->ffmpeg->open(Arr::first($localPaths));
104
        } else {
105
            $this->media = $this->ffmpeg->openAdvanced($localPaths);
106
        }
107
108
        return $this;
109
    }
110
111
    public function frame(TimeCode $timecode)
112
    {
113
        $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

113
        /** @scrutinizer ignore-call */ 
114
        $this->media = $this->media->frame($timecode);
Loading history...
114
115
        return $this;
116
    }
117
118
    public function concatWithoutTranscoding()
119
    {
120
        $localPaths = $this->mediaCollection->getLocalPaths();
121
122
        $this->media = $this->ffmpeg->open(Arr::first($localPaths))
123
            ->concat($localPaths);
124
125
        return $this;
126
    }
127
128
    /**
129
     * Force 'openAdvanced' when opening the MediaCollection
130
     */
131
    public function openAdvanced(MediaCollection $mediaCollection): self
132
    {
133
        $this->forceAdvanced = true;
134
135
        return $this->open($mediaCollection);
136
    }
137
138
    public function getStreams(): array
139
    {
140
        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

140
        return iterator_to_array($this->media->/** @scrutinizer ignore-call */ getStreams());
Loading history...
141
    }
142
143
    public function getFilters(): array
144
    {
145
        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

145
        return iterator_to_array(/** @scrutinizer ignore-type */ $this->media->getFiltersCollection());
Loading history...
146
    }
147
148
    //
149
150
    public function getDurationInSeconds(): int
151
    {
152
        return round($this->getDurationInMiliseconds() / 1000);
153
    }
154
155
    /**
156
     * Gets the duration of the media from the first stream or from the format.
157
     */
158
    public function getDurationInMiliseconds(): int
159
    {
160
        $stream = Arr::first($this->getStreams());
161
162
        if ($stream->has('duration')) {
163
            return $stream->get('duration') * 1000;
164
        }
165
166
        $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

166
        /** @scrutinizer ignore-call */ 
167
        $format = $this->getFormat();
Loading history...
167
168
        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

168
        if ($format->/** @scrutinizer ignore-call */ has('duration')) {
Loading history...
169
            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

169
            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...
170
        }
171
    }
172
173
    /**
174
     * Gets the first video streams of the media.
175
     */
176
    public function getVideoStream(): ?Stream
177
    {
178
        return Arr::first($this->getStreams(), function (Stream $stream) {
179
            return $stream->isVideo();
180
        });
181
    }
182
183
    //
184
185
    /**
186
     * Helper method to provide multiple ways to add a filter to the underlying
187
     * media object.
188
     *
189
     * @return self
190
     */
191
    public function addFilter(): self
192
    {
193
        $arguments = func_get_args();
194
195
        // to support '[in]filter[out]' complex filters
196
        if ($this->isAdvancedMedia() && count($arguments) === 3) {
197
            $this->media->filters()->custom(...$arguments);
198
199
            return $this;
200
        }
201
202
        // use a callback to add a filter
203
        if ($arguments[0] instanceof Closure) {
204
            call_user_func_array($arguments[0], [$this->media->filters()]);
205
206
            return $this;
207
        }
208
209
        // use an object to add a filter
210
        if ($arguments[0] instanceof FilterInterface) {
211
            call_user_func_array([$this->media, 'addFilter'], $arguments);
212
213
            return $this;
214
        }
215
216
        // use a single array with parameters to define a filter
217
        if (is_array($arguments[0])) {
218
            $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

218
            $this->media->/** @scrutinizer ignore-call */ 
219
                          addFilter(new SimpleFilter($arguments[0]));
Loading history...
219
220
            return $this;
221
        }
222
223
        // use all function arguments as a filter
224
        $this->media->addFilter(new SimpleFilter($arguments));
225
226
        return $this;
227
    }
228
229
    /**
230
     * Maps the arguments into a 'LegacyFilterMapping' instance and
231
     * pushed it to the 'pendingComplexFilters' collection. These
232
     * filters will be applied later on by the MediaExporter.
233
     */
234
    public function addFilterAsComplexFilter($in, $out, ...$arguments): self
235
    {
236
        $this->pendingComplexFilters->push(new LegacyFilterMapping(
237
            $in,
238
            $out,
239
            ...$arguments
240
        ));
241
242
        return $this;
243
    }
244
245
    public function getPendingComplexFilters(): Collection
246
    {
247
        return $this->pendingComplexFilters;
248
    }
249
250
    /**
251
     * Returns the underlying media object itself.
252
     */
253
    public function __invoke(): AbstractMediaType
254
    {
255
        return $this->get();
256
    }
257
258
    /**
259
     * Forwards the call to the underling media object and returns the result
260
     * if it's something different than the media object itself.
261
     */
262
    public function __call($method, $arguments)
263
    {
264
        $result = $this->forwardCallTo($media = $this->get(), $method, $arguments);
265
266
        return ($result === $media) ? $this : $result;
267
    }
268
}
269