Completed
Branch v7 (f01542)
by Pascal
08:46
created

PHPFFMpeg::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Pbmedia\LaravelFFMpeg\Drivers;
4
5
use Closure;
6
use FFMpeg\Coordinate\TimeCode;
7
use FFMpeg\FFMpeg;
8
use FFMpeg\Filters\Audio\SimpleFilter;
9
use FFMpeg\Filters\FilterInterface;
10
use FFMpeg\Media\AbstractMediaType;
11
use FFMpeg\Media\AdvancedMedia;
12
use FFMpeg\Media\Concat;
13
use FFMpeg\Media\Frame;
14
use Illuminate\Support\Arr;
15
use Illuminate\Support\Collection;
16
use Illuminate\Support\Traits\ForwardsCalls;
17
use Pbmedia\LaravelFFMpeg\FFMpeg\LegacyFilterMapping;
18
use Pbmedia\LaravelFFMpeg\Filesystem\MediaCollection;
19
20
class PHPFFMpeg
21
{
22
    use ForwardsCalls;
23
24
    private FFMpeg $ffmpeg;
25
    private bool $forceAdvanced = false;
26
    private MediaCollection $mediaCollection;
27
    private Collection $pendingComplexFilters;
28
    private ?AbstractMediaType $media = null;
29
30
    public function __construct(FFMpeg $ffmpeg)
31
    {
32
        $this->ffmpeg                = $ffmpeg;
33
        $this->pendingComplexFilters = new Collection;
34
    }
35
36
    /**
37
     * Returns a fresh instance of itself with only the underlying FFMpeg instance.
38
     */
39
    public function fresh(): self
40
    {
41
        return new static($this->ffmpeg);
42
    }
43
44
    public function get(): AbstractMediaType
45
    {
46
        return $this->media;
47
    }
48
49
    private function isAdvancedMedia(): bool
50
    {
51
        return $this->get() instanceof AdvancedMedia;
52
    }
53
54
    public function isFrame(): bool
55
    {
56
        return $this->get() instanceof Frame;
57
    }
58
59
    public function isConcat(): bool
60
    {
61
        return $this->get() instanceof Concat;
62
    }
63
64
    public function getMediaCollection(): MediaCollection
65
    {
66
        return $this->mediaCollection;
67
    }
68
69
    /**
70
     * Opens the MediaCollection if it's not been instanciated yet.
71
     */
72
    public function open(MediaCollection $mediaCollection): self
73
    {
74
        if ($this->media) {
75
            return $this;
76
        }
77
78
        $this->mediaCollection = $mediaCollection;
79
80
        $localPaths = $mediaCollection->getLocalPaths();
81
82
        if (count($localPaths) === 1 && !$this->forceAdvanced) {
83
            $this->media = $this->ffmpeg->open(Arr::first($localPaths));
84
        } else {
85
            $this->media = $this->ffmpeg->openAdvanced($localPaths);
86
        }
87
88
        return $this;
89
    }
90
91
    public function frame(TimeCode $timecode)
92
    {
93
        $this->media = $this->media->frame($timecode);
0 ignored issues
show
Bug introduced by
The method frame() does not exist on null. ( Ignorable by Annotation )

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

93
        /** @scrutinizer ignore-call */ 
94
        $this->media = $this->media->frame($timecode);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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

93
        /** @scrutinizer ignore-call */ 
94
        $this->media = $this->media->frame($timecode);
Loading history...
94
95
        return $this;
96
    }
97
98
    public function concatWithoutTranscoding()
99
    {
100
        $localPaths = $this->mediaCollection->getLocalPaths();
101
102
        $this->media = $this->ffmpeg->open(Arr::first($localPaths))
103
            ->concat($localPaths);
104
105
        return $this;
106
    }
107
108
    /**
109
     * Force 'openAdvanced' when opening the MediaCollection
110
     */
111
    public function openAdvanced(MediaCollection $mediaCollection): self
112
    {
113
        $this->forceAdvanced = true;
114
115
        return $this->open($mediaCollection);
116
    }
117
118
    public function getStreams(): array
119
    {
120
        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

120
        return iterator_to_array($this->media->/** @scrutinizer ignore-call */ getStreams());
Loading history...
121
    }
122
123
    public function getFilters(): array
124
    {
125
        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

125
        return iterator_to_array(/** @scrutinizer ignore-type */ $this->media->getFiltersCollection());
Loading history...
126
    }
127
128
    //
129
130
    public function getDurationInSeconds(): int
131
    {
132
        return round($this->getDurationInMiliseconds() / 1000);
133
    }
134
135
    /**
136
     * Gets the duration of the media from the first stream or from the format.
137
     */
138
    public function getDurationInMiliseconds(): int
139
    {
140
        $stream = Arr::first($this->getStreams());
141
142
        if ($stream->has('duration')) {
143
            return $stream->get('duration') * 1000;
144
        }
145
146
        $format = $this->getFormat();
0 ignored issues
show
Bug introduced by
The method getFormat() does not exist on Pbmedia\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

146
        /** @scrutinizer ignore-call */ 
147
        $format = $this->getFormat();
Loading history...
147
148
        if ($format->has('duration')) {
0 ignored issues
show
Bug introduced by
The method has() does not exist on Pbmedia\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

148
        if ($format->/** @scrutinizer ignore-call */ has('duration')) {
Loading history...
149
            return $format->get('duration') * 1000;
0 ignored issues
show
Unused Code introduced by
The call to Pbmedia\LaravelFFMpeg\Drivers\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

149
            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...
150
        }
151
    }
152
153
    //
154
155
    /**
156
     * Helper method to provide multiple ways to add a filter to the underlying
157
     * media object.
158
     *
159
     * @return self
160
     */
161
    public function addFilter(): self
162
    {
163
        $arguments = func_get_args();
164
165
        // to support '[in]filter[out]' complex filters
166
        if ($this->isAdvancedMedia() && count($arguments) === 3) {
167
            $this->media->filters()->custom(...$arguments);
168
169
            return $this;
170
        }
171
172
        // use a callback to add a filter
173
        if ($arguments[0] instanceof Closure) {
174
            call_user_func_array($arguments[0], [$this->media->filters()]);
175
176
            return $this;
177
        }
178
179
        // use an object to add a filter
180
        if ($arguments[0] instanceof FilterInterface) {
181
            call_user_func_array([$this->media, 'addFilter'], $arguments);
182
183
            return $this;
184
        }
185
186
        // use a single array with parameters to define a filter
187
        if (is_array($arguments[0])) {
188
            $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

188
            $this->media->/** @scrutinizer ignore-call */ 
189
                          addFilter(new SimpleFilter($arguments[0]));
Loading history...
189
190
            return $this;
191
        }
192
193
        // use all function arguments as a filter
194
        $this->media->addFilter(new SimpleFilter($arguments));
195
196
        return $this;
197
    }
198
199
    /**
200
     * Maps the arguments into a 'LegacyFilterMapping' instance and
201
     * pushed it to the 'pendingComplexFilters' collection. These
202
     * filters will be applied later on by the MediaExporter.
203
     */
204
    public function addFilterAsComplexFilter($in, $out, ...$arguments): self
205
    {
206
        $this->pendingComplexFilters->push(new LegacyFilterMapping(
207
            $in,
208
            $out,
209
            ...$arguments,
210
        ));
211
212
        return $this;
213
    }
214
215
    public function getPendingComplexFilters(): Collection
216
    {
217
        return $this->pendingComplexFilters;
218
    }
219
220
    /**
221
     * Returns the underlying media object itself.
222
     */
223
    public function __invoke(): AbstractMediaType
224
    {
225
        return $this->get();
226
    }
227
228
    /**
229
     * Forwards the call to the underling media object and returns the result
230
     * if it's something different than the media object itself.
231
     */
232
    public function __call($method, $arguments)
233
    {
234
        $result = $this->forwardCallTo($media = $this->get(), $method, $arguments);
235
236
        return ($result === $media) ? $this : $result;
237
    }
238
}
239