This package provides an integration with FFmpeg for Laravel. Laravel's Filesystem handles the storage of the files.

Overview

Laravel FFMpeg

Latest Version on Packagist Software License run-tests Quality Score Total Downloads

This package provides an integration with FFmpeg for Laravel 6.0 and higher. Laravel's Filesystem handles the storage of the files.

Features

  • Super easy wrapper around PHP-FFMpeg, including support for filters and other advanced features.
  • Integration with Laravel's Filesystem, configuration system and logging handling.
  • Compatible with Laravel 6.0 and higher, support for Package Discovery.
  • Built-in support for HLS.
  • Built-in support for encrypted HLS (AES-128) and rotating keys (optional).
  • Built-in support for concatenation, multiple inputs/outputs, image sequences (timelapse), complex filters (and mapping), frame/thumbnail exports.
  • Built-in support for watermarks (positioning and manipulation).
  • PHP 7.3 and higher.
  • Lots of integration tests, GitHub Actions with both Ubuntu and Windows.

Support

We proudly support the community by developing Laravel packages and giving them away for free. Keeping track of issues and pull requests takes time, but we're happy to help! If this package saves you time or if you're relying on it professionally, please consider supporting the maintenance and development.

Installation

You can install the package via composer:

composer require pbmedia/laravel-ffmpeg

Add the Service Provider and Facade to your app.php config file if you're not using Package Discovery.

// config/app.php

'providers' => [
    ...
    ProtoneMedia\LaravelFFMpeg\Support\ServiceProvider::class,
    ...
];

'aliases' => [
    ...
    'FFMpeg' => ProtoneMedia\LaravelFFMpeg\Support\FFMpeg::class
    ...
];

Publish the config file using the artisan CLI tool:

php artisan vendor:publish --provider="ProtoneMedia\LaravelFFMpeg\Support\ServiceProvider"

Upgrading to v7

  • The namespace has changed to ProtoneMedia\LaravelFFMpeg, the facade has been renamed to ProtoneMedia\LaravelFFMpeg\Support\FFMpeg, and the Service Provider has been renamed to ProtoneMedia\LaravelFFMpeg\Support\ServiceProvider.
  • Chaining exports are still supported, but you have to reapply filters for each export.
  • HLS playlists now include bitrate, framerate and resolution data. The segments also use a new naming pattern (read more). Please verify your exports still work in your player.
  • HLS export is now executed as one job instead of exporting each format/stream separately. This uses FFMpeg's map and filter_complex features. It might be sufficient to replace all calls to addFilter with addLegacyFilter, but some filters should be migrated manually. Please read the documentation on HLS to find out more about adding filters.

Usage

Convert an audio or video file:

FFMpeg::fromDisk('songs')
    ->open('yesterday.mp3')
    ->export()
    ->toDisk('converted_songs')
    ->inFormat(new \FFMpeg\Format\Audio\Aac)
    ->save('yesterday.aac');

Instead of the fromDisk() method you can also use the fromFilesystem() method, where $filesystem is an instance of Illuminate\Contracts\Filesystem\Filesystem.

$media = FFMpeg::fromFilesystem($filesystem)->open('yesterday.mp3');

Progress monitoring

You can monitor the transcoding progress. Use the onProgress method to provide a callback, which gives you the completed percentage. In previous versions of this package you had to pass the callback to the format object.

FFMpeg::open('steve_howe.mp4')
    ->export()
    ->onProgress(function ($percentage) {
        echo "{$percentage}% transcoded";
    });

As of version 7.0, the callback also exposes $remaining (in seconds) and $rate:

FFMpeg::open('steve_howe.mp4')
    ->export()
    ->onProgress(function ($percentage, $remaining, $rate) {
        echo "{$remaining} seconds left at rate: {$rate}";
    });

Open files from the web

You can open files from the web by using the openUrl method. You can specify custom HTTP headers with the optional second parameter:

FFMpeg::openUrl('https://videocoursebuilder.com/lesson-1.mp4');

FFMpeg::openUrl('https://videocoursebuilder.com/lesson-2.mp4', [
    'Authorization' => 'Basic YWRtaW46MTIzNA==',
]);

Handling exceptions

When the encoding fails, a ProtoneMedia\LaravelFFMpeg\Exporters\EncodingException shall be thrown, which extends the underlying FFMpeg\Exception\RuntimeException class. This class has two methods that can help you identify the problem. Using the getCommand method, you can get the executed command with all parameters. The getErrorOutput method gives you a full output log.

For legacy reasons, the message of the exception is always Encoding failed. You can replace this message with a more informative message by updating the set_command_and_error_output_on_exception configuration key to true.

try {
    FFMpeg::open('yesterday.mp3')
        ->export()
        ->inFormat(new Aac)
        ->save('yesterday.aac');
} catch (EncodingException $exception) {
    $command = $exception->getCommand();
    $errorLog = $exception->getErrorOutput();
}

Filters

You can add filters through a Closure or by using PHP-FFMpeg's Filter objects:

use FFMpeg\Filters\Video\VideoFilters;

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->addFilter(function (VideoFilters $filters) {
        $filters->resize(new \FFMpeg\Coordinate\Dimension(640, 480));
    })
    ->export()
    ->toDisk('converted_videos')
    ->inFormat(new \FFMpeg\Format\Video\X264)
    ->save('small_steve.mkv');

// or

$start = \FFMpeg\Coordinate\TimeCode::fromSeconds(5)
$clipFilter = new \FFMpeg\Filters\Video\ClipFilter($start);

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->addFilter($clipFilter)
    ->export()
    ->toDisk('converted_videos')
    ->inFormat(new \FFMpeg\Format\Video\X264)
    ->save('short_steve.mkv');

As of version 7.0, you can also call the addFilter method after the export method:

use FFMpeg\Filters\Video\VideoFilters;

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->export()
    ->toDisk('converted_videos')
    ->inFormat(new \FFMpeg\Format\Video\X264)
    ->addFilter(function (VideoFilters $filters) {
        $filters->resize(new \FFMpeg\Coordinate\Dimension(640, 480));
    })
    ->save('small_steve.mkv');

Resizing

Since resizing is a common operation, we've added a dedicated method for it:

FFMpeg::open('steve_howe.mp4')
    ->export()
    ->resize(640, 480);

The first argument is the width, and the second argument the height. The optional third argument is the mode. You can choose between fit (default), inset, width or height. The optional fourth argument is a boolean whether or not to force the use of standards ratios. You can find about these modes in the FFMpeg\Filters\Video\ResizeFilter class.

Custom filters

Sometimes you don't want to use the built-in filters. You can apply your own filter by providing a set of options. This can be an array or multiple strings as arguments:

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->addFilter(['-itsoffset', 1]);

// or

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->addFilter('-itsoffset', 1);

Watermark filter

As of version 7.3, you can easily add a watermark using the addWatermark method. With the WatermarkFactory, you can open your watermark file from a specific disk, just like opening an audio or video file. When you discard the fromDisk method, it uses the default disk specified in the filesystems.php configuration file.

After opening your watermark file, you can position it with the top, right, bottom, and left methods. The first parameter of these methods is the offset, which is optional and can be negative.

use ProtoneMedia\LaravelFFMpeg\Filters\WatermarkFactory;

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->addWatermark(function(WatermarkFactory $watermark) {
        $watermark->fromDisk('local')
            ->open('logo.png')
            ->right(25)
            ->bottom(25);
    });

Instead of using the position methods, you can also use the horizontalAlignment and verticalAlignment methods.

For horizontal alignment, you can use the WatermarkFactory::LEFT, WatermarkFactory::CENTER and WatermarkFactory::RIGHT constants. For vertical alignment, you can use the WatermarkFactory::TOP, WatermarkFactory::CENTER and WatermarkFactory::BOTTOM constants. Both methods take an optional second parameter, which is the offset.

FFMpeg::open('steve_howe.mp4')
    ->addWatermark(function(WatermarkFactory $watermark) {
        $watermark->open('logo.png')
            ->horizontalAlignment(WatermarkFactory::LEFT, 25)
            ->verticalAlignment(WatermarkFactory::TOP, 25);
    });

The WatermarkFactory also supports opening files from the web with the openUrl method. It supports custom HTTP headers as well.

