Transcoding Videos for Web Streaming with FFmpeg using Laravel Queues

I’ve been working on a project where we were using AWS elastic transcoder for media conversion. Elastic transcoder is a highly scalable solution for media transcoding. However, it charges your per minute for media conversion depending on your region. To reduce operational costs, we decided to shift away from AWS transcoder and use FFmpeg with Laravel for media conversion on our own servers. In this tutorial, I’ll show you how we can use FFmpeg for media conversion and defer processing using Laravel Queues.

Let’s get started by setting up a new project. Create a new Video model, its migration, and controller.  We will store uploaded videos information on videos table.

On file upload, we will store video title, original file name, and path of the stored file in the database. After upload, we will dispatch a Job for transcoding it to a web streamable format and update stream_path with the output file path, update converted_for_streaming_at timestamp and set processed to true after FFmpeg is done processing uploaded media file.

In Video model class, add the converted_for_streaming_at column to $dates array so that it should be mutated to dates like created_at or updated_at columns.

Add these routes to web.php file.

GET /uploader route will render a form for uploading videos and POST /upload route will handle the form submission, upload video, create a database record and dispatch an FFmpeg transcoding job. GET / index route will render videos view where all uploaded videos will be displayed in native HTML video player.

In VideoController add these methods.

Create  uploader.blade.php under views directory.

Also, create a StoreVideoRequest form request for validating uploader form input.

