Convert large video files with php-ffmpeg

698 Views Asked by At

I have a very strange problem. I need to convert video files by file resolution, for example, if the video file resolution is '1920x1080' then it must convert to 1080, 720, and 480 resolutions. The converter works fine but with low-size files. For example, I have a 46 MB '1920x1080' file, and it's converting to 1080, 720, and 480. and its works fine. But I have another file which is 312 MB and its resolution is 1280x720. it must convert to 720 and 480, but when it's starting converting 720 and some point it stops, and am getting an error.

{
    "success": false,
    "message": "Encoding failed"
}

I tried everything I really don't understand why am getting this error. and why it converts low-size files and does not convert big-size files... Can please someone help me with this task...

So this is my method:

    public function convertVideo(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'video' => 'required|mimes:mp4',
                'content_type' => 'required|string',
                'content_id' => 'required|numeric',
            ]);

            if ($validator->fails()) {
                $errors = $validator->errors()->all();
                return response()->json(['error' => $errors], 400);
            }

            $instance = new File();

            // Get the uploaded file from the request
            $file = $request->file('video');
            // (show/serie/movie)
            $contentType = $request->input('content_type');
            // ID 
            $contentId = $request->input('content_id');

            // Store the uploaded video file and get its path
            $filePath = $file->storeAs('temp', $file->getClientOriginalName(), 'local');
            $filePath = storage_path('app/' . $filePath);

            // Getting video formats. ( 1080p, 720p & 480p )
            $formats  = $instance->getVideoFormats($filePath);

            // Getting movie, show or serie.
            $content = $instance->getContentForVideo($contentType, $contentId);
            $userId = $content->user_id;

            foreach ($formats as $format) {
                $message = "Started converting " . $format['resolution'] . " on " . $content->title . ' content.';
                $event = new VideoConvertor($message, $userId);
                Event::dispatch($event);

                // Convert video to formats.resolution
                $result = $instance->convertToResolution($filePath, $format);

                if ($result->success === true) {
                    $convertedVideo = VideoConversion::where('file_name', $result->convertedVideoName)->where('status', 'pending')->where('bucket_status', 'pending')->firstOrFail();

                    if ($convertedVideo) {
                        $convertedVideo->status = 'completed';
                        $convertedVideo->content_type = $contentType;
                        $convertedVideo->content_id = $contentId;
                        $convertedVideo->save();

                        // Sending notification to user (Who is uploading video) that video was converted, and we are starting uploading it on bucket. 
                        $message = $convertedVideo->resolution . " was converted for " . $content->title . ' content.';

                        $event = new VideoConvertor($message, $userId);
                        Event::dispatch($event);

                    } else {
                        return response()->json([
                            'success' => false,
                            'message' => 'Something went wrong while updating video conversion status.',
                        ], 500);
                    }
                } else if ($result->success === false) {
                    return response()->json([
                        'success' => false,
                        'message' => $result->message,
                    ], 500);
                }
            }

            // Delete the file from local storage
            unlink($filePath);

            return response()->json([
                'message' => 'Video was converted successfully.',
            ], 200);
        } catch (\Exception $e) {
            return response()->json([
                'message:' =>  $e->getMessage(),
            ], 500);
        }
    }

my getVideoFormats function:

    public function getVideoFormats($filePath)
    {
        $ffprobe = FFProbe::create();

        // Get the resolution of the video
        $resolution = $ffprobe->streams($filePath)->videos()->first()->get('width') . 'x' . $ffprobe->streams($filePath)->videos()->first()->get('height');
        // Create 1080p and lower video if the resolution is higher than 1080p
        if ($resolution === '1920x1080') {
            $formats = [
                ['resolution' => '1080p', 'format' => new X264('aac', 'libx264', 'mp4'), 'event' => 'video.converted.1080p', 'dimension' => new Dimension(1920, 1080)],
                ['resolution' => '720p', 'format' => new X264('aac', 'libx264', 'mp4'), 'event' => 'video.converted.720p', 'dimension' => new Dimension(1280, 720)],
                ['resolution' => '480p', 'format' => new X264('aac', 'libx264', 'mp4'), 'event' => 'video.converted.480p', 'dimension' => new Dimension(854, 480)],
            ];
        }
        // Create 720p and lower video if the resolution is higher than 720p
        else if ($resolution === '1280x720') {
            $formats = [
                ['resolution' => '720p', 'format' => new X264('aac', 'libx264', 'mp4'), 'event' => 'video.converted.720p', 'dimension' => new Dimension(1280, 720)],
                ['resolution' => '480p', 'format' => new X264('aac', 'libx264', 'mp4'), 'event' => 'video.converted.480p', 'dimension' => new Dimension(854, 480)],
            ];
        }
        // Create 480p video if the resolution is lower than 720p
        else if ($resolution === '854x480') {
            $formats = [
                ['resolution' => '480p', 'format' => new X264('aac', 'libx264', 'mp4'), 'event' => 'video.converted.480p', 'dimension' => new Dimension(854, 480)],
            ];
        } else {
            $formats = [];
        }

        return $formats;
    }