FFMpeg::open('steve_howe.mp4')
    ->addWatermark(function(WatermarkFactory $watermark) {
        $watermark->openUrl('https://videocoursebuilder.com/logo.png');

        // or

        $watermark->openUrl('https://videocoursebuilder.com/logo.png', [
            'Authorization' => 'Basic YWRtaW46MTIzNA==',
        ]);
    });

If you want more control over the GET request, you can pass in an optional third parameter, which gives you the Curl resource.

$watermark->openUrl('https://videocoursebuilder.com/logo.png', [
    'Authorization' => 'Basic YWRtaW46MTIzNA==',
], function($curl) {
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
});

Watermark manipulation

This package can manipulate watermarks by using Spatie's Image package. To get started, install the package with Composer:

composer require spatie/image

Now you can chain one more manipulation methods on the WatermarkFactory instance:

FFMpeg::open('steve_howe.mp4')
    ->addWatermark(function(WatermarkFactory $watermark) {
        $watermark->open('logo.png')
            ->right(25)
            ->bottom(25)
            ->width(100)
            ->height(100)
            ->greyscale();
    });

Check out the documentation for all available methods.

Export without transcoding

This package comes with a ProtoneMedia\LaravelFFMpeg\FFMpeg\CopyFormat class that allows you to export a file without transcoding the streams. You might want to use this to use another container:

use ProtoneMedia\LaravelFFMpeg\FFMpeg\CopyFormat;

FFMpeg::open('video.mp4')
    ->export()
    ->inFormat(new CopyFormat)
    ->save('video.mkv');

Chain multiple convertions

// The 'fromDisk()' method is not required, the file will now
// be opened from the default 'disk', as specified in
// the config file.

FFMpeg::open('my_movie.mov')

    // export to FTP, converted in WMV
    ->export()
    ->toDisk('ftp')
    ->inFormat(new \FFMpeg\Format\Video\WMV)
    ->save('my_movie.wmv')

    // export to Amazon S3, converted in X264
    ->export()
    ->toDisk('s3')
    ->inFormat(new \FFMpeg\Format\Video\X264)
    ->save('my_movie.mkv');

    // you could even discard the 'toDisk()' method,
    // now the converted file will be saved to
    // the same disk as the source!
    ->export()
    ->inFormat(new FFMpeg\Format\Video\WebM)
    ->save('my_movie.webm')

    // optionally you could set the visibility
    // of the exported file
    ->export()
    ->inFormat(new FFMpeg\Format\Video\WebM)
    ->withVisibility('public')
    ->save('my_movie.webm')

Create a frame from a video

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->getFrameFromSeconds(10)
    ->export()
    ->toDisk('thumnails')
    ->save('FrameAt10sec.png');

// Instead of the 'getFrameFromSeconds()' method, you could
// also use the 'getFrameFromString()' or the
// 'getFrameFromTimecode()' methods:

$media = FFMpeg::open('steve_howe.mp4');
$frame = $media->getFrameFromString('00:00:13.37');

// or

$timecode = new FFMpeg\Coordinate\TimeCode(...);
$frame = $media->getFrameFromTimecode($timecode);

You can also get the raw contents of the frame instead of saving it to the filesystem:

$contents = FFMpeg::open('video.mp4')
    ->getFrameFromSeconds(2)
    ->export()
    ->getFrameContents();

Multiple exports using loops

Chaining multiple conversions works because the save method of the MediaExporter returns a fresh instance of the MediaOpener. You can use this to loop through items, for example, to exports multiple frames from one video:

$mediaOpener = FFMpeg::open('video.mp4');

foreach ([5, 15, 25] as $key => $seconds) {
    $mediaOpener = $mediaOpener->getFrameFromSeconds($seconds)
        ->export()
        ->save("thumb_{$key}.png");
}

The MediaOpener comes with an each method as well. The example above could be refactored like this:

FFMpeg::open('video.mp4')->each([5, 15, 25], function ($ffmpeg, $seconds, $key) {
    $ffmpeg->getFrameFromSeconds($seconds)->export()->save("thumb_{$key}.png");
});

Create a timelapse

You can create a timelapse from a sequence of images by using the asTimelapseWithFramerate method on the exporter

FFMpeg::open('feature_%04d.png')
    ->export()
    ->asTimelapseWithFramerate(1)
    ->inFormat(new X264)
    ->save('timelapse.mp4');

Multiple inputs

As of version 7.0 you can open multiple inputs, even from different disks. This uses FFMpeg's map and filter_complex features. You can open multiple files by chaining the open method of by using an array. You can mix inputs from different disks.

FFMpeg::open('video1.mp4')->open('video2.mp4');

FFMpeg::open(['video1.mp4', 'video2.mp4']);

FFMpeg::fromDisk('uploads')
    ->open('video1.mp4')
    ->fromDisk('archive')
    ->open('video2.mp4');

When you open multiple inputs, you have to add mappings so FFMpeg knows how to route them. This package provides a addFormatOutputMapping method, which takes three parameters: the format, the output, and the output labels of the -filter_complex part.

The output (2nd argument) should be an instanceof ProtoneMedia\LaravelFFMpeg\Filesystem\Media. You can instantiate with the make method, call it with the name of the disk and the path (see example).

Check out this example, which maps separate video and audio inputs into one output.

FFMpeg::fromDisk('local')
    ->open(['video.mp4', 'audio.m4a'])
    ->export()
    ->addFormatOutputMapping(new X264, Media::make('local', 'new_video.mp4'), ['0:v', '1:a'])
    ->save();

This is an example from the underlying library:

// This code takes 2 input videos, stacks they horizontally in 1 output video and
// adds to this new video the audio from the first video. (It is impossible
// with a simple filter graph that has only 1 input and only 1 output).

FFMpeg::fromDisk('local')
    ->open(['video.mp4', 'video2.mp4'])
    ->export()
    ->addFilter('[0:v][1:v]', 'hstack', '[v]')  // $in, $parameters, $out
    ->addFormatOutputMapping(new X264, Media::make('local', 'stacked_video.mp4'), ['0:a', '[v]'])
    ->save();

Just like single inputs, you can also pass a callback to the addFilter method. This will give you an instance of \FFMpeg\Filters\AdvancedMedia\ComplexFilters:

use FFMpeg\Filters\AdvancedMedia\ComplexFilters;

FFMpeg::open(['video.mp4', 'video2.mp4'])
    ->export()
    ->addFilter(function(ComplexFilters $filters) {
        // $filters->watermark(...);
    });

Opening files from the web works similarly. You can pass an array of URLs to the openUrl method, optionally with custom HTTP headers.

FFMpeg::openUrl([
    'https://videocoursebuilder.com/lesson-3.mp4',
    'https://videocoursebuilder.com/lesson-4.mp4',
]);

FFMpeg::openUrl([
    'https://videocoursebuilder.com/lesson-3.mp4',
    'https://videocoursebuilder.com/lesson-4.mp4',
], [
    'Authorization' => 'Basic YWRtaW46MTIzNA==',
]);

If you want to use another set of HTTP headers for each URL, you can chain the openUrl method:

FFMpeg::openUrl('https://videocoursebuilder.com/lesson-5.mp4', [
    'Authorization' => 'Basic YWRtaW46MTIzNA==',
])->openUrl('https://videocoursebuilder.com/lesson-6.mp4', [
    'Authorization' => 'Basic bmltZGE6NDMyMQ==',
]);

Concat files without transcoding

FFMpeg::fromDisk('local')
    ->open(['video.mp4', 'video2.mp4'])
    ->export()
    ->concatWithoutTranscoding()
    ->save('concat.mp4');

Concat files with transcoding

FFMpeg::fromDisk('local')
    ->open(['video.mp4', 'video2.mp4'])
    ->export()
    ->inFormat(new X264)
    ->concatWithTranscoding($hasVideo = true, $hasAudio = true)
    ->save('concat.mp4');

Determinate duration

With the Media class you can determinate the duration of a file:

$media = FFMpeg::open('wwdc_2006.mp4');

$durationInSeconds = $media->getDurationInSeconds(); // returns an int
$durationInMiliseconds = $media->getDurationInMiliseconds(); // returns a float

Handling remote disks

