spAo's avatar
Level 1

Can't convert large video files with php-ffmpeg

Hello all, 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?

0 likes
3 replies
spAo's avatar
Level 1

I increased memory_limit = 1G no result added this ['-preset', 'ultrafast'] no result ['-crf', '23', '-preset', 'medium', '-tune', 'film'] no result

any ideas?

spAo's avatar
Level 1

Is my question visible? : /

Tray2's avatar

@spAo Relax, it might take some time when you share a shitload of code and don't provide any error message.

My guess is that you upload a file, and then in your controller you convert it right?

If you do it that way, the request will take quite some time, and probably time out after 60 seconds.

What you need to do is upload the file, store it in some kind of to process directory, then register a job that converts it in the background.

Please or to participate in this conversation.