2011-03-31 118 views
1

我寫了下面的包裝爲FFMPEG: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; 
} 

正如你可以看到我用我輸出的原始輸入音頻和視頻比特率,即使輸入視頻裁剪或調整,這在HD空間方面似乎相當低效。

我對這些事情知之甚少,但根據我的理解,比特率與媒體的持續時間,質量和分辨率直接相關,對嗎?如果是這樣,我如何使用這些值來確定適當的音頻和視頻比特率,以保持輸入質量並減小文件大小?

在此先感謝!

回答

3

一般而言,您不應該指定比特率。它僅適用於流式傳輸,在這種情況下,您還需要尊重VBV(指定隨時間變化的最大比特率以及平均比特率)。

使用x264 crf 23 - 其默認的質量恆定模式 - 並很高興。在ffmpeg的情況下,這是類似的:

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

至於音頻,最好直接複製,如果輸入被壓縮。在某些情況下這是不可能的,比如輸入是vorbis,輸出是.flv文件。在這種情況下,我會堅持選擇音頻編碼器的默認值。

+0

這是我第一次嘗試,但質量是廢話。根據FFMPEG文檔:** - b比特率:以bit/s(默認= 200 kb/s)**設置視頻比特率。至於音頻,我將比特率限制爲最大128kb。另外,因爲我對這個有點新鮮,「crf 23」代表什麼? – 2011-04-04 00:31:40

+0

固定費率因子。 – 2011-04-04 00:34:47

+0

Humm,我想我已經在http:// rob中找到它了。opendot.cl/index.php/useful-stuff/ffmpeg-x264-encoding-guide/:恆定速率因子。你知道它究竟做了什麼嗎?我可以與其他視頻編解碼器一起使用嗎? 23,那麼有一個規模? – 2011-04-04 00:35:47

0

你想尋找Shannon-Entropy -log(P)/ log(2)。這是任何信息可以想到的最小位數。但我不確定它對你有用。

+0

以前從未聽說過香農熵,我現在要去查看維基百科頁面。 – 2011-03-31 15:58:50

+0

我瞥了一眼它,但我不明白我可以如何將這個問題應用於我的問題,我可以使用的變量...請詳細說明一下這個問題嗎? – 2011-03-31 16:19:18

+0

基本上我的想法是統計數據中的每個符號,然後用Shannon-Entropy公式對其進行求和,以獲得數據壓縮的最小位數。這是一個理論上的限制,它取決於你的數據和你的算法。我相信它只是用於文本壓縮。但我確信我錯了,因爲我回答了一個類似的問題,在http://stackoverflow.com/questions/5450489/how-does-winrar-perform-a-compression-ratio-check/5451331#5451331之前。用戶shelwien是一名壓縮專業人士。 – Bytemain 2011-03-31 16:25:52

0

我結束了使用-sameq flag,我讀了一些地方,這不會轉化爲相同的質量,但現在這比強制原始比特率更好。

無論如何,我遇到過this Bash script,這表明我的想法是正確的,我仍然不知道如何計算輸出比特率,而沒有輸出大小作爲約束。如果有人知道,請分享!