When opening or saving files from or to a remote disk, temporary files will be created on your server. After you're done exporting or processing these files, you could clean them up by calling the cleanupTemporaryFiles() method:

FFMpeg::cleanupTemporaryFiles();

By default, the root of the temporary directories is evaluated by PHP's sys_get_temp_dir() method, but you can modify it by setting the temporary_files_root configuration key to a custom path.

HLS

You can create a M3U8 playlist to do HLS.

$lowBitrate = (new X264)->setKiloBitrate(250);
$midBitrate = (new X264)->setKiloBitrate(500);
$highBitrate = (new X264)->setKiloBitrate(1000);

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->exportForHLS()
    ->setSegmentLength(10) // optional
    ->setKeyFrameInterval(48) // optional
    ->addFormat($lowBitrate)
    ->addFormat($midBitrate)
    ->addFormat($highBitrate)
    ->save('adaptive_steve.m3u8');

The addFormat method of the HLS exporter takes an optional second parameter which can be a callback method. This allows you to add different filters per format. First, check out the Multiple inputs section to understand how complex filters are handled.

You can use the addFilter method to add a complex filter (see $lowBitrate example). Since the scale filter is used a lot, there is a helper method (see $midBitrate example). You can also use a callable to get access to the ComplexFilters instance. The package provides the $in and $out arguments so you don't have to worry about it (see $highBitrate example).

As of version 7.0, HLS export is built using FFMpeg's map and filter_complex features. This is a breaking change from previous versions which performed a single export for each format. If you're upgrading, replace the addFilter calls with addLegacyFilter calls and verify the result (see $superBitrate example). Not all filters will work this way and some need to be upgraded manually.

$lowBitrate = (new X264)->setKiloBitrate(250);
$midBitrate = (new X264)->setKiloBitrate(500);
$highBitrate = (new X264)->setKiloBitrate(1000);
$superBitrate = (new X264)->setKiloBitrate(1500);

FFMpeg::open('steve_howe.mp4')
    ->exportForHLS()
    ->addFormat($lowBitrate, function($media) {
        $media->addFilter('scale=640:480');
    })
    ->addFormat($midBitrate, function($media) {
        $media->scale(960, 720);
    })
    ->addFormat($highBitrate, function ($media) {
        $media->addFilter(function ($filters, $in, $out) {
            $filters->custom($in, 'scale=1920:1200', $out); // $in, $parameters, $out
        });
    })
    ->addFormat($superBitrate, function($media) {
        $media->addLegacyFilter(function ($filters) {
            $filters->resize(new \FFMpeg\Coordinate\Dimension(2560, 1920));
        });
    })
    ->save('adaptive_steve.m3u8');

Using custom segment patterns

You can use a custom pattern to name the segments and playlists. The useSegmentFilenameGenerator gives you 5 arguments. The first, second and third argument provide information about the basename of the export, the format of the current iteration and the key of the current iteration. The fourth argument is a callback you should call with your segments pattern. The fifth argument is a callback you should call with your playlist pattern. Note that this is not the name of the primary playlist, but the name of the playlist of each format.

FFMpeg::fromDisk('videos')
    ->open('steve_howe.mp4')
    ->exportForHLS()
    ->useSegmentFilenameGenerator(function ($name, $format, $key, callable $segments, callable $playlist) {
        $segments("{$name}-{$format}-{$key}-%03d.ts");
        $playlist("{$name}-{$format}-{$key}.m3u8");
    });

Encrypted HLS

As of version 7.5, you can encrypt each HLS segment using AES-128 encryption. To do this, call the withEncryptionKey method on the HLS exporter with a key. We provide a generateEncryptionKey helper method on the HLSExporter class to generate a key. Make sure you store the key well, as the exported result is worthless without the key. By default, the filename of the key is secret.key, but you can change that with the optional second parameter of the withEncryptionKey method.

use ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter;

$encryptionKey = HLSExporter::generateEncryptionKey();

FFMpeg::open('steve_howe.mp4')
    ->exportForHLS()
    ->withEncryptionKey($encryptionKey)
    ->addFormat($lowBitrate)
    ->addFormat($midBitrate)
    ->addFormat($highBitrate)
    ->save('adaptive_steve.m3u8');

To secure your HLS export even further, you can rotate the key on each exported segment. By doing so, it will generate multiple keys that you'll need to store. Use the withRotatingEncryptionKey method to enable this feature and provide a callback that implements the storage of the keys.

FFMpeg::open('steve_howe.mp4')
    ->exportForHLS()
    ->withRotatingEncryptionKey(function ($filename, $contents) {
        $videoId = 1;

        // use this callback to store the encryption keys

        Storage::disk('secrets')->put($videoId . '/' . $filename, $contents);

        // or...

        DB::table('hls_secrets')->insert([
            'video_id' => $videoId,
            'filename' => $filename,
            'contents' => $contents,
        ]);
    })
    ->addFormat($lowBitrate)
    ->addFormat($midBitrate)
    ->addFormat($highBitrate)
    ->save('adaptive_steve.m3u8');

The withRotatingEncryptionKey method has an optional second argument to set the number of segments that use the same key. This defaults to 1.

FFMpeg::open('steve_howe.mp4')
    ->exportForHLS()
    ->withRotatingEncryptionKey($callable, 10);

Protecting your HLS encryption keys

To make working with encrypted HLS even better, we've added a DynamicHLSPlaylist class that modifies playlists on-the-fly and specifically for your application. This way, you can add your authentication and authorization logic. As we're using a plain Laravel controller, you can use features like Gates and Middleware.

In this example, we've saved the HLS export to the public disk, and we've stored the encryption keys to the secrets disk, which isn't publicly available. As the browser can't access the encryption keys, it won't play the video. Each playlist has paths to the encryption keys, and we need to modify those paths to point to an accessible endpoint.

This implementation consists of two routes. One that responses with an encryption key and one that responses with a modified playlist. The first route (video.key) is relatively simple, and this is where you should add your additional logic.

The second route (video.playlist) uses the DynamicHLSPlaylist class. Call the dynamicHLSPlaylist method on the FFMpeg facade, and similar to opening media files, you can open a playlist utilizing the fromDisk and open methods. Then you must provide three callbacks. Each of them gives you a relative path and expects a full path in return. As the DynamicHLSPlaylist class implements the Illuminate\Contracts\Support\Responsable interface, you can return the instance.

The first callback (KeyUrlResolver) gives you the relative path to an encryption key. The second callback (MediaUrlResolver) gives you the relative path to a media segment (.ts files). The third callback (PlaylistUrlResolver) gives you the relative path to a playlist.

Now instead of using Storage::disk('public')->url('adaptive_steve.m3u8') to get the full url to your primary playlist, you can use route('video.playlist', ['playlist' => 'adaptive_steve.m3u8']). The DynamicHLSPlaylist class takes care of all the paths and urls.

Route::get('/video/secret/{key}', function ($key) {
    return Storage::disk('secrets')->download($key);
})->name('video.key');

Route::get('/video/{playlist}', function ($playlist) {
    return FFMpeg::dynamicHLSPlaylist()
        ->fromDisk('public')
        ->open($playlist)
        ->setKeyUrlResolver(function ($key) {
            return route('video.key', ['key' => $key]);
        })
        ->setMediaUrlResolver(function ($mediaFilename) {
            return Storage::disk('public')->url($mediaFilename);
        })
        ->setPlaylistUrlResolver(function ($playlistFilename) {
            return route('video.playlist', ['playlist' => $playlistFilename]);
        });
})->name('video.playlist');

Live Coding Session

Here you can find a Live Coding Session about HLS encryption:

https://www.youtube.com/watch?v=WlbzWoAcez4

Process Output

You can get the raw process output by calling the getProcessOutput method. Though the use-case is limited, you can use it to analyze a file (for example, with the volumedetect filter). It returns a ProtoneMedia\LaravelFFMpeg\Support\ProcessOutput class that has three methods: all, errors and output. Each method returns an array with the corresponding lines.

$processOutput = FFMpeg::open('video.mp4')
    ->export()
    ->addFilter(['-filter:a', 'volumedetect', '-f', 'null'])
    ->getProcessOutput();

$processOutput->all();
$processOutput->errors();
$processOutput->out();

Advanced

The Media object you get when you 'open' a file, actually holds the Media object that belongs to the underlying driver. It handles dynamic method calls as you can see here. This way all methods of the underlying driver are still available to you.

// This gives you an instance of ProtoneMedia\LaravelFFMpeg\MediaOpener
$media = FFMpeg::fromDisk('videos')->open('video.mp4');

// The 'getStreams' method will be called on the underlying Media object since
// it doesn't exists on this object.
$codec = $media->getStreams()->first()->get('codec_name');

If you want direct access to the underlying object, call the object as a function (invoke):

// This gives you an instance of ProtoneMedia\LaravelFFMpeg\MediaOpener
$media = FFMpeg::fromDisk('videos')->open('video.mp4');

// This gives you an instance of FFMpeg\Media\MediaTypeInterface
$baseMedia = $media();

Experimental

The progress listener exposes the transcoded percentage, but the underlying package also has an internal AbstractProgressListener that exposes the current pass and the current time. Though the use-case is limited, you might want to get access to this listener instance. You can do this by decorating the format with the ProgressListenerDecorator. This feature is highly experimental, so be sure the test this thoroughly before using it in production.

use FFMpeg\Format\ProgressListener\AbstractProgressListener;
use ProtoneMedia\LaravelFFMpeg\FFMpeg\ProgressListenerDecorator;

$format = new \FFMpeg\Format\Video\X264;
$decoratedFormat = ProgressListenerDecorator::decorate($format);

FFMpeg::open('video.mp4')
    ->export()
    ->inFormat($decoratedFormat)
    ->onProgress(function () use ($decoratedFormat) {
        $listeners = $decoratedFormat->getListeners();  // array of listeners

        $listener = $listeners[0];  // instance of AbstractProgressListener

        $listener->getCurrentPass();
        $listener->getTotalPass();
        $listener->getCurrentTime();
    })
    ->save('new_video.mp4');

Since we can't get rid of some of the underlying options, you can interact with the final FFmpeg command by adding a callback to the exporter. You can add one or more callbacks by using the beforeSaving method:

FFMpeg::open('video.mp4')
    ->export()
    ->inFormat(new X264)
    ->beforeSaving(function ($commands) {
        $commands[] = '-hello';

        return $commands;
    })
    ->save('concat.mp4');

Note: this does not work with concatenation and frame exports

Example app

Here's a blog post that will help you get started with this package:

https://protone.media/en/blog/how-to-use-ffmpeg-in-your-laravel-projects

Using Video.js to play HLS in any browser

Here's a 20-minute overview how to get started with Video.js. It covers including Video.js from a CDN, importing it as an ES6 module with Laravel Mix (Webpack) and building a reusable Vue.js component.

https://www.youtube.com/watch?v=nA1Jy8BPjys

Wiki

Changelog

Please see CHANGELOG for more information about what has changed recently.

Testing

$ composer test

Contributing

Please see CONTRIBUTING for details.

Other Laravel packages

  • Laravel Analytics Event Tracking: Laravel package to easily send events to Google Analytics.
  • Laravel Blade On Demand: Laravel package to compile Blade templates in memory.
  • Laravel Cross Eloquent Search: Laravel package to search through multiple Eloquent models.
  • Laravel Eloquent Scope as Select: Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.
  • Laravel Eloquent Where Not: This Laravel package allows you to flip/invert an Eloquent scope, or really any query constraint.
  • Laravel Form Components: Blade components to rapidly build forms with Tailwind CSS Custom Forms and Bootstrap 4. Supports validation, model binding, default values, translations, includes default vendor styling and fully customizable!
  • Laravel Mixins: A collection of Laravel goodies.
  • Laravel Paddle: Paddle.com API integration for Laravel with support for webhooks/events.
  • Laravel Verify New Email: This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
  • Laravel WebDAV: WebDAV driver for Laravel's Filesystem.

Security

If you discover any security-related issues, please email [email protected] instead of using the issue tracker. Please do not email any questions, open an issue if you have a question.

Credits

License

The MIT License (MIT). Please see License File for more information.