We have a  mimetypes validation rule with video/* wildcard to only allow video uploads.

Now create a  ConvertVideoForStreaming job which will be dispatched after video is done uploading and a database record is created in VideoController@store method.

In handle() method of the dispatched job, we will create a low bitrate X264 format. We will open uploaded file from public disk and add a resize filter to it. Then we will tell FFmpeg to start transcoding by calling export() method and output file to public disk in a low bitrate mp4 container format.

Before you go an test it, make sure you have installed Laravel FFmpeg package that we are using in our transcoding job.

Also, make sure you have ffmpeg binaries installed of your machine. If you’re running Linux, you can easily install it by running following apt install command.

You must also add FFmpeg Service Provider and Facade to app.php.

and run following command to publish package configuration files.

If you’re running windows, you must add ffmpeg binaries to the system PATH. If you don’t have access to that, you can define these environment variables in your .env file.

Laravel Queues Configuration

You also need to configure queue connection in your env file. For this tutorial, I’m using database queue connection. Edit .env  file and update QUEUE_CONNECTION variable to database.

Also run  php artisan queue:table to create database queue table migration and php artisan migrate to create table. To deal with failed jobs, run  php artisan queue:failed-table to create failed queue jobs migration table and  php artisan migrate to create table.

Running Queue Worker

Before we go and test, run Laravel’s queue worker

we have added a --timeout flag to queue worker. This indicates that don’t want our queue jobs to run longer than 8600 seconds.

Now if you head over to /uploader route in your application and upload a video file, a database record will be created a transcoding job will be dispatched. You’ll be able to view your dispatched job in the terminal.

Displaying Videos

Create videos.blade.php file under view directory.

We will display an alert for videos that are currently being processed. For processed videos, we will render a video element with transcoded stream_path.

Here’s a demo of what we have done so far.

I have set up a Github repository with example application code. If you run into any issue or have any questions, leave a comment and I will try to help you in any way possible.

Share this post

19 thoughts on “Transcoding Videos for Web Streaming with FFmpeg using Laravel Queues”

  1. Hello Waleed,

    I wanted to repicate your transcoder webservice, unfortunatelly I keep running into the same issue like the guy who posted on your youtuve video.

    [2019-04-28 10:18:38][1] Processing: App\Jobs\ConvertVideoForStreaming
    [2019-04-28 10:18:38][2] Processing: App\Jobs\ConvertVideoForStreaming
    [2019-04-28 10:18:38][3] Processing: App\Jobs\ConvertVideoForStreaming
    [2019-04-28 10:18:38][3] Failed: App\Jobs\ConvertVideoForStreaming

    I copied the binaries into my web root folder in htdocs/xampp (local webserver)

    FFmpeg seems to be installed correctly since it is responding on comands globally with no problems.

    The video I am trying to upload and convert is a test.mp4 about 50 MB big

    I adjusted the .ENV variable to

    FFMPEG_BINARIES=’/FFmpeg’
    FFPROBE_BINARIES=’/FFmpeg’

    where I put the binaries and I also upgraded them under ENVIRONMENT VARIABLES System Variables in windows.

    Let me know if you have any ideas 🙂

    Cheers

    Rainer

      1. I am also getting failed jobs error.Please mention the correct way of declaring “FFMPEG_BINARIES” and “FFPROBE_BINARIES”.

  2. Hello! I have some strange problem. I don’t have anything in queue:work.
    Its just staying without printing any single line. But the movies are been converted.

  3. I have the same error as other students. I have set up the absolute path but still not working
    FFMPEG_BINARIES=’C:/ffmpeg/bin/ffmpeg.exe’ even if l remove the .exe
    FFPROBE_BINARIES=’C:/ffmpeg/bin/ffprobe.exe’

    Also, the package doesn’t recognize use FFMpeg;

    ‘local’,

    ‘ffmpeg’ => [
    ‘binaries’ => env(‘FFMPEG_BINARIES’, ‘ffmpeg’),
    ‘threads’ => 12,
    ],
    ‘ffprobe’ => [
    ‘binaries’ => env(‘FFPROBE_BINARIES’, ‘ffprobe’),
    ],
    ‘timeout’ => 3600,
    ];

    in the log file
    [2019-12-01 20:58:29] local.INFO: ffprobe running command “C:/ffmpeg/bin/ffprobe.exe” -help -loglevel quiet
    [2019-12-01 20:58:30] local.INFO: ffprobe executed command successfully
    [2019-12-01 20:58:30] local.INFO: ffprobe running command “C:/ffmpeg/bin/ffprobe.exe” “C:\laragon\www\clone-youtube-application\storage\app\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4” -show_streams -print_format json
    [2019-12-01 20:58:30] local.ERROR: ffprobe failed to execute command “C:/ffmpeg/bin/ffprobe.exe” “C:\laragon\www\clone-youtube-application\storage\app\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4” -show_streams -print_format json
    [2019-12-01 20:58:30] local.ERROR: Unable to probe C:\laragon\www\clone-youtube-application\storage\app\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4 {“exception”:”[object] (FFMpeg\\Exception\\RuntimeException(code: 0): Unable to probe C:\\laragon\\www\\clone-youtube-application\\storage\\app\\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4 at C:\\laragon\\www\\clone-youtube-application\\vendor\\pbmedia\\php-ffmpeg\\src\\FFMpeg\\FFProbe.php:263)
    [stacktrace]

    i

      1. I am running on php 7.37

        [2019-12-01 20:58:29] local.INFO: ffprobe running command “C:/ffmpeg/bin/ffprobe.exe” -help -loglevel quiet
        [2019-12-01 20:58:30] local.INFO: ffprobe executed command successfully
        [2019-12-01 20:58:30] local.INFO: ffprobe running command “C:/ffmpeg/bin/ffprobe.exe” “C:\laragon\www\clone-youtube-application\storage\app\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4” -show_streams -print_format json
        [2019-12-01 20:58:30] local.ERROR: ffprobe failed to execute command “C:/ffmpeg/bin/ffprobe.exe” “C:\laragon\www\clone-youtube-application\storage\app\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4” -show_streams -print_format json
        [2019-12-01 20:58:30] local.ERROR: Unable to probe C:\laragon\www\clone-youtube-application\storage\app\channels/3bf10f3c-59f6-4e9d-94ea-110459ece645/4nQxPhT8ejB724eilbwxrFxHkgwfMV8pKycTWkpE.mp4 {“exception”:”[object] (FFMpeg\\Exception\\RuntimeException(code: 0):

  4. I got error how can i fix it? thank you.

    message: “Encoding failed”
    exception: “FFMpeg\Exception\RuntimeException”
    file: “C:\Users\visal\Desktop\project test\ffmpeg_blog\vendor\pbmedia\php-ffmpeg\src\FFMpeg\Media\AbstractVideo.php”
    line: 106

  5. hi buddy,
    I am using ajax in controller store method , after :
    ProcessVideoForStreming::dispatch($video);

    return response()->json([message => ‘video under processing’]);

    here, return response is not working. It is only working after the job finishes , but I want to
    send message first and process the video after it

  6. Hi Waleed,
    Thanks for your sharing, but i’m getting an error when run php artisan vendor:publish –provider=”Pbmedia\LaravelFFMpeg\FFMpegServiceProvider”
    Message: Class Pbmedia\LaravelFFMpeg\FFMpegServiceProvider not found.
    I install ffmpeg at my applycation root/app so my path is :
    FFMPEG_BINARIES=/app/ffmpeg/bin
    FFPROBE_BINARIES=/app/ffmpeg/bin
    Is there some thing wrong?

    1. Hi Cheng,
      I have the same issue with FFMpegServiceProvider

      In ProviderRepository.php line 208:
      Class ‘Pbmedia\LaravelFFMpeg\FFMpegServiceProvider’ not found

      I really couldn’t find correct answers or solutions for this problem.

      1. Yes i did one,

        comment out that line from app/config.php and after run this two lines:
        php artisan cache:clear
        php artisan config:clear

        and run again your command

      2. I solved this by removing the two lines from the config/app.php and then run the following:
        COMPOSER_MEMORY_LIMIT=-1 composer require pbmedia/laravel-ffmpeg

        Worked like a charm 🤓

  7. Hi, I’m pretty new in laravel. And when I want to run the server it says the following:

    Warning: require(C:\wamp64\www\laravel-stream/vendor/autoload.php): failed to open stream: No such file or directory in C:\wamp64\www\laravel-stream\artisan on line 18

    Fatal error: require(): Failed opening required ‘C:\wamp64\www\laravel-stream/vendor/autoload.php’ (include_path=’.;C:\php\pear’) in C:\wamp64\www\laravel-stream\artisan
    on line 18

    What should I do in this situation?

  8. Hello Waleed,

    My jobs get executed correctly, but my videos are just 1 seconds long and gray or very pixely quality and one second long.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.