PHP and FFMPEG - Performing intelligent video conversion

前端 未结 3 1816
北海茫月
北海茫月 2020-12-24 00:14

I have an oddly difficult task to perform. I thought it would be easy, but all my efforts have been fruitless.

I\'m converting videos uploaded to a php script from

3条回答
  •  无人及你
    2020-12-24 00:28

    Ok. I have a fully-functional solution. This is for anyone who finds this question wanting to do the same thing. My code may not be very elegant, but it gets the job done.


    Getting FFMPEG's output

    First I had to get the output from ffmpeg -i, which was a challenge in itself. Thanks to hegemon's answer on my other question, I was finally able to get it working with 2>&1 at the end of my command. And thanks to Evert's answer to this question, I was able to parse the output with preg_match to find the original file's height and width.

    function get_vid_dim($file)
    {
        $command = '/usr/bin/ffmpeg -i ' . escapeshellarg($file) . ' 2>&1';
        $dimensions = array();
        exec($command,$output,$status);
        if (!preg_match('/Stream #(?:[0-9\.]+)(?:.*)\: Video: (?P.*) (?P[0-9]*)x(?P[0-9]*)/',implode('\n',$output),$matches))
        {
            preg_match('/Could not find codec parameters \(Video: (?P.*) (?P[0-9]*)x(?P[0-9]*)\)/',implode('\n',$output),$matches);
        }
        if(!empty($matches['width']) && !empty($matches['height']))
        {
            $dimensions['width'] = $matches['width'];
            $dimensions['height'] = $matches['height'];
        }
        return $dimensions;
    }
    

    Determining the best dimensions

    I wrote this function to determine the best dimensions to use for conversion. It takes the original's dimensions, target dimensions, and whether or not to force conversion to the target aspect ratio (determined from its width/height). The target dimensions will always be the largest result this function can return.

    function get_dimensions($original_width,$original_height,$target_width,$target_height,$force_aspect = true)
    {
        // Array to be returned by this function
        $target = array();
        // Target aspect ratio (width / height)
        $aspect = $target_width / $target_height;
        // Target reciprocal aspect ratio (height / width)
        $raspect = $target_height / $target_width;
    
        if($original_width/$original_height !== $aspect)
        {
            // Aspect ratio is different
            if($original_width/$original_height > $aspect)
            {
                // Width is the greater of the two dimensions relative to the target dimensions
                if($original_width < $target_width)
                {
                    // Original video is smaller.  Scale down dimensions for conversion
                    $target_width = $original_width;
                    $target_height = round($raspect * $target_width);
                }
                // Calculate height from width
                $original_height = round($original_height / $original_width * $target_width);
                $original_width = $target_width;
                if($force_aspect)
                {
                    // Pad top and bottom
                    $dif = round(($target_height - $original_height) / 2);
                    $target['padtop'] = $dif;
                    $target['padbottom'] = $dif;
                }
            }
            else
            {
                // Height is the greater of the two dimensions relative to the target dimensions
                if($original_height < $target_height)
                {
                    // Original video is smaller.  Scale down dimensions for conversion
                    $target_height = $original_height;
                    $target_width = round($aspect * $target_height);
                }
                //Calculate width from height
                $original_width = round($original_width / $original_height * $target_height);
                $original_height = $target_height;
                if($force_aspect)
                {
                    // Pad left and right
                    $dif = round(($target_width - $original_width) / 2);
                    $target['padleft'] = $dif;
                    $target['padright'] = $dif;
                }
            }
        }
        else
        {
            // The aspect ratio is the same
            if($original_width !== $target_width)
            {
                if($original_width < $target_width)
                {
                    // The original video is smaller.  Use its resolution for conversion
                    $target_width = $original_width;
                    $target_height = $original_height;
                }
                else
                {
                    // The original video is larger,  Use the target dimensions for conversion
                    $original_width = $target_width;
                    $original_height = $target_height;
                }
            }
        }
        if($force_aspect)
        {
            // Use the target_ vars because they contain dimensions relative to the target aspect ratio
            $target['width'] = $target_width;
            $target['height'] = $target_height;
        }
        else
        {
            // Use the original_ vars because they contain dimensions relative to the original's aspect ratio
            $target['width'] = $original_width;
            $target['height'] = $original_height;
        }
        return $target;
    }
    

    Usage

    Here are a few examples of what you will get from get_dimensions() to make things more clear:

    get_dimensions(480,360,640,480,true);
    -returns: Array([width] => 480, [height] => 360)
    
    get_dimensions(480,182,640,480,true);
    -returns: Array([padtop] => 89, [padbottom] => 89, [width] => 480, [height] => 360)
    
    get_dimensions(480,182,640,480,false);
    -returns: Array([width] => 480, [height] => 182)
    
    get_dimensions(640,480,480,182,true);
    -returns: Array([padleft] => 119, [padright] => 119, [width] => 480, [height] => 182)
    
    get_dimensions(720,480,640,480,true);
    -returns: Array([padtop] => 27, [padbottom] => 27, [width] => 640, [height] => 480)
    
    get_dimensions(720,480,640,480,false);
    -returns: Array([width] => 640, [height] => 427)
    

    The Finished Product

    Now, to put it all together:

    $src = '/var/videos/originals/original.mpg';
    $original = get_vid_dim($src);
    if(!empty($original['width']) && !empty($original['height']))
    {
        $target = get_dimensions($original['width'],$original['height'],640,480,true);
        $command = '/usr/bin/ffmpeg -i ' . $src . ' -ab 96k -b 700k -ar 44100 -s ' . $target['width'] . 'x' . $target['height'];
        $command .= (!empty($target['padtop']) ? ' -padtop ' . $target['padtop'] : '');
        $command .= (!empty($target['padbottom']) ? ' -padbottom ' . $target['padbottom'] : '');
        $command .= (!empty($target['padleft']) ? ' -padleft ' . $target['padleft'] : '');
        $command .= (!empty($target['padright']) ? ' -padright ' . $target['padright'] : '');
        $command .= ' -acodec mp3 /var/videos/converted/target.flv 2>&1';
    
        exec($command,$output,$status);
    
        if($status == 0)
        {
            // Success
            echo 'Woohoo!';
        }
        else
        {
            // Error.  $output has the details
            echo '
    ',join('\n',$output),'
    '; } }

提交回复
热议问题