Comments
  • Trying to add loop on GIF watermark gives error

    Trying to add loop on GIF watermark gives error

    I am adding gif watermark on a the video and it is giving me error No such filter: '-ignore_loop 0' when I am trying to add the loop in that GIF.

    Here is the code I am using

    $customFilter = (' -ignore_loop 0 ');
    addFilter(function ($filters) use ($watermarkPath) {
                            $filters->watermark($watermarkPath, [
                                'position' => 'absolute',
                                'bottom' => 300,
                                'right' => 100,
                                'scale=w' => 300,
                                'scale=h' => 200,
                            ]
                        );
                        })
    ->addFilter(function ($filters) use ($customFilter) {
                            $filters->custom( $customFilter );
                            
                     })
    

    This is the output command

    '/usr/local/bin/ffmpeg' '-y' '-i' '/Users/xfd/Documents/Projects/Test/storage/app/public/uploaded/AboruApZgJgf7faZ9mTAPBu5a2qn2w4ldWbosAdb.mp4' '-preset' 'ultrafast' '-threads' '12' '-vcodec' 'libx264' '-acodec' 'libmp3lame' '-b:v' '2500k' '-refs' '6' '-coder' '1' '-sc_threshold' '40' '-flags' '+loop' '-me_range' '16' '-subq' '7' '-i_qfactor' '0.71' '-qcomp' '0.6' '-qdiff' '4' '-trellis' '1' '-b:a' '128k' '-vf' 'movie=/Users/xfd/Documents/Projects/Test/public/storage/img/vfilter/aruba_frame.gif [watermark];[in]-ignore_loop 0[p0];[p0][watermark] overlay=0:0 [out]' '-pass' '1' '-passlogfile' '/var/folders/pg/7b4c598j08nd4lcd6c2h4_zr0000gn/T/ffmpeg-passes5ec2fe304004fhdai8/pass-5ec2fe304026f' '/Users/xfd/Documents/Projects/Test/storage/app/public/7.mp4'

    Please let me know what I am doing wrong here. Thank you

    edit: I did try the method you suggested in docs, that also didn't work (doesn't loop the gif).

    ->addFilter('-ignore_loop', 0);

    opened by swankey 13
  •  Error while opening encoder for output stream #0:1 - maybe incorrect parameters such as bit_rate, rate, width or height Conversion failed!

    Error while opening encoder for output stream #0:1 - maybe incorrect parameters such as bit_rate, rate, width or height Conversion failed!

    Thank you for the great package!

    I'm getting error when I transcode videos to HLS. This issue comes only for some of the videos.

    video info Dimensions: 1920 × 1080 Codecs: H.264

    $lowBitrate  = (new X264 ('aac'))->setKiloBitrate(500)->setVideoCodec('libx264');
    $midBitrate  = (new X264 ('aac'))->setKiloBitrate(1000)->setVideoCodec('libx264');
    $highBitrate = (new X264 ('aac'))->setKiloBitrate(3000)->setVideoCodec('libx264');
                   
    FFMpeg::fromDisk('videos_disk')
    ->open($this->video->original_file_path)
    ->addFilter(function ($filters) {
        $filters->watermark(storage_path('app/public') . '/watermark/logo.png', [
             'position' => 'relative',
               top' => 50,
               'right' => 50,
                ]);
          })
        ->exportForHLS()
       ->setSegmentLength(10) // optional
        ->toDisk('streamable_videos')
    
        ->addFormat($lowBitrate, function($media) {
             $media->addFilter(function ($filters) {
                    $filters->resize(new Dimension(640, 480));
             });
       })
         ->addFormat($midBitrate, function($media) {
               $media->addFilter(function ($filters) {
                      $filters->resize(new Dimension(1280, 960));
                 });
         })
         ->addFormat($highBitrate, function($media) {
                 $media->addFilter(function ($filters) {
                       $filters->resize(new Dimension(1280, 960));
                  });
          })
          ->save($this->video->name . '/' . $this->video->video_id . '/' . $this->video->video_id . '.m3u8');
    
    

    Error

    Metadata:
        major_brand     : isom
        minor_version   : 512
        compatible_brands: isomiso2avc1mp41
        track           : 0
        artist          : 
        album           : 
        date            : 0
        genre           : 
        lyrics          : 
        title           : 
        encoder         : Lavf56.36.100
      Duration: 00:00:08.00, start: 0.000000, bitrate: 2030 kb/s
        Stream #0:0(eng): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 2021 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
        Metadata:
          handler_name    : VideoHandler
        Stream #0:1: Video: png, rgba(pc), 58x38 [SAR 304:261 DAR 16:9], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Stream mapping:
      Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
      Stream #0:1 -> #0:1 (png (native) -> h264 (libx264))
    Press [q] to stop, [?] for help
    [segment @ 0x7fe6df00b200] Frame rate very high for a muxer not efficiently supporting it.
    Please consider specifying a lower framerate, a different muxer or -vsync 2
    [libx264 @ 0x7fe6df00e200] using SAR=4/3
    [libx264 @ 0x7fe6df00e200] MB rate (108000000) > level limit (16711680)
    [libx264 @ 0x7fe6df00e200] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
    [libx264 @ 0x7fe6df00e200] statistics are damaged at line 1, parser out=-1
    Error initializing output stream 0:1 -- Error while opening encoder for output stream #0:1 - maybe incorrect parameters such as bit_rate, rate, width or height
    Conversion failed!
      
    [2020-05-18 08:25:15] local.ERROR: Encoding failed {"exception":"[object] (FFMpeg\\Exception\\RuntimeException(code: 0): Encoding failed at /Library/WebServer/Documents/testproject/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/AbstractVideo.php:106)
    [stacktrace]
    
    opened by shanka12 13
  •  HLS encryption with key not working

    HLS encryption with key not working

    Package works fine with my disks when I'm using that without encryption key but when I added encryption key; I faced with this error image

    PHP version: 7.4.12 Laravel version: 8.74.0 laravel-ffmpeg: 7.5.12 FFmpeg: 4.2.3

    And this is my code

    image image

    I checked the exact file path in error and file exists in path.

    What is the problem?

    Thanks in advance!!

    opened by seyyedam7 12
  • v7, HLS not work on player

    v7, HLS not work on player

    I'm using v7.

    $lowBitrate  = (new X264 ('aac'))->setKiloBitrate(250)->setVideoCodec('libx264');
    $midBitrate  = (new X264 ('aac'))->setKiloBitrate(500)->setVideoCodec('libx264');
    $highBitrate = (new X264 ('aac'))->setKiloBitrate(1000)->setVideoCodec('libx264');
    
    FFMpeg::fromDisk('videos_disk')
         ->open($this->video->original_file_path)
          ->exportForHLS()
          ->addFormat($lowBitrate, function($media) {
              $media->scale(480, 360);
           })
           ->addFormat($midBitrate, function($media) {
                   $media->scale(640, 480);
           })
           ->addFormat($highBitrate, function($media) {
                    $media->scale(1280, 720);
            })
           ->toDisk('streamable_videos')
                    ->save($this->video->name . '/' . $this->video->video_id . '/' . $this->video->video_id . '.m3u8');
    
    

    location of streamable_videos /storage/app/public/project1/stream

    problem is in v7, each format ts file, m3u8 file storing into stream folder. final output file storing into the video id folder like below path

    ->save($this->video->name . '/' . $this->video->video_id . '/' . $this->video->video_id . '.m3u8');
    

    I'm using this final output m3u8 file on player. but video not playing. when I move all the ts files into this folder, it is working. I did not face this issue on previous version.

    opened by shanka12 12
  • Unable to load FFProbe (Linux Ubuntu)

    Unable to load FFProbe (Linux Ubuntu)

    Before i say anything, i should mention how sorry i am for reopening this Issue And thank you for your exceptional Package.

    Now, i have an issue when using FFMPEG in my laravel 5.7 app.

    FFMpeg \ Exception \ ExecutableNotFoundException Unable to load FFProbe

    Here is the path to my FFMPEG and FFProbe

    root@CY-TOOL:~# which ffmpeg
      /usr/bin/ffmpeg
    root@CY-TOOL:~# which ffprobe 
     /usr/bin/ffprobe
    root@CY-TOOL:~# php -v
    PHP 7.2.15-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Feb  8 2019 15:37:29) ( NTS )
    Copyright (c) 1997-2018 The PHP Group
    Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
        with Zend OPcache v7.2.15-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
    

    App\Config\laravel-ffmpeg.php

    just like it is said in laravel-ffmpeg/config/laravel-ffmpeg-ubuntu.php

     <?php
    
    return [
        'default_disk' => 'local',
    
        'ffmpeg' => [
            'binaries' => env('FFMPEG_BINARIES', '/usr/bin/ffmpeg'),
            'threads' => 12,
        ],
    
        'ffprobe' => [
            'binaries' => env('FFPROBE_BINARIES', '/usr/bin/ffprobe'),
        ],
    
        'timeout' => 3600,
    ];
    
    ?>
    

    composer.json

    
        "require": {
            "php": "^7.1.3",
            "fideloper/proxy": "^4.0",
            "laravel/framework": "5.7.*",
            "laravel/tinker": "^1.0",
            "pbmedia/laravel-ffmpeg": "^3.0",
            "php-ffmpeg/extras": "^0.3.1"
        }
    
    

    Please try to answer in such away that we wont have to ask you any another question anymore(concerning this), because i know it might annoy too when we keep on repeating the same questions again and again

    I am not that advance in laravel so don't use deep terms.

    Thanks in advance...

    opened by rabichawila 12
  • [Windows] file_put_contents failed: folder does not exist inside a queue

    [Windows] file_put_contents failed: folder does not exist inside a queue

    When the code is running using HLS rotating encryption on windows inside of a daemon queue, the first conversion works fine, but subsequent conversions all fail with the error message ErrorException: file_put_contents(C:\Users\Mordo\AppData\Local\Temp/fcadda63f51de69f/3f3e68a945107db2.key): failed to open stream: No such file or directory in E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\EncryptsHLSSegments.php:160.

    I noticed that the same directory is used for all conversions (C:\Users\Mordo\AppData\Local\Temp/fcadda63f51de69f), but this one is deleted after the first conversion is done.

    Operating system is Windows x64.

    My job's handler function:

    public function handle()
        {
            $lowBitrate = (new X264('aac'))->setKiloBitrate(250);
            $midBitrate = (new X264('aac'))->setKiloBitrate(500);
            $highBitrate = (new X264('aac'))->setKiloBitrate(1000);
    
            FFMpeg::fromDisk($this->disk)
                ->open($this->file)->exportForHLS()
                ->withRotatingEncryptionKey(function ($filename, $contents) {
                    VideoKey::create([
                        'video_id' => $this->video->id,
                        'filename' => $filename,
                        'contents' => $contents
                    ]);
                })
                ->addFormat($lowBitrate)
                ->addFormat($midBitrate)
                ->addFormat($highBitrate)
                ->toDisk('videos')
                ->save($this->video->id . '/index.m3u8');
    
            $media = FFMpeg::fromDisk($this->disk)
                ->open($this->file);
    
            FFMpeg::fromDisk($this->disk)
                ->open($this->file)
                ->getFrameFromSeconds($media->getDurationInSeconds() / 2)
                ->export()
                ->toDisk('videos')
                ->save($this->video->id . '/thumb.jpg');
    
            $this->video->status = "done";
            $this->video->save();
        }
    

    full stack trace:

    ErrorException: file_put_contents(C:\Users\Mordo\AppData\Local\Temp/fcadda63f51de69f/3f3e68a945107db2.key): failed to open stream: No such file or directory in E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\EncryptsHLSSegments.php:160
    Stack trace:
    #0 [internal function]: Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'file_put_conten...', 'E:\\xampp\\htdocs...', 160, Array)
    #1 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\EncryptsHLSSegments.php(160): file_put_contents('C:\\Users\\Mordo\\...', '\f\x86<\x90l\xC4\xE7\x17\x87\xE2B9\x18\x01\f...')
    #2 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\EncryptsHLSSegments.php(225): ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter->rotateEncryptionKey()
    #3 E:\xampp\htdocs\mywanderlust\vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter->ProtoneMedia\LaravelFFMpeg\Exporters\{closure}('[hls @ 000001d7...', 'err')
    #4 E:\xampp\htdocs\mywanderlust\vendor\alchemy\binary-driver\src\Alchemy\BinaryDriver\Listeners\Listeners.php(85): Evenement\EventEmitter->emit('listen', Array)
    #5 E:\xampp\htdocs\mywanderlust\vendor\evenement\evenement\src\Evenement\EventEmitterTrait.php(123): Alchemy\BinaryDriver\Listeners\Listeners->Alchemy\BinaryDriver\Listeners\{closure}('[hls @ 000001d7...', 'err')
    #6 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\FFMpeg\StdListener.php(49): Evenement\EventEmitter->emit('listen', Array)
    #7 E:\xampp\htdocs\mywanderlust\vendor\alchemy\binary-driver\src\Alchemy\BinaryDriver\ProcessRunner.php(87): ProtoneMedia\LaravelFFMpeg\FFMpeg\StdListener->handle('err', 'ffmpeg version ...')
    #8 E:\xampp\htdocs\mywanderlust\vendor\symfony\process\Process.php(1329): Alchemy\BinaryDriver\ProcessRunner->Alchemy\BinaryDriver\{closure}('err', 'ffmpeg version ...')
    #9 E:\xampp\htdocs\mywanderlust\vendor\symfony\process\Process.php(1429): Symfony\Component\Process\Process->Symfony\Component\Process\{closure}('err', 'ffmpeg version ...')
    #10 E:\xampp\htdocs\mywanderlust\vendor\symfony\process\Process.php(417): Symfony\Component\Process\Process->readPipes(true, false)
    #11 E:\xampp\htdocs\mywanderlust\vendor\symfony\process\Process.php(239): Symfony\Component\Process\Process->wait()
    #12 E:\xampp\htdocs\mywanderlust\vendor\alchemy\binary-driver\src\Alchemy\BinaryDriver\ProcessRunner.php(64): Symfony\Component\Process\Process->run(Object(Closure))
    #13 E:\xampp\htdocs\mywanderlust\vendor\alchemy\binary-driver\src\Alchemy\BinaryDriver\AbstractBinary.php(207): Alchemy\BinaryDriver\ProcessRunner->run(Object(Symfony\Component\Process\Process), Object(SplObjectStorage), false)
    #14 E:\xampp\htdocs\mywanderlust\vendor\alchemy\binary-driver\src\Alchemy\BinaryDriver\AbstractBinary.php(136): Alchemy\BinaryDriver\AbstractBinary->run(Object(Symfony\Component\Process\Process), false, Array)
    #15 E:\xampp\htdocs\mywanderlust\vendor\php-ffmpeg\php-ffmpeg\src\FFMpeg\Media\AdvancedMedia.php(237): Alchemy\BinaryDriver\AbstractBinary->command(Array, false, Array)
    #16 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php(23): FFMpeg\Media\AdvancedMedia->save()
    #17 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Drivers\PHPFFMpeg.php(214): ProtoneMedia\LaravelFFMpeg\Drivers\PHPFFMpeg->forwardCallTo(Object(ProtoneMedia\LaravelFFMpeg\FFMpeg\AdvancedMedia), 'save', Array)
    #18 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\MediaExporter.php(187): ProtoneMedia\LaravelFFMpeg\Drivers\PHPFFMpeg->__call('save', Array)
    #19 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\MediaExporter.php(135): ProtoneMedia\LaravelFFMpeg\Exporters\MediaExporter->saveWithMappings()
    #20 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\HLSExporter.php(209): ProtoneMedia\LaravelFFMpeg\Exporters\MediaExporter->save()
    #21 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Collections\Traits\EnumeratesValues.php(692): ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter->ProtoneMedia\LaravelFFMpeg\Exporters\{closure}(Object(Illuminate\Support\Collection))
    #22 E:\xampp\htdocs\mywanderlust\vendor\pbmedia\laravel-ffmpeg\src\Exporters\HLSExporter.php(223): Illuminate\Support\Collection->pipe(Object(Closure))
    #23 E:\xampp\htdocs\mywanderlust\app\Jobs\VideoConverterJob.php(60): ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter->save('8/index.m3u8')
    #24 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): App\Jobs\VideoConverterJob->handle()
    #25 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\Util.php(40): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
    #26 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
    #27 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
    #28 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\Container.php(610): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
    #29 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php(128): Illuminate\Container\Container->call(Array)
    #30 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(128): Illuminate\Bus\Dispatcher->Illuminate\Bus\{closure}(Object(App\Jobs\VideoConverterJob))
    #31 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(App\Jobs\VideoConverterJob))
    #32 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php(132): Illuminate\Pipeline\Pipeline->then(Object(Closure))
    #33 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(118): Illuminate\Bus\Dispatcher->dispatchNow(Object(App\Jobs\VideoConverterJob), false)
    #34 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(128): Illuminate\Queue\CallQueuedHandler->Illuminate\Queue\{closure}(Object(App\Jobs\VideoConverterJob))
    #35 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(App\Jobs\VideoConverterJob))
    #36 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(120): Illuminate\Pipeline\Pipeline->then(Object(Closure))
    #37 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(70): Illuminate\Queue\CallQueuedHandler->dispatchThroughMiddleware(Object(Illuminate\Queue\Jobs\DatabaseJob), Object(App\Jobs\VideoConverterJob))
    #38 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\Jobs\Job.php(98): Illuminate\Queue\CallQueuedHandler->call(Object(Illuminate\Queue\Jobs\DatabaseJob), Array)
    #39 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(406): Illuminate\Queue\Jobs\Job->fire()
    #40 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(356): Illuminate\Queue\Worker->process('database', Object(Illuminate\Queue\Jobs\DatabaseJob), Object(Illuminate\Queue\WorkerOptions))
    #41 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(158): Illuminate\Queue\Worker->runJob(Object(Illuminate\Queue\Jobs\DatabaseJob), 'database', Object(Illuminate\Queue\WorkerOptions))
    #42 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(116): Illuminate\Queue\Worker->daemon('database', 'default', Object(Illuminate\Queue\WorkerOptions))
    #43 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(100): Illuminate\Queue\Console\WorkCommand->runWorker('database', 'default')
    #44 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Illuminate\Queue\Console\WorkCommand->handle()
    #45 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\Util.php(40): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
    #46 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
    #47 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
    #48 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Container\Container.php(610): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
    #49 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Console\Command.php(136): Illuminate\Container\Container->call(Array)
    #50 E:\xampp\htdocs\mywanderlust\vendor\symfony\console\Command\Command.php(255): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
    #51 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Console\Command.php(121): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
    #52 E:\xampp\htdocs\mywanderlust\vendor\symfony\console\Application.php(971): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #53 E:\xampp\htdocs\mywanderlust\vendor\symfony\console\Application.php(290): Symfony\Component\Console\Application->doRunCommand(Object(Illuminate\Queue\Console\WorkCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #54 E:\xampp\htdocs\mywanderlust\vendor\symfony\console\Application.php(166): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #55 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Console\Application.php(93): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #56 E:\xampp\htdocs\mywanderlust\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(129): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #57 E:\xampp\htdocs\mywanderlust\artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #58 {main}```
    opened by Mordo95 11
  • Driver [] is not supported.

    Driver [] is not supported.

    I am using the following code FFMpeg::fromDisk('public/audio_profiles') ->open($this->filename) ->export() ->toDisk('public/audio_profiles') ->inFormat(new Aac) ->save('test.aac'); And getting the following error Driver [] is not supported. All the video paths are correct . But The code doesn't seems to work

    opened by Aryanr64x 11
  • Laravel 5.6 composer require pbmedia/laravel-ffmpeg  got error message

    Laravel 5.6 composer require pbmedia/laravel-ffmpeg got error message

    for laravel 5.6.3, when I run composer require pbmedia/laravel-ffmpeg,

    got below error message:

    • Conclusion: remove symfony/console v4.0.4
    • Conclusion: don't install symfony/console v4.0.4
    • symfony/process 2.1.x-dev conflicts with symfony/console[v4.0.4].

    ..........

    can you please help?

    Thanks

    opened by liucf 11
  • A non-numeric value encountered - AbstractProgressListener

    A non-numeric value encountered - AbstractProgressListener

    When encoding uploaded videos i always run into an error exception:

    #Code used

    `
    $lowBitrate = (new X264('libmp3lame'))->setKiloBitrate(250); $midBitrate = (new X264('libmp3lame'))->setKiloBitrate(500); $highBitrate = (new X264('libmp3lame'))->setKiloBitrate(1000);

    FFMpeg::fromDisk('public')->open($video)->exportForHLS()
        ->setSegmentLength(10)
        ->addFormat($lowBitrate)
        ->addFormat($midBitrate)
        ->addFormat($highBitrate)
        ->save($filename . '.m3u8');
    

    `

    #Stack Crawl

    [2017-02-02 12:47:39] local.INFO: ffmpeg running command '/usr/local/bin/ffmpeg' '-y' '-i' '/Users/oj/code/kinklink/storage/app/public/users/1/upload/p3Anee5RoWmd-1.mp4' '-map' '0' '-flags' '-global_header' '-f' 'segment' '-segment_format' 'mpeg_ts' '-segment_list' '/Users/oj/code/kinklink/storage/app/public/users/1/content/p3Anee5RoWmd-1_250.m3u8' '-segment_time' '10' '-threads' '12' '-vcodec' 'libx264' '-acodec' 'libmp3lame' '-b:v' '250k' '-refs' '6' '-coder' '1' '-sc_threshold' '40' '-flags' '+loop' '-me_range' '16' '-subq' '7' '-i_qfactor' '0.71' '-qcomp' '0.6' '-qdiff' '4' '-trellis' '1' '-b:a' '128k' '-pass' '1' '-passlogfile' '/var/folders/g_/b9xjq9yx6859bxwchm6tqp0c0000gr/T/ffmpeg-passes58932a6b4a6acfitu5/pass-58932a6b4a77e' '/content/p3Anee5RoWmd-1_250_%05d.ts' [2017-02-02 12:47:40] local.ERROR: ErrorException: A non-numeric value encountered in /Users/oj/code/kinklink/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php:182 Stack trace: #0 /Users/oj/code/kinklink/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php(182): Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(2, 'A non-numeric v...', '/Users/oj/code/...', 182, Array) #1 /Users/oj/code/kinklink/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Format/ProgressListener/AbstractProgressListener.php(136): FFMpeg\Format\ProgressListener\AbstractProgressListener->parseProgress('frame= 659 fps...') #2 /Users/oj/code/kinklink/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessRunner.php(90): FFMpeg\Format\ProgressListener\AbstractProgressListener->handle('err', 'frame= 659 fps...') #3 [internal function]: Alchemy\BinaryDriver\ProcessRunner->Alchemy\BinaryDriver\{closure}('err', 'frame= 659 fps...') #4 /Users/oj/code/kinklink/vendor/symfony/process/Process.php(1345): call_user_func(Object(Closure), 'err', 'frame= 659 fps...') #5 /Users/oj/code/kinklink/vendor/symfony/process/Process.php(1450): Symfony\Component\Process\Process->Symfony\Component\Process\{closure}('err', 'frame= 659 fps...')

    Thanks a lot for looking into the problem...

    opened by ojsoft 10
  • Export to s3

    Export to s3

    I have a working exportForHLS, but the resulting upload is just the m3u8 file when I use s3. Then it doesn't play.

    When I use local disk, there is the m3u8 and 3 ts files, and it plays.

            $file = storage_path() . '/uploads/videos/' . $this->filename;
    
            $lowBitrate = (new X264)->setKiloBitrate(250);
            $lowBitrate = ( new X264 )->setKiloBitrate( 144 )->setVideoCodec( 'libx264' )->setAudioCodec( "libmp3lame" )->setAdditionalParameters( [
                "-strict",
                "-2",
                ] );
            $midBitrate = (new X264)->setKiloBitrate(500)->setVideoCodec( 'libx264' )->setAudioCodec( "libmp3lame" )->setAdditionalParameters( [
                "-strict",
                "-2",
                ] );;
            $highBitrate = (new X264)->setKiloBitrate(1000)->setVideoCodec( 'libx264' )->setAudioCodec( "libmp3lame" )->setAdditionalParameters( [
                "-strict",
                "-2",
                ] );;
            
            FFMpeg::fromDisk('uploads')
                ->open('videos/'.$this->filename)
                ->exportForHLS()
                ->setSegmentLength(10) // optional
                // ->inFormat(new \FFMpeg\Format\Video\X264('libmp3lame'))
                ->addFormat($lowBitrate)
        
                ->addFormat($midBitrate)
                ->addFormat($highBitrate)
                ->toDisk('s3video')
                ->save('/videos/ffmpeg/'.$this->filename.'.m3u8');
    
    
    testers wanted 
    opened by tpharaoh 9
  • Problem with 'Unable to load FFProbe'

    Problem with 'Unable to load FFProbe'

    Greeting, I have a problem with this 'Unable to load FFProbe' I want to convert from all audio to .flac and my controller's just like this public function conversion(StoreAudioRequest $request)

       {
            $audio = Audio::create([
                'disk'          => 'audios_disk',
                'original_name' => $request->audio->getClientOriginalName(),
                'path'          => $request->audio->store('audios', 'audios_disk'),
                //'title'         => $request->title,
            ]);
    
            $this->dispatch(new ConvertAudioForDownloading($audio));
    
            return response()->json([
                'id' => $audio->id,
            ], 201);
        }
    

    Then run in $this->dispatch(new ConvertAudioForDownloading($audio)); its throw to public function handle()

          {
              $lowBitrateFormat = (new Flac)->setAudioKiloBitrate(16);
              FFMpeg::fromDisk($this->audio->disk)
                  ->open($this->audio->path)
                  ->export()
                  ->toDisk('downloadable_audios')
                  ->inFormat($lowBitrateFormat)
                  ->save($this->audio->id . '.flac');
              $this->audio->update([
                  'converted_for_downloading_at' => Carbon::now(),
              ]);
          }
    

    and Laravel call the error in FFMpeg::fromDisk($this->audio->disk). I followed with your old issue about this problem liked this https://github.com/pascalbaljetmedia/laravel-ffmpeg/issues/73 Can't solve this problem at all. What should I do? Thank you very much.

    Ps. I've set filesystems.php liked

       'disks' => [
            'audios_disk' => [
                'driver' => 'local',
                'root' => public_path('uploads'),
            ],
        ],
    
    opened by kawaiipeace 9
  • hls errors if you pass audio only

    hls errors if you pass audio only

    This solves the problem when you only want to generate hls from an mp3. Basically, MP3 doesn't have video, the code is as is,

    $low = (new X264)->setAudioKiloBitrate(96);
            $med = (new X264)->setAudioKiloBitrate(128);
            $high = (new X264)->setAudioKiloBitrate(256);
            $ultra = (new X264)->setAudioKiloBitrate(320);
    
            FFMpeg::fromDisk(config('filesystems.default'))
                ->open($storageLocation) // MP3 file
                ->exportForHLS()
                ->addFormat($low)
                ->addFormat($med)
                ->addFormat($high)
                ->addFormat($ultra)
                ->setSegmentLength(10)
                ->save($toLocation); //m3u8
    

    take note, I am only passing an MP3 file, here, you can still specify the AudioKB

    opened by aronquiray 0
  • Audio to HLS Problem

    Audio to HLS Problem

    Hi! I recently ran into an issue, Basically this is I wanted to run this works.

    ffmpeg -y -i "storage/audio.mp3" -c:a aac -b:a 320k -muxdelay 0 -f segment -sc_threshold 0 -segment_time 10 -segment_list "storage/audio.m3u8" -segment_format mpegts "storage/audio%d.m4a
    

    Tried this,

    FFMpeg::fromDisk(config('filesystems.default'))
                ->open($storageLocation) // MP3 file
                ->exportForHLS()
                ->save($location); //m3u8
    

    Throws error ProtoneMedia\LaravelFFMpeg\Exporters\NoFormatException

    I tried adding a format (take note, it's only audio that I am converting)

    $lowBitrate = (new X264)->setKiloBitrate(250);
    
            FFMpeg::fromDisk(config('filesystems.default'))
                ->open($storageLocation) // MP3 file
                ->exportForHLS()
                ->addFormat($lowBitrate)
                ->save($location); //m3u8
    

    Throws another error. ProtoneMedia\LaravelFFMpeg\Support\StreamParser::new(): Argument #1 ($stream) must be of type FFMpeg\FFProbe\DataMapping\Stream, null given

    I tried $audioFormat = (new Aac)->setAudioCodec('libfdk_aac'); ->addFormat($audioFormat)

    ProtoneMedia\LaravelFFMpeg\Exporters\HLSExporter::getSegmentPatternAndFormatPlaylistPath(): Argument #2 ($format) must be of type FFMpeg\Format\VideoInterface, FFMpeg\Format\Audio\Aac given,

    I'm quite confused. normally, thru normal execution via cli, it does work. how can I achieve that, or is there something I am missing?

    opened by aronquiray 0
  • Too few arguments to function FilesystemAdapter::setVisibility()

    Too few arguments to function FilesystemAdapter::setVisibility()

    Hello, get error in line https://github.com/protonemedia/laravel-ffmpeg/blob/main/src/Filesystem/Media.php#L151

    With forwards call to FilesystemAdapter method setVisibility expect 2 arguments ($path and $visibility), but in your code you pass only $visibility (but in previous method in Media.php, called copyAllFromTemporaryDirectory you pass two arguments and it's work).

    Can you please fix that method with something like this?

    public function setVisibility(string $path, string $visibility = null)
    {
        $disk = $this->getDisk();
    
        if ($visibility && $disk->isLocalDisk()) {
            $disk->setVisibility($path, $visibility);
        }
    
        return $this;
    }
    

    and this in https://github.com/protonemedia/laravel-ffmpeg/blob/main/src/Exporters/MediaExporter.php#L242

    if ($outputMedia) {
        $outputMedia->copyAllFromTemporaryDirectory($this->visibility);
        $outputMedia->setVisibility($path, $this->visibility);
    }
    
    

    Laravel 9.45.0 PHP 8.1 laravel-ffmpeg 8.2.0

    Thank you

    opened by ateamcms 0
  • Can't execute ffmpeg command - any idea why?

    Can't execute ffmpeg command - any idea why?

    This is the Reddit post i just created: https://www.reddit.com/r/ffmpeg/comments/z6k106/it_was_working_and_then_it_wasnt_ffmpeg_with_code/ - thought it ight be valuable to tag it here also - if you have any input much appreciated. Thank you!

    opened by HeadStudios 0
  • is it possible to force download m3u8 file from laravel side to client side

    is it possible to force download m3u8 file from laravel side to client side

    i want the user to download the file once he visits the m3u8 video path

    when i serve the file through FFMPEG DynamicPlaylist : in desktop -> browsers do it by default but in mobile -> browsers streams the file normally as the normal player does

    so how can i force the download when the user calls the api endpoint

    i hope anyone answer me if it's possible or not and if yes, How ?

    Thank you so much in advance

    opened by MohamedAbdel-wahed 1
Releases(8.2.0)
Owner
Protone Media
We are a Dutch software company that develops apps, websites, and cloud platforms. As we're building projects, we gladly contribute to OSS by sharing our work.
Protone Media
PHP Exif Library - library for reading and writing Exif headers in JPEG and TIFF files using PHP.

PEL: PHP Exif Library README file for PEL: PHP Exif Library. A library with support for reading and writing Exif headers in JPEG and TIFF images using

null 264 Dec 4, 2022
Anime4KCPP provides an optimized bloc97's Anime4K algorithm - a high performance anime upscaler

Anime4KCPP provides an optimized bloc97's Anime4K algorithm version 0.9, and it also provides its own CNN algorithm ACNet, it provides a variety of way to use, including preprocessing and real-time playback, it aims to be a high performance tools to process both image and video.

TianZer 1.5k Jan 1, 2023
Laravel Package for making thumbnails instantly

Laravel Thumbnail Generator Package for uploading the image and saving that image along with it's thumbnail. What does it do ? Uploads Image Make its

DRH2SO4 50 Dec 5, 2022
Laravel Optical Character Reader(OCR) package using ocr engines like Tesseract

LaraOCR Laravel Optical Character Reader(OCR) package using ocr engines like Tesseract under the hood. Features Read text from image using WebUI/Progr

Al Imran Ahmed 100 Jan 4, 2023
Picasso is a Laravel Image Management and Optimization Package

Picasso is a Laravel Image Management and Optimization Package. Define image dimensions and options, store uploaded image in multiple dimensions with or without a watermark and retrieve optimized images on your website when needed.

Laravelista 82 Nov 24, 2022
A Laravel Gravatar package for retrieving gravatar image URLs or checking the existance of an image.

Gravatar for Laravel 5.x, 6, 7 and 8 Installation First, pull in the package through Composer via the command line: composer require creativeorange/gr

Creativeorange 477 Dec 1, 2022
Laravel package a helper to Generate the QR code and signed it for ZATCA E-invoicing

Laravel ZATCA E-invoicing Introduction Laravel package a helper to Generate the QR code and signed it for ZATCA E-invoicing Installation To get the la

Ayman Alaiwah 8 Aug 17, 2022
An unofficial package maintained by Salla to help developers to implement ZATCA (Fatoora) QR code easily which required for e-invoicing

ZATCA (Fatoora) QR-Code Implementation An unofficial package maintained by Salla to help developers to implement ZATCA (Fatoora) QR code easily which

Salla 96 Dec 25, 2022
Auto Image & file upload, resize and crop for Laravel eloquent model using Intervention image

Laravel ImageUp The qcod/laravel-imageup is a trait which gives you auto upload, resize and crop for image feature with tons of customization. Install

QCode.in 708 Dec 22, 2022
Laragram is a simple instagram "clone" built with Laravel and Tailwind CSS

Laragram is a simple instagram "clone" built with Laravel and Tailwind CSS that gives to the users the ability to create or edit their own profiles (including profile image and description), upload images and share them among friends

null 0 Jul 24, 2021
Laravel Qcloud Content Security T-Sec 腾讯云内容安全(文字图片内容审核)服务

Laravel Qcloud Content Security T-Sec 天御内容安全服务使用了深度学习技术,识别文本/图片中出现的可能令人反感、不安全或不适宜内容,支持用户配置词库/图片黑名单,识别自定义的识别类型。

安正超 34 Jun 20, 2022
An open source image hosting service powered by Laravel

Limg An open source image hosting service powered by Laravel Features Upload your image via file, url or ShareX ! Manage your image (custom title, pub

Thomas 56 Dec 16, 2022
An easy-to-use PHP QrCode generator with first-party support for Laravel.

An easy-to-use PHP QrCode generator with first-party support for Laravel.

Simple Software LLC 2.2k Jan 5, 2023
get nearby location by coordinate from eloquent laravel

LARAVEL-COORDINATE get nearby location data from database with eloquent laravel Installation composer require bagusindrayana/laravel-coordinate In M

Bagus Indrayana 26 Sep 9, 2022
Optimize your images on the fly with Glide for Laravel.

Glide for Laravel Optimize your images on the fly with Glide for Laravel. Support us Like our work? You can support us by purchasing one of our produc

Flowframe 53 Oct 17, 2022
Laravel Favicon - Create dynamic favicons based on your environment settings.

Laravel Favicon Create dynamic favicons based on your environment settings. Laravel Package Development If you want to learn how to create reusable PH

Beyond Code 320 Dec 12, 2022
This is an image manipulation REST API written in PHP Laravel Framework

Laravel Image Manipulation REST API Demo Here is fully working Demo: https://www.lobiimages.com/ You have to register first in order to generate acces

TheCodeholic 42 Dec 15, 2022
🤹‍♀️Very simple to use Gravatar implementation for Laravel

Very simple to use Gravatar implementation for Laravel. Install, Include the Facade and then generate, simple! Installation You can install the packag

Wavey 11 May 7, 2022
This package provides an integration with FFmpeg for Laravel. Laravel's Filesystem handles the storage of the files.

Laravel FFMpeg This package provides an integration with FFmpeg for Laravel 6.0 and higher. Laravel's Filesystem handles the storage of the files. Lau

Protone Media 1.3k Jan 1, 2023
PHP FFmpeg - An Object Oriented library to convert video/audio files with FFmpeg / AVConv

PHP FFmpeg An Object Oriented library to convert video/audio files with FFmpeg / AVConv. Check another amazing repo: PHP FFMpeg extras, you will find

Alchemy 10 Dec 31, 2022