开发者

FFMPEG Bitrate Calculation / Optimization

I wrote the following wrapper for FFMPEG:

function Video($input, $crop = null, $scale = null, $output = null, $extra = null)
{
    $input = @new ffmpeg_movie($input);

    if ((is_object($input) === true) && ($input->hasVideo() === true))
    {
        $size = array($input->getFrameWidth(), $input->getFrameHeight());
        $crop = array_values(array_filter(explode('/', $crop), 'is_numeric'));
        $开发者_如何学Goscale = array_values(array_filter(explode('*', $scale), 'is_numeric'));

        if ((is_callable('shell_exec') === true) && (is_executable($ffmpeg = trim(shell_exec('which ffmpeg'))) === true))
        {
            if (count($crop) == 2)
            {
                $crop = array($size[0] / $size[1], $crop[0] / $crop[1]);

                if ($crop[0] > $crop[1])
                {
                    $size[0] = round($size[1] * $crop[1]);
                }

                else if ($crop[0] < $crop[1])
                {
                    $size[1] = round($size[0] / $crop[1]);
                }

                $crop = array($input->getFrameWidth() - $size[0], $input->getFrameHeight() - $size[1]);
            }

            else
            {
                $crop = array(0, 0);
            }

            if (count($scale) >= 1)
            {
                if (empty($scale[0]) === true)
                {
                    $scale[0] = round($scale[1] * $size[0] / $size[1] / 2) * 2;
                }

                else if (empty($scale[1]) === true)
                {
                    $scale[1] = round($scale[0] * $size[1] / $size[0] / 2) * 2;
                }
            }

            else
            {
                $scale = array(round($size[0] / 2) * 2, round($size[1] / 2) * 2);
            }

            $result = array();

            if (array_product($scale) > 0)
            {
                $result[] = sprintf('%s -i %s', escapeshellcmd($ffmpeg), escapeshellarg($input->getFileName()));

                if (array_sum($crop) > 0)
                {
                    if (stripos(shell_exec(escapeshellcmd($ffmpeg) . ' -h | grep crop'), 'removed') !== false)
                    {
                        $result[] = sprintf('-vf "crop=in_w-2*%u:in_h-2*%u"', round($crop[0] / 4) * 2, round($crop[1] / 4) * 2);
                    }

                    else if ($crop[0] > 0)
                    {
                        $result[] = sprintf('-cropleft %u -cropright %u', round($crop[0] / 4) * 2, round($crop[0] / 4) * 2);
                    }

                    else if ($crop[1] > 0)
                    {
                        $result[] = sprintf('-croptop %u -cropbottom %u', round($crop[1] / 4) * 2, round($crop[1] / 4) * 2);
                    }
                }

                if ($input->hasAudio() === true)
                {
                    $result[] = sprintf('-ab %u -ar %u', $input->getAudioBitRate(), $input->getAudioSampleRate());
                }

                $result[] = sprintf('-b %u -r %u -s %s', $input->getBitRate(), min(25, $input->getFrameRate()), implode('x', $scale));

                if (strlen($format = strtolower(ltrim(strrchr($output, '.'), '.'))) > 0)
                {
                    $result[] = sprintf('-f %s %s -y %s', $format, escapeshellcmd($extra), escapeshellarg($output . '.ffmpeg'));

                    if ((strncmp('flv', $format, 3) === 0) && (is_executable($flvtool2 = trim(shell_exec('which flvtool2'))) === true))
                    {
                        $result[] = sprintf('&& %s -U %s %s', escapeshellcmd($flvtool2), escapeshellarg($output . '.ffmpeg'), escapeshellarg($output . '.ffmpeg'));
                    }

                    $result[] = sprintf('&& mv -u %s %s', escapeshellarg($output . '.ffmpeg'), escapeshellarg($output));

                    if ((is_writable(dirname($output)) === true) && (is_resource($stream = popen('(' . implode(' ', $result) . ') 2>&1 &', 'r')) === true))
                    {
                        while (($buffer = fgets($stream)) !== false)
                        {
                            if (strpos($buffer, 'to stop encoding') !== false)
                            {
                                return (is_int(pclose($stream)) === true) ? true : false;
                            }
                        }

                        if (is_file($output . '.ffmpeg') === true)
                        {
                            unlink($output . '.ffmpeg');
                        }

                        pclose($stream);
                    }
                }
            }
        }
    }

    return false;
}

As you can see I am using the original input audio and video bitrate in my output, even if the input video is cropped or resized, which seems pretty inefficient in terms of HD space.

I know very little about these matters but from my understanding bitrates are directly connected to the duration, quality and resolution of the media, right? If so, how can I use those values to determine an appropriate audio and video bitrate to maintain input quality and reduce the file size?

Thanks in advance!


In general you shouldn't specify a bitrate at all. It's only useful for streaming, in which case you need to respect VBV as well (which specifies a maximum bitrate over time, as well as the average bitrate).

Use x264 crf 23 - its default constant-quality mode- and be happy. In the case of ffmpeg, this is something like:

ffmpeg -i <file> -vcodec libx264 -vpre slower -acodec copy <outfile>

As for audio, it's best directly copied if the input was compressed. This is not possible in some situations, such as if the input was vorbis and the output is a .flv file. In that case I would stick to whatever the default of the audio encoder selected is.


You want to look for the Shannon-Entropy -log(P)/log(2). This is the minimum bits any information can be think of. But I'm unsure if it is useful to you.


I ended up using the -sameq flag, I read somewhere that this doesn't translate to the same quality but for now this is better than forcing the original bit rate.

Anyway, I've come across this Bash script that suggests that my thinking is right, I still don't know how to calculate the output bit rate without having the output size as a constrain. If anyone knows, please share!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