JFIF  x x C         C     "        } !1AQa "q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz        w !1AQ aq"2B #3Rbr{ gilour
<?php namespace ProtoneMedia\LaravelFFMpeg\Drivers; use Alchemy\BinaryDriver\Listeners\ListenerInterface; use Exception; use FFMpeg\Coordinate\TimeCode; use FFMpeg\Driver\FFMpegDriver; use FFMpeg\FFMpeg; use FFMpeg\Media\AbstractMediaType; use FFMpeg\Media\AdvancedMedia as BaseAdvancedMedia; use FFMpeg\Media\Audio; use FFMpeg\Media\Concat; use FFMpeg\Media\Frame; use FFMpeg\Media\Video; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Traits\ForwardsCalls; use ProtoneMedia\LaravelFFMpeg\FFMpeg\AdvancedMedia; use ProtoneMedia\LaravelFFMpeg\FFMpeg\AudioMedia; use ProtoneMedia\LaravelFFMpeg\FFMpeg\FFProbe; use ProtoneMedia\LaravelFFMpeg\FFMpeg\VideoMedia; use ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection; /** * @mixin \FFMpeg\Media\AbstractMediaType */ class PHPFFMpeg { use ForwardsCalls; use InteractsWithFilters; use InteractsWithMediaStreams; /** * @var \FFMpeg\FFMpeg */ private $ffmpeg; /** * @var \ProtoneMedia\LaravelFFMpeg\Filesystem\MediaCollection */ private $mediaCollection; /** * @var boolean */ private $forceAdvanced = false; /** * @var \FFMpeg\Media\AbstractMediaType */ private $media; /** * Callbacks that should be called just before the * underlying library hits the save method. * * @var array */ private $beforeSavingCallbacks = []; public function __construct(FFMpeg $ffmpeg) { $this->ffmpeg = $ffmpeg; $this->pendingComplexFilters = new Collection(); } /** * Returns a fresh instance of itself with only the underlying FFMpeg instance. */ public function fresh(): self { return new static($this->ffmpeg); } public function get(): AbstractMediaType { return $this->media; } private function isAdvancedMedia(): bool { return $this->get() instanceof BaseAdvancedMedia; } public function isFrame(): bool { return $this->get() instanceof Frame; } public function isConcat(): bool { return $this->get() instanceof Concat; } public function isVideo(): bool { return $this->get() instanceof Video; } public function getMediaCollection(): MediaCollection { return $this->mediaCollection; } /** * Opens the MediaCollection if it's not been instanciated yet. */ public function open(MediaCollection $mediaCollection): self { if ($this->media) { return $this; } $this->mediaCollection = $mediaCollection; if ($mediaCollection->count() === 1 && !$this->forceAdvanced) { $media = Arr::first($mediaCollection->collection()); $this->ffmpeg->setFFProbe( FFProbe::make($this->ffmpeg->getFFProbe())->setMedia($media) ); $ffmpegMedia = $this->ffmpeg->open($media->getLocalPath()); // this should be refactored to a factory... if ($ffmpegMedia instanceof Video) { $this->media = VideoMedia::make($ffmpegMedia); } elseif ($ffmpegMedia instanceof Audio) { $this->media = AudioMedia::make($ffmpegMedia); } else { $this->media = $ffmpegMedia; } if (method_exists($this->media, 'setHeaders')) { $this->media->setHeaders(Arr::first($mediaCollection->getHeaders()) ?: []); } } else { $ffmpegMedia = $this->ffmpeg->openAdvanced($mediaCollection->getLocalPaths()); $this->media = AdvancedMedia::make($ffmpegMedia) ->setHeaders($mediaCollection->getHeaders()); } return $this; } public function frame(TimeCode $timecode) { if (!$this->isVideo()) { throw new Exception('Opened media is not a video file.'); } $this->media = $this->media->frame($timecode); return $this; } public function concatWithoutTranscoding() { $localPaths = $this->mediaCollection->getLocalPaths(); $this->media = $this->ffmpeg->open(Arr::first($localPaths)) ->concat($localPaths); return $this; } /** * Force 'openAdvanced' when opening the MediaCollection */ public function openAdvanced(MediaCollection $mediaCollection): self { $this->forceAdvanced = true; return $this->open($mediaCollection); } /** * Returns the FFMpegDriver of the underlying library. * * @return \FFMpeg\Driver\FFMpegDriver */ private function getFFMpegDriver(): FFMpegDriver { return $this->get()->getFFMpegDriver(); } /** * Add a Listener to the underlying library. * * @param \Alchemy\BinaryDriver\Listeners\ListenerInterface $listener * @return self */ public function addListener(ListenerInterface $listener): self { $this->getFFMpegDriver()->listen($listener); return $this; } /** * Remove the Listener from the underlying library. * * @param \Alchemy\BinaryDriver\Listeners\ListenerInterface $listener * @return self */ public function removeListener(ListenerInterface $listener): self { $this->getFFMpegDriver()->unlisten($listener); return $this; } /** * Adds a callable to the callbacks array. * * @param callable $callback * @return self */ public function beforeSaving(callable $callback): self { $this->beforeSavingCallbacks[] = $callback; return $this; } /** * Set the callbacks on the Media. * * @return self */ public function applyBeforeSavingCallbacks(): self { $media = $this->get(); if (method_exists($media, 'setBeforeSavingCallbacks')) { $media->setBeforeSavingCallbacks($this->beforeSavingCallbacks); } return $this; } /** * Add an event handler to the underlying library. * * @param string $event * @param callable $callback * @return self */ public function onEvent(string $event, callable $callback): self { $this->getFFMpegDriver()->on($event, $callback); return $this; } /** * Returns the underlying media object itself. */ public function __invoke(): AbstractMediaType { return $this->get(); } /** * Forwards the call to the underling media object and returns the result * if it's something different than the media object itself. */ public function __call($method, $arguments) { $result = $this->forwardCallTo($media = $this->get(), $method, $arguments); return ($result === $media) ? $this : $result; } }