Passed
Pull Request — master (#273)
by Pascal
02:30
created

PHPFFMpeg::frame()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace ProtoneMedia\LaravelFFMpeg\Drivers;
4
5
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
6
use Exception;
7
use FFMpeg\Coordinate\TimeCode;
8
use FFMpeg\Driver\FFMpegDriver;
9
use FFMpeg\FFMpeg;
10
use FFMpeg\Media\AbstractMediaType;
11
use FFMpeg\Media\AdvancedMedia as BaseAdvancedMedia;
12
use FFMpeg\Media\Concat;
13
use FFMpeg\Media\Frame;
14
use FFMpeg\Media\Video;
15
use Illuminate\Support\Arr;
16
use Illuminate\Support\Collection;
17
use Illuminate\Support\Traits\ForwardsCalls;
18
use ProtoneMedia\LaravelFFMpeg\FFMpeg\AdvancedMedia;
19
use ProtoneMedia\LaravelFFMpeg\FFMpeg\AudioMedia;
20
use ProtoneMedia\LaravelFFMpeg\FFMpeg\FFProbe;
21
use ProtoneMedia\LaravelFFMpeg\FFMpeg\VideoMedia;
22
use ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection;
23
24
/**
25
 * @mixin \FFMpeg\Media\AbstractMediaType
26
 */
27
class PHPFFMpeg
28
{
29
    use ForwardsCalls;
30
    use InteractsWithFilters;
31
    use InteractsWithMediaStreams;
32
33
    /**
34
     * @var \FFMpeg\FFMpeg
35
     */
36
    private $ffmpeg;
37
38
    /**
39
     * @var \ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection
40
     */
41
    private $mediaCollection;
42
43
    /**
44
     * @var boolean
45
     */
46
    private $forceAdvanced = false;
47
48
    /**
49
     * @var \FFMpeg\Media\AbstractMediaType
50
     */
51
    private $media;
52
53
    /**
54
     * Callbacks that should be called just before the
55
     * underlying library hits the save method.
56
     *
57
     * @var array
58
     */
59
    private $beforeSavingCallbacks = [];
60
61
    public function __construct(FFMpeg $ffmpeg)
62
    {
63
        $this->ffmpeg                = $ffmpeg;
64
        $this->pendingComplexFilters = new Collection;
65
    }
66
67
    /**
68
     * Returns a fresh instance of itself with only the underlying FFMpeg instance.
69
     */
70
    public function fresh(): self
71
    {
72
        return new static($this->ffmpeg);
73
    }
74
75
    public function get(): AbstractMediaType
76
    {
77
        return $this->media;
78
    }
79
80
    private function isAdvancedMedia(): bool
0 ignored issues
show
Unused Code introduced by
The method isAdvancedMedia() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
81
    {
82
        return $this->get() instanceof BaseAdvancedMedia;
83
    }
84
85
    public function isFrame(): bool
86
    {
87
        return $this->get() instanceof Frame;
88
    }
89
90
    public function isConcat(): bool
91
    {
92
        return $this->get() instanceof Concat;
93
    }
94
95
    public function isVideo(): bool
96
    {
97
        return $this->get() instanceof Video;
98
    }
99
100
    public function getMediaCollection(): MediaCollection
101
    {
102
        return $this->mediaCollection;
103
    }
104
105
    /**
106
     * Opens the MediaCollection if it's not been instanciated yet.
107
     */
108
    public function open(MediaCollection $mediaCollection): self
109
    {
110
        if ($this->media) {
111
            return $this;
112
        }
113
114
        $this->mediaCollection = $mediaCollection;
115
116
        if ($mediaCollection->count() === 1 && !$this->forceAdvanced) {
117
            $media = Arr::first($mediaCollection->collection());
118
119
            $this->ffmpeg->setFFProbe(
120
                FFProbe::make($this->ffmpeg->getFFProbe())->setMedia($media)
121
            );
122
123
            $ffmpegMedia = $this->ffmpeg->open($media->getLocalPath());
124
125
            $this->media = $ffmpegMedia instanceof Video
126
                ? VideoMedia::make($ffmpegMedia)
127
                : AudioMedia::make($ffmpegMedia);
128
129
            if (method_exists($this->media, 'setHeaders')) {
130
                $this->media->setHeaders(Arr::first($mediaCollection->getHeaders()) ?: []);
131
            }
132
        } else {
133
            $ffmpegMedia = $this->ffmpeg->openAdvanced($mediaCollection->getLocalPaths());
134
135
            $this->media = AdvancedMedia::make($ffmpegMedia)
136
                ->setHeaders($mediaCollection->getHeaders());
137
        }
138
139
        return $this;
140
    }
141
142
    public function frame(TimeCode $timecode)
143
    {
144
        if (!$this->isVideo()) {
145
            throw new Exception('Opened media is not a video file.');
146
        }
147
148
        $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

148
        /** @scrutinizer ignore-call */ 
149
        $this->media = $this->media->frame($timecode);
Loading history...
149
150
        return $this;
151
    }
152
153
    public function concatWithoutTranscoding()
154
    {
155
        $localPaths = $this->mediaCollection->getLocalPaths();
156
157
        $this->media = $this->ffmpeg->open(Arr::first($localPaths))
158
            ->concat($localPaths);
159
160
        return $this;
161
    }
162
163
    /**
164
     * Force 'openAdvanced' when opening the MediaCollection
165
     */
166
    public function openAdvanced(MediaCollection $mediaCollection): self
167
    {
168
        $this->forceAdvanced = true;
169
170
        return $this->open($mediaCollection);
171
    }
172
173
    /**
174
     * Returns the FFMpegDriver of the underlying library.
175
     *
176
     * @return \FFMpeg\Driver\FFMpegDriver
177
     */
178
    private function getFFMpegDriver(): FFMpegDriver
179
    {
180
        return $this->get()->getFFMpegDriver();
181
    }
182
183
    /**
184
     * Add a Listener to the underlying library.
185
     *
186
     * @param \Alchemy\BinaryDriver\Listeners\ListenerInterface $listener
187
     * @return self
188
     */
189
    public function addListener(ListenerInterface $listener): self
190
    {
191
        $this->getFFMpegDriver()->listen($listener);
192
193
        return $this;
194
    }
195
196
    /**
197
     * Adds a callable to the callbacks array.
198
     *
199
     * @param callable $callback
200
     * @return self
201
     */
202
    public function beforeSaving(callable $callback): self
203
    {
204
        $this->beforeSavingCallbacks[] = $callback;
205
206
        return $this;
207
    }
208
209
    /**
210
     * Set the callbacks on the Media.
211
     *
212
     * @return self
213
     */
214
    public function applyBeforeSavingCallbacks(): self
215
    {
216
        $media = $this->get();
217
218
        if (method_exists($media, 'setBeforeSavingCallbacks')) {
219
            $media->setBeforeSavingCallbacks($this->beforeSavingCallbacks);
220
        }
221
222
        return $this;
223
    }
224
225
    /**
226
     * Add an event handler to the underlying library.
227
     *
228
     * @param string $event
229
     * @param callable $callback
230
     * @return self
231
     */
232
    public function onEvent(string $event, callable $callback): self
233
    {
234
        $this->getFFMpegDriver()->on($event, $callback);
235
236
        return $this;
237
    }
238
239
    /**
240
     * Returns the underlying media object itself.
241
     */
242
    public function __invoke(): AbstractMediaType
243
    {
244
        return $this->get();
245
    }
246
247
    /**
248
     * Forwards the call to the underling media object and returns the result
249
     * if it's something different than the media object itself.
250
     */
251
    public function __call($method, $arguments)
252
    {
253
        $result = $this->forwardCallTo($media = $this->get(), $method, $arguments);
254
255
        return ($result === $media) ? $this : $result;
256
    }
257
}
258