1

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'));
        $scale = 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!

Alix Axel
  • 151,645
  • 95
  • 393
  • 500

3 Answers3

3

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.

alex strange
  • 1,249
  • 9
  • 9
  • That was the first thing I tried, but the quality was crap. According to the FFMPEG documentation: **-b bitrate: Set the video bitrate in bit/s (default = 200 kb/s)**. As for the audio, I am limiting the bitrate to a maximum of 128kb. Also, since I'm kinda new to this, what does "crf 23" stands for? – Alix Axel Apr 04 '11 at 00:31
  • Humm, I think I've found it in http://rob.opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/: Constant Rate Factor. Do you know what it does exactly? Can I use it with other video codecs? And what about the 23, is there a scale? – Alix Axel Apr 04 '11 at 00:35
  • 1
    No, it's an option specific to x264, which also has a different scale than other codecs. The mpeg4 equivalent is '-vqscale 2'. I'm not sure what the right options for vp8 or theora are. – alex strange Apr 04 '11 at 00:37
  • @alex strange: Thank you for the explanation, where do you learn all this stuff from? I find the FFMPEG docs to be confusing, misleading and outdated... – Alix Axel Apr 04 '11 at 00:40
  • 1
    By working on it and reading a lot of source code. I don't recommend this approach, since we're slowly driving towards making knowing this unnecessary. – alex strange Apr 04 '11 at 09:20
0

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.

Micromega
  • 12,486
  • 7
  • 35
  • 72
  • Never heard about Shannon-Entropy before, I'm gonna check the Wikipedia page now. – Alix Axel Mar 31 '11 at 15:58
  • I took a glance at it but I don't understand how I can apply that to my problem with the variables I have available... Could you elaborate a bit more on this please? – Alix Axel Mar 31 '11 at 16:19
  • Basically my idea is to count every symbol in your data and then sum it with Shannon-Entropy formula to get the least bits how your data can be compressed. This is a theoratical limit and it depends on your data and your algorithm. I believe it is only for text compression. But I'm very sure I'm wrong because I answered a similiar question before http://stackoverflow.com/questions/5450489/how-does-winrar-perform-a-compression-ratio-check/5451331#5451331. user shelwien is a professional in compression. – Micromega Mar 31 '11 at 16:25
  • Yeah, that's basically what I got from Wikipedia. However it's highly impractical to make PHP transverse every symbol in a large video and I'm not sure how I would go about doing that. A fuzzier and simpler calculation would be enough for me I guess. – Alix Axel Mar 31 '11 at 16:32
  • Other thing you can do is using a space-filling-curve partitioning the video file and then take a few sample of your file and then calculate the shannon-entropy and let ffmpeg compress the sample too. shannon-entropy is only for accuracy. – Micromega Mar 31 '11 at 16:39
0

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!

Alix Axel
  • 151,645
  • 95
  • 393
  • 500