And the convertToResolution function

    public function convertToResolution($filePath, $format)
    {
        try {
            $uuid = uniqid();
            $randomVideoName = "{$uuid}-{$format['resolution']}.mp4";

            $outputPath = storage_path("app/videos/{$randomVideoName}");
            $resizeFilter = new ResizeFilter($format['dimension']);

            $ffmpeg = FFMpeg::create();
            $video = $ffmpeg->open($filePath);


            // Perform video conversion and save it to the output path
            $video->addFilter($resizeFilter)->save($format['format'], $outputPath);

            // Save the converted video file name in a separate table
            VideoConversion::create([
                'file_name' => $randomVideoName,
                'status' => 'pending',
                'bucket_status' => 'pending',
                'resolution' => $format['resolution'],
            ]);

            return (object) [
                'success' => true,
                'convertedVideoName' => $randomVideoName,
            ];
        } catch (ProcessFailedException $e) {
            Log::error('Video encoding failed: ' . $e->getMessage());
            return (object) [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        } catch (\Exception $e) {
            Log::error('Error during video conversion: ' . $e->getMessage());
            return (object) [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

Any opinion on what the hell this convertor wants? Oh yes, and I increased memory_limit = 1G upload_max_filesize = 3G post_max_size = 3G max_execution_time = 600 in php.ini but the same result...

...................... I dd($e) and am getting this

FFMpeg\Exception\RuntimeException {#1528 ▼ // app/Models/File.php:219
  #message: "Encoding failed"
  #code: 0
  #file: "/project/backend/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/AbstractVideo.php"
  #line: 116
  -previous: Alchemy\BinaryDriver\Exception\ExecutionFailureException {#1526 ▶}
  trace: {▼
    /project/backend/vendor/php-ffmpeg/php-ffmpeg/src/FFMpeg/Media/AbstractVideo.php:116 {▶}
    /project/backend/app/Models/File.php:198 {▼
      App\Models\File->convertToResolution($filePath, $format) …
      › // Perform video conversion and save it to the output path
      › $video->addFilter($resizeFilter)->save($format['format'], $outputPath);
      › 
    }
    /project/backend/app/Http/Controllers/FileController.php:175 {▼
      App\Http\Controllers\FileController->convertVideo(Request $request) …
      › // Convert video to formats.resolution
      › $result = $instance->convertToResolution($filePath, $format);
      › 
    }
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Controller.php:54 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php:43 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Route.php:259 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Route.php:205 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Router.php:798 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141 {▶}
    /project/backend/app/Http/Middleware/CheckRole.php:38 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php:50 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php:126 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php:92 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php:54 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php:44 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php:44 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:116 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Router.php:797 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Router.php:776 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Router.php:740 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Routing/Router.php:729 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:190 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php:21 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php:31 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php:21 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php:40 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php:27 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php:86 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php:62 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php:39 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:116 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:165 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:134 {▶}
    /project/backend/public/index.php:51 {▶}
    /project/backend/vendor/laravel/framework/src/Illuminate/Foundation/resources/server.php:16 {▶}
  }
}

-previous: Alchemy\BinaryDriver\Exception\ExecutionFailureException

#message: """
      ffmpeg failed to execute command '/usr/bin/ffmpeg' '-y' '-i' '/project/vod/backend/storage/app/temp/002 Advanced - Grammar - All or What for Emp ▶
      
      Error Output:
      
       ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers
        built with gcc 11 (Ubuntu 11.2.0-19ubuntu1)
        configuration: --prefix=/usr --extra-version=0ubuntu0.22.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu - ▶
        libavutil      56. 70.100 / 56. 70.100
        libavcodec     58.134.100 / 58.134.100
        libavformat    58. 76.100 / 58. 76.100
        libavdevice    58. 13.100 / 58. 13.100
        libavfilter     7.110.100 /  7.110.100
        libswscale      5.  9.100 /  5.  9.100
        libswresample   3.  9.100 /  3.  9.100
        libpostproc    55.  9.100 / 55.  9.100
      Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/project/vod/backend/storage/app/temp/002 Advanced - Grammar - All or What for Emphasis.mp4':
        Metadata:
          major_brand     : mp42
          minor_version   : 0
          compatible_brands: isomiso2avc1mp41mp42
          encoder         : Lavf53.32.100
        Duration: 00:11:32.61, start: 0.000000, bitrate: 2182 kb/s
        Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1989 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default)
          Metadata:
            handler_name    : VideoHandler
            vendor_id       : [0][0][0][0]
        Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 189 kb/s (default)
          Metadata:
            handler_name    : SoundHandler
            vendor_id       : [0][0][0][0]
      Stream mapping:
        Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
        Stream #0:1 -> #0:1 (aac (native) -> aac (native))
      Press [q] to stop, [?] for help
      [libx264 @ 0x558dbd591a40] using SAR=1/1
      [libx264 @ 0x558dbd591a40] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2 AVX512
      [libx264 @ 0x558dbd591a40] profile High, level 4.0, 4:2:0, 8-bit
      [libx264 @ 0x558dbd591a40] 264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref ▶
      Output #0, mp4, to '/project/vod/backend/storage/app/videos/646b1879e764b-720p.mp4':
        Metadata:
          major_brand     : mp42
          minor_version   : 0
          compatible_brands: isomiso2avc1mp41mp42
          encoder         : Lavf58.76.100
        Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 1000 kb/s, 25 fps, 12800 tbn (default)
          Metadata:
            handler_name    : VideoHandler
            vendor_id       : [0][0][0][0]
            encoder         : Lavc58.134.100 libx264
          Side data:
            cpb: bitrate max/min/avg: 0/0/1000000 buffer size: 0 vbv_delay: N/A
        Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
          Metadata:
            handler_name    : SoundHandler
            vendor_id       : [0][0][0][0]
            encoder         : Lavc58.134.100 aac
      frame=    3 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    \rframe=   94 fps=0.0 q=28.0 size=       0kB time=00:00:03.88 bitrate=   0 ▶
      """
    #code: 0
    #file: "/project/vod/backend/vendor/php-ffmpeg/php-ffmpeg/src/Alchemy/BinaryDriver/ProcessRunner.php"
    #line: 94
    -previous: Symfony\Component\Process\Exception\ProcessTimedOutException {#1526 ▶}
    #command: "'/usr/bin/ffmpeg' '-y' '-i' '/project/vod/backend/storage/app/temp/002 Advanced - Grammar - All or What for Emphasis.mp4' '-vcodec' 'libx264' '- ▶"
    #errorOutput: """
      ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers
        built with gcc 11 (Ubuntu 11.2.0-19ubuntu1)
        configuration: --prefix=/usr --extra-version=0ubuntu0.22.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu - ▶
        libavutil      56. 70.100 / 56. 70.100
        libavcodec     58.134.100 / 58.134.100
        libavformat    58. 76.100 / 58. 76.100
        libavdevice    58. 13.100 / 58. 13.100
        libavfilter     7.110.100 /  7.110.100
        libswscale      5.  9.100 /  5.  9.100
        libswresample   3.  9.100 /  3.  9.100
        libpostproc    55.  9.100 / 55.  9.100
      Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/project/vod/backend/storage/app/temp/002 Advanced - Grammar - All or What for Emphasis.mp4':
        Metadata:
          major_brand     : mp42
          minor_version   : 0
          compatible_brands: isomiso2avc1mp41mp42
          encoder         : Lavf53.32.100
        Duration: 00:11:32.61, start: 0.000000, bitrate: 2182 kb/s
        Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1989 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default)
          Metadata:
            handler_name    : VideoHandler
            vendor_id       : [0][0][0][0]
        Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 189 kb/s (default)
          Metadata:
            handler_name    : SoundHandler
            vendor_id       : [0][0][0][0]
      Stream mapping:
        Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
        Stream #0:1 -> #0:1 (aac (native) -> aac (native))
      Press [q] to stop, [?] for help
      [libx264 @ 0x558dbd591a40] using SAR=1/1
      [libx264 @ 0x558dbd591a40] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2 AVX512
      [libx264 @ 0x558dbd591a40] profile High, level 4.0, 4:2:0, 8-bit
      [libx264 @ 0x558dbd591a40] 264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref ▶
      Output #0, mp4, to '/project/vod/backend/storage/app/videos/646b1879e764b-720p.mp4':
        Metadata:
          major_brand     : mp42
          minor_version   : 0
          compatible_brands: isomiso2avc1mp41mp42
          encoder         : Lavf58.76.100
        Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 1000 kb/s, 25 fps, 12800 tbn (default)
          Metadata:
            handler_name    : VideoHandler
            vendor_id       : [0][0][0][0]
            encoder         : Lavc58.134.100 libx264
          Side data:
            cpb: bitrate max/min/avg: 0/0/1000000 buffer size: 0 vbv_delay: N/A
        Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
          Metadata:
            handler_name    : SoundHandler
            vendor_id       : [0][0][0][0]
            encoder         : Lavc58.134.100 aac
      frame=    3 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    \rframe=   94 fps=0.0 q=28.0 size=       0kB time=00:00:03.88 bitrate=   0 ▶
      """
1

There are 1 best solutions below

1
Boychik On

I added timeout when creating FFMpeg and now it can convert large files.

      $ffmpeg = FFMpeg::create(array(
                'timeout'          => 0, // The timeout for the underlying process
            ));