FFmpeg command line tool(Android中使用FFmpeg命令列)

BryceLee發表於2019-03-02

關於轉碼:

網路限制了視訊傳輸的頻寬。由於頻寬限制,這就要求我們在傳輸之前通過實時視訊轉碼將視訊資料轉換成頻寬效率更高的格式。轉碼的意義在於可以在視訊質量幾乎不損失的前提下節省大量的網路頻寬。FFmpeg主要就是做轉碼這件事情。

FFmpeg轉碼流程示意圖:

 _______              ______________
|       |            |              |
| input |  demuxer   | encoded data |   decoder
| file  | ---------> | packets      | -----+
|_______|            |______________|      |
                                           v
                                         _________
                                        |         |
                                        | decoded |
                                        | frames  |
                                        |_________|
 ________             ______________       |
|        |           |              |      |
| output | <-------- | encoded data | <----+
| file   |   muxer   | packets      |   encoder
|________|           |______________| 
複製程式碼

基本命令解釋:

  • -y => 覆蓋輸出

  • -i => 輸入源頭,比如一個視訊的絕對路徑:/sdcard/....,輸出我們也是寫絕對路徑

  • -f => 輸出格式,如果省略就取輸出錄製的字尾名為輸出格式,比如輸入mp4格式:-y -i input -f mp4 output

  • -c:v => 針對視訊流的編碼 ,等介於-vcodec

  • -c:a => 針對音訊流的編碼,等介於-acodec

  • -c:v h264 => 指定視訊流h264編碼方式,有時候也寫成libx264,FFmpeg也可以識別

  • -c:a aac => 指定音訊流aac編碼方式

  • copy => 不編碼,如-c:v copy就是對視訊流不編碼,注意:如果加了不必要的編碼操作,會影響執行效率。

  • -an => 禁用音訊

  • -vn => 禁用視訊

  • -x264-params keyint=10 => 為視訊設定關鍵幀,每隔10幀生成一個關鍵幀

  • -crf => 動態位元速率,壓縮效果佳,不是一味追求體積,畫質佳,H.264的CRF數字範圍在0~51,23是預設值。

  • -b:v => 靜態位元速率,壓縮效率比較爆炸,一味控制體積

  • -filter_complex => 濾鏡,簡單濾鏡是-vf,常用的有變速濾鏡,水印濾鏡等

  • -map => 可以理解為流的過濾器:

    • -map[0:v] 就是選擇第一輸入源的視訊流,流向輸出。
    • -map[1:a] 就是選擇第二輸入源的音訊流,流向輸出。
  • -max_muxing_queue_size=>如果視訊比較大,需要指定這個值比較大(比如9999),否則程式執行會中斷。

  • -fps 幀率

  • -vframes 幀數

  • -r幀率

  • -preset

    • preset和crd引數一樣,會影響壓縮效果,preset可以很大程度影響程式的執行速度,每條命令都可以加,一般載入命令尾部,output之前:
    preset 描述
    ultrafast 編碼速度最快 ,壓縮效果最差
    superfast ···
    veryfast ···
    faster ···
    fast ···
    medium – default preset
    slow ···
    slower ···
    veryslow 編碼速度最慢 ,壓縮效果從最好
  • h264編碼相關,涉及CRF,Preset等,Encode/H.264

合併多個視訊

  • 使用concat協議:把視訊轉成ts流,contact合併多個ts生成視訊:

    mp4-->ts:
    remux an MP4 file containing an H.264 stream to mpegts format with ffmpeg, 
    you can use the command:
    
    ffmpeg -i INPUT.mp4 -codec copy -bsf:v h264_mp4toannexb OUTPUT.ts
    
    多個ts合併:
    
    ffmpeg -i "concat:input1.ts|input2.ts|input3.ts" -c copy output
    複製程式碼

    Concat protocol

視訊音訊流變速

  • How to speed up / slow down a video 視訊速度變成原來的兩倍:
    ffmpeg -i input.mkv -filter:v "setpts=0.5*PTS" output.mkv 音訊速度變成原來的兩倍: ffmpeg -i input.mkv -filter:a "atempo=2.0" -vn output.mkv 如果是音訊和視訊一致加速兩杯: 這裡會涉及到濾鏡的概念和-map指令的使用,輸入有多個流,我們就可能用到-map指定: 假設有音訊流和視訊流:
speed是目標速率,比如2.
-y -i input  -filter_complex [0:v]setpts=" + 1d/speed + "*PTS[v];[0:a]atempo=" + speed + "[a] output
複製程式碼

如果只有視訊流:

-y -i input  -vf [0:v]setpts=" + 1d/speed + "*PTS" output 
複製程式碼

裁剪時長

  • -ss 裁剪開始時間,單位秒
  • -t 需要裁剪多長時間,單位秒 從第一秒,裁剪3秒視訊生成新視訊 -y -i input.mp4 -s 1 -t 3 ouput.mp4

生成縮率圖

  • 這裡涉及兩個指令-vframes ,-fps;
    • -vframes是幀數
    • -fps是幀率,(frames per second),時間(秒)fps=vframs -生成某個時刻的縮率圖: ffmpeg -i input.flv -ss 00:00:14.435 -vframes 1 out.png 每秒生成一張圖片,命名為thumb1.png,thumb2.png,thumb3.png.....(輸出是絕對路徑) ffmpeg -i input -vf fps=1 thumb%d.png 每秒生成兩張圖片,命名為thumb001.png,thumb002.png,thumb003.png,-s指令來指定解析度100100..... ffmpeg -i input -s 100X100 -vf fps=2 thumb%3d.png

圖片生成幻燈片(多圖合成視訊)

  • 如何輸出的圖片是規則的,img001png,img002.pmg,img003.pn... 我生成每秒10幀的幻燈片,預設是25幀每秒: ffmpeg -framerate 10 -i img%03d.png output.mp4 注意:如果生成是h264的視訊,加上 -vf format=yuv420p 或者 -pix_fmt yuv420p可以防止某些播放器無法解碼視訊。 生成幻燈片相關
  • 如果檔名是不規則,也可以使用concat協議:
    • 先生成一個text檔案,寫上對應的file和duration(時長):
      duration 5
      file '/path/to/cat.png'
      duration 1
      file '/path/to/rat.png'
      duration 3
      file '/path/to/tapeworm.png'
      duration 2
      file '/path/to/tapeworm.png'
      複製程式碼
    • ffmpeg -f concat -i input.txt -vsync vfr -pix_fmt yuv420p output.mp4

更改解析度

  • ffmpeg -i input -s width*height -c:v h264 output

從視訊中裁剪一塊區域出來

在起點(x,y)的地方裁剪下長高為wh的區域對應的視訊(座標系:以視訊左上角為原點,向右為正方向,向下為正方向) ffmpeg -i -vf crop=w:h:x:y output crop指令

水印

  • 疊加一張靜態圖,overlay是picture左上角對應的座標,(座標系:以視訊左上角為原點,向右為正方向,向下為正方向): ffmpeg -i input -i picture -filter_complex overlay=0:0 output
  • 疊加GIF,如果想要GIF無限迴圈,使用這條指令(否則可以用上面的指令): ffmpeg -i input -ignore_loop 0 -i gif -filter_complex overlay=shortest=1 如果需要加座標: ffmpeg -i input -ignore_loop 0 -i gif -filter_complex overlay=x:y:shortest=1

GIF

從視訊前兩秒片段中抽出一張幀率是10的GIF ffmpeg -i INPUT -ss 0 -t 2 -r 10 out.gif 上門的指令生成的GIF是比較模糊的,如果要生成高清的GIF: 先生成調色盤,再利用調色盤生成GIF,假設fps=10, ffmpeg -i INPUT -r 10 -vf fps=10,scale=300:-1:flags=lanczos,palettegen colorPalette.png scale是縮放的意思,這裡是設定寬為300,-1是保持寬高比. ffmpeg -i input -i colorPalette.png -r 10 -lavfi fps=10,scale=300:-1:flags=lanczos[x];[x][1:v]paletteuse 得到清晰GIF相關

Android 使用:

  • 生成一個Shell命令陣列,丟給FFmpeg可執行檔案處理即可 tips:文件上的命令都是用空格隔開,其實把他理解成陣列就好,重點是陣列內的元素不要錯即可;

      String ffmpegSplitWord = "如果是空格容易出錯,儘量是一個不容易和其他命令出突的字元(儘量和檔名不衝突)";
      StringBuilder sb = new StringBuilder("-y");
      sb.append(ffmpegSplitWord + "-i");
      sb.append(ffmpegSplitWord + input.toString());
      sb.append(ffmpegSplitWord + "-c:v");
      sb.append(ffmpegSplitWord + "libx264");
      sb.append(ffmpegSplitWord + "-x264-params");
      sb.append(ffmpegSplitWord + "keyint=" + Constant.Camerasettings.keyint);
      setBitRate(ffmpegSplitWord, sb);
      setPreset(ffmpegSplitWord, sb);
      sb.append(ffmpegSplitWord + output.toString());
      String ffmpeg_s = sb.toString();
      String[] command=ffmpeg_s.split(ffmpegSplitWord);
      try {
              FFmpeg.getInstance(IApplication.getInstance()).execute(command, new ExecuteBinaryResponseHandler() {
                  @Override
                  public void onFailure(String s) {
                     
                  }
    
                  @Override
                  public void onSuccess(String s) {
             
                  }
    
                  @Override
                  public void onProgress(String s) {
                   
                  }
    
                  @Override
                  public void onStart() {
    
                  }
    
                  @Override
                  public void onFinish() {
                  }
              });
          } catch (FFmpegCommandAlreadyRunningException e) {
          }
    複製程式碼

如何獲取處理進度:

  • FFmpeg在處理的過程會列印出一些資訊,比如 frame= 1 fps=0.0 q=0.0 size= 0kB time=00:01:02.71 bitrate= 0.0kbits/s speed=2.88x 我們可以拿到time資訊,這是處理到哪個時間點的資訊,把他專程秒,處理視訊時長,就可以得到處理進度。

相關文章