FFmpeg音視頻核心技術精講與實戰

常見問題

第一章 課程導學與準備工作

1-1 課程導學

1-2 音視頻的應用范圍與播放器架構講解

未來+高薪

  • 音視頻具有更加廣闊的未來
  • 優秀音視頻技術人才奇缺
  • 自學成本高

課程安排

  • FFmpeg 常用命令

    1. 視頻錄制命令
    2. 多媒體文件的分解、復用命令
    3. 裁剪與合并命令
    4. 圖片、視頻互轉命令
    5. 直播相關命令
    6. 各種濾鏡命令
  • FFmpeg基本開發

    1. C語言回顧
    2. FFmpeg核心概念與常用結構體
    3. 實戰-多媒體文件的分解與復用
    4. 實戰-多媒體格式的互轉
    5. 實戰-從MP4剪一段視頻
    6. 作業-實現一個簡單的小咖秀
  • 音視頻編解碼實戰

    1. 實戰-H264解碼
    2. 實戰-H264編碼
    3. 實戰-音頻AAC解碼
    4. 實戰-音頻AAC編碼
    5. 實戰-視頻轉圖片
  • 音視頻渲染實戰

    1. SDL事件處理
    2. SDL視頻紋理渲染
    3. SDL音頻渲染
    4. 實戰1-實現YUV視頻播放器
    5. 實戰2-YUV視頻倍速播放
    6. 實戰3-實現PCM播放器
  • FFmpeg開發播放器核心功能

    1. 實戰-實現MP4文件的視頻播放
    2. 實戰-實現MP4文件的音頻播放
    3. 實戰-實現一個初級播放器
    4. 實戰-音視頻同步
    5. 實戰-實現播放器內核
  • Android Q中實戰FFmpeg

    1. 編譯Android端可以使用的FFmpeg
    2. Java與C語言相互調用
    3. 實戰-Android調用FFmpeg

適合人群

  • 從事音視頻相關工作,想提高技能的人
  • 想轉行到音視頻行業的人
  • 剛剛畢業或即將畢業還沒有確定未來方向的人

學習建議

  • 牢牢抓住音視頻的處理機制,了解其本質
  • 勤加練習,熟能生巧
  • 帶著問題去學習,事半功倍

1-3 音視頻的應用范圍與播放器架構講解

音視頻的廣泛應用

  • 直播類:音視頻會議、教育直播、娛樂/游戲直播等
  • 短視頻類:抖音、快手、小咖秀等
  • 網絡視頻:優酷、騰訊視頻、愛奇藝等
  • 視頻通話:微信、QQ、Skype等
  • 視頻監控
  • 人工智能:人臉識別,智能音箱等,更關注算法

播放器架構

播放器架構

渲染流程

2019_8_16_15_40_52.png

1-4 什么是FFmpeg,它能做什么?

FFmpeg從何而來?

  • 2000年,由法布里斯·貝拉(FabriceBellard)創建
  • 2004年,邁克爾(Michael Niedermayer)接管
  • 2011年,Libav從FFmpeg分離

法布里斯·貝拉

  • 高中就讀期間開發了著名的可執行壓縮程序LzEXE
  • 2000年創建了FFmpeg項目
  • 2011年,他用JavaScript寫了一個Linux虛擬機(JSLinux)
  • 他還是QEMU,TinyCC的作者

FFmpeg都能做啥?

  • FFmpeg是一個非常優秀的多媒體框架
  • FFmpeg可以運行在Linux,Mac,Windows等品臺上
  • 能夠解碼,編碼,轉碼,復用,解復用,過濾音視頻數據

恥辱柱

  • 偷用了FFmpeg開源代碼卻違背FFmpeg遵守的開源協議
  • GPL的核心思想是基于GPL協議的代碼都必須開源
  • QQ影音、暴風影音、格式工廠等都被釘在了恥辱柱上。

1-5 FFmpeg下載編譯與安裝

FFmpeg下載編譯與安裝

  • git clone https://git.ffmpeg.org/ffmpeg.git
  • config --help
  • ./configure --prefix=/usr/local/ffmpeg --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libx264 --enable-libx265 --enable-filter=delogo --enable-debug --disable-optimizations --enable-libspeex --enable-videotoolbox --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --cc=clang --host-cflags= --host-ldflags=
  • make && make install

遇到報錯:ERROR: videotoolbox requested, but not all dependencies are satisfied: corefoundation coremedia corevideo
解決方法:安裝NVIDIA headers

git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
make
sudo make install

遇到報錯:
說某個靜態庫未找到,但你確實可以找到。
解決方法:
/etc/ld.so.conf文件中加入兩行:

/usr/local/ffmpeg/lib
/usr/local/lib

這樣操作后就正常了

安裝過程中還安裝了:
fdk-aac
speex
nasm
x264
cmake

tar -xzf  [cmake-3.7.0-rc1.tar.gz](https://cmake.org/files/v3.7/cmake-3.7.0-rc1.tar.gz)
make && make install

x265

tar -xzf x265_2.1.tar.gz

$ cd x265/build/linux
$ ./make-Makefiles.bash
$ make && make install

注:安裝中,有些事mac系統特有的庫,可不用裝或者用替代品

1-6 Windows下安裝FFmpeg

https://www.imooc.com/article/247113

1-7 FFmpeg 命令大全文檔

第二章 FFmpeg常用命令實戰

2-1 FFmpeg常用命令

FFmpeg命令分類

  1. 基本信息查詢命令
  2. 錄制命令
  3. 分解、復用命令
  4. 處理原始數據命令
  5. 裁剪與合并命令
  6. 圖片、視頻互轉命令
  7. 直播相關命令
  8. 各種濾鏡命令

2-2 FFmpeg處理流程

FFmpeg處理音視頻流程

FFmpeg處理音視頻流程

2-3 FFmpeg基本信息查詢命令實戰

FFmpeg基本信息查詢命令

-version 顯示版本

分解與復用命令
-demuxers 顯示可用的demuxers
-muxers 顯示可用的muxers

查詢設備
-devices 顯示可用設備

編解碼相關
-codecs 顯示所有編解碼器
-decoders 顯示所有的解碼器
-encoders 顯示所有的編碼器

比特流處理
-bsfs 顯示比特流filter

格式信息
-formats 顯示可用的格式

網絡協議
-protocols 顯示可用的協議

濾鏡
-filters 顯示可用的過濾器

像素格式
-pix_fmts 顯示可用的像素格式

其他
-sample_fmts 顯示可用的采樣格式
-layouts 顯示channel名稱
-colors 顯示識別的顏色名稱

2-4 FFmpeg錄制命令實戰

FFmpeg錄屏命令

  • ffmpeg -f avfoundation -i 1 -r 30 out.yuv
    -f:指定使用 AVfoundation 采集數據
    -i:輸入,指定從哪兒采集數據,1是一個文件索引號
    -r:指定幀率
    實戰:
    ./ffmpeg -f gdigrab -i desktop -r 30 out.yuv windows下錄制屏幕
    ./ffplay -s 3840x1080 -pix_fmt bgra ./out.yuv 播放錄制的文件
    -s 輸入播放尺寸
    -pix_fmt 像素格式,錄制的時候有顯示

  • 查詢可用設備:
    ffmpeg -f avfoundation -list_devices true -i '' 試了沒用

FFmpeg錄音命令

ffmpeg -f avfoundation -i :0 out.wav
:0 代表錄音設備

課后答疑任務:同時錄制視頻與音頻

2-5 FFmpeg分解與復用命令實戰

分解與復用

分解與復用

多媒體格式轉換

ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv
-i:輸入文件
-vcodec:設置視頻編碼處理方式為copy復制
-acodec:設置音頻編碼處理方式為copy復制

image.png

抽取視頻數據:
ffmpeg -i f35.mov -an -vcodec copy out.h264
抽取音頻:
ffmpeg -i f35.mov -acodec copy -vn out.aac

2-6 ffmpeg 處理原始數據命令實戰

提取YUV數據

ffmpeg -i input.mp4 -an -c:v rawvideo -pix_fmts yuv420p out.yuv
-an: 不提取音頻
-c:v: 對視頻進行編碼,使用rawvideo(原始視頻)格式進行編碼
-pix_fmt yuv420p: 輸出的YUV像素格式

FFmpeg提取PCM數據

ffmpeg -i out.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm
-ar: audiu rate音頻采樣率
-ac: audiu channel 音頻聲道數為2
-f:音頻的數據存儲格式s16le: s:有符號16位lettle end

2-7 ffmpeg濾鏡命令實戰

回顧FFmpeg處理音視頻流程

ffmpeg濾鏡

處理流程

ffmpeg濾鏡命令

ffmpeg -i in.mov -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4
-vf 視頻濾鏡(video filter)
crop 濾鏡名,格式:out_w:out_h:x:y(xy代表剪切的起始位置,默認為視頻中心點)
in_w 視頻本身寬度
in_h 視頻本身高度
-c:v 指定視頻解碼器為libx264(codec video)
-c:a 指定音頻處理方式為copy(codec audio)

2-8 FFmpeg音視頻裁剪與合并命令實戰

音視頻裁剪

ffmpeg -i in.mp4 -ss 00:00:00 -t 10 out.ts
-ss 視頻裁剪的起始時間,格式時:分:秒
-t 裁剪的時長s

音視頻合并

ffmpeg -f concat -i inputs.txt out.flv
inputs.txt 需要合并的文件列表
格式: file '文件名'
out.flv: 輸出的文件名

2-9 ffmpeg 圖片與視頻互轉實戰

FFmpeg視頻轉圖片

ffmpeg -i input.flv -r 1 -f image2 image-%3d.jpeg
-r 指定轉換圖片的幀率
-f 指定輸入文件轉成的格式
ffmpeg -f image2 -r 1 -i bmp%d.jpeg -loop 1 -pix_fmt yuv420p -video_size 1280x720 -r 10 -c:v libx264 out.mp4

FFmpeg 圖片轉視頻

ffmpeg -i image-%3d.jpeg output.mp4

2-10 ffmpeg 直播相關的命令實戰

直播推/拉流

  • 直播推流
    ffmpeg -re -i input.mp4 -c copy -f flv rtmp://server/live/streamName
    -re 降低幀率
    -c 對音視頻進行編解碼(-a 音頻 -v 視頻)
    -f 推出的文件格式

  • 直播拉流
    ffmpeg -i rtmp://server/live/streamName -c copy dump.flv

rtmp、rtsp、http視頻協議直播流地址
https://blog.csdn.net/github_30662571/article/details/72466091

第三章 基礎開發

3-1 FFmpeg 基礎開發概述

基礎開發內容

  • Vim編輯器
  • C語言回顧,重點介紹指針概念
  • Linux/Mac C語言的編譯與調試
  • Linux/Mac 常用開發工具介紹

3-2 Vim 模式及創建文件

Vim 處理模式

  • 命令模式
    拷貝、刪除、粘貼等,通過i/a等鍵切換到編輯模式
  • 編輯模式
    編輯字符,通過Esc鍵進行切換

Vim 常用命令

  • 創建文件: vim filename
  • 保存文件: :w
  • 關閉文件: :q

Vim 拷貝、粘貼與刪除

  • 拷貝 yy/yw 拷貝一行或一個詞
  • 粘貼 p
  • 刪除 dd/dw

Vim 光標移動

  • 左下上右 h/j/k/l
  • 調到文件頭 gg
  • 跳到文件尾 G

Vim 行內光標移動

  • 移動到行首 ^
  • 移動到行尾 $
  • 按單詞移動 向前w/ 2w/,向后 b/ 2b

Vim 查找與替換

  • 查找關鍵字 /關鍵字
  • 查找與替換: :%s/關鍵字/替換字/gc
    % 表示查找文檔內所有的行,如果需要查找某個范圍內的則用:行號,行號表示
    s 代表Search
    g 查找所有
    c 每次替換需要確認

Vim 多窗口

  • 分窗口: :split/vsplit
  • 窗口之間的跳轉: Ctrl + ww/w[hjkl]
  • 關閉窗口::close
  • 最大化窗口:Ctrl + w + |
  • 上下屏窗口調整: Ctrl + w + _
  • 窗口平均分配:Ctrl + w + =

第四章 C語言基礎

4-1 Hello World

#include <stdio.h>
int main(int argc, char* argv[])
{
    printf("Hello World!\n");
    return 0;
}

編譯代碼,gcc -g -o helloworld helloworld.c
將helloworld.c編譯為helloworld可執行程序

常用基本類型

  • short、int、long(字節:2,4,4)
  • float、double(32bit,32bit,前者精度低,后者精度高)
  • char(單個字符)
  • void 無符號型

4-2 C語言中的常量與變量

變量與常量

  • int a=0; // 變量,可以再賦值
  • const int len= 256; // 常量定義

內存管理

內存管理

實踐

#include <stdio.h>
int main (int argc, char* argv[]) {
  int a = 0;
  const int b = 10;
  printf("a=%d\n", a);
  printf("b=%d\n", b);

  a = 10;

  printf("a=%d\n", a);

  return 0;
}

4-3 C語言中的指針

指針與數組

  • 指針就是內存地址: void、char
  • 數組(連續的同一類型的空間),如: char c[2]、int arr[10]

指針

指針

數組

數組

4-4 C語言中的指針-2(實踐)

#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
    int *a, *b;
    printf("addr of a:%p\n", &a);
    printf("addr of b:%p"\n, &b);
    return 0;
}
運行結果
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]) {
  int *a, *b;
  a = (int*)malloc(sizeof(int));
  b = (int*)malloc(sizeof(int));

  *a = 1;
  *b = 2;

  int c[3] = {0, 1, 2};
  printf("addr of a: %p, %p, %d\n", &a, a, *a);
  printf("addr of b: %p, %p, %d\n", &b, b, *b);

  printf("addr of c: %p, %d, %d, %d\n", c, c[0], c[1], c[2]);
  return 0;
}
運行結果

C 庫函數 void *malloc(size_t size) 分配所需的內存空間,并返回一個指向它的指針。
sizeof()是獲得類型的內存大小,字節為單位

4-5 C語言結構體

結構體

struct st{
    int a; // 成員a
    int b; // 成員b
};

枚舉類型

#include <stdio.h>

enum e_type {
  red = 0,
  green,
  blue
};

int main (int argc, char* argv[]) {
  enum e_type et;
  et = red;

  printf("the color is %d\n", et);

  return 0;
}

4-6 C語言中的if_else

算術運算與比較運算

  • +、-、*、/、%
  • >、 ==、 <、 >=、 <=、 !=

if/else 語句

if (a>b){

} else {

}

4-7 C語言中的for_while

for語句

for(int i = 0; i<100;i++){
  
}

while 語句

while(1){

}

4-8 C語言中的函數

函數

void func (int a) {

}

實操:

#include <stdio.h>

int sum (int a, int b) {
 return (a + b);
}

void printInfo () {
  printf("help: this is program of calc a+b \n\n");
}

int main (int argc, char* argv[]) {
  printInfo();

  int result;
  result = sum(3, 5);

  printf("3 + 5 = %d\n", result);
  return 0;
}

4-9 C語言中的文件操作

文件操作

  • 文件類型 FILE* file;
  • 打開文件 FILE* fopen(path, mode);
  • 關閉文件 fclose(FILE*);
#include<stdio.h>
int main(int argc, char* argv[])
{
    FILE* file;
    char buf[1024] = {0,};
    file = fopen("1.txt", "a+"); // 追加文件; 沒有則創建
    fwrite("hello, world!", 1, 13, file); // 寫入的內容;每個元素的大小;寫入多少個元素
    rewind(file);//將游標放到文件的開頭
    fread(buf, 1, 10, file);
    fclose(file);
    printf("buf: %s \n", buf);
    return 0;
}

4-10 再論C語言指針

指針的物理意義

  • 它就是內存中的一個地址
  • 指針本身運算
  • 指針所指向內容的操作

操作系統是如何管理內存的?

  • 棧空間,局部變量,如函數內
  • 堆空間,存放程序
  • 內存映射,數據庫廣泛應用

內存的分配與釋放

  • 分配內存 void* mem = malloc(size);
  • 釋放內存 free( mem );

內存泄露與野指針

  • 不斷的向系統申請內存
  • 申請的內存不用,也不釋放
  • 釋放看還使用該指針,占用別人的內存稱為野指針

函數指針

  • 返回值類型 (*指針變量名)([形參列表]);
int func(int x); /* 聲明一個函數 */
int (*f)(int x); /* 聲明一個函數指針 */
f = func; /* 將func 函數的首地址賦給指針f */

實踐:

#include <stdio.h>

int sum (int a, int b) {
 return (a + b);
}

void printInfo () {
  printf("help: this is program of calc a+b \n\n");
}

int main (int argc, char* argv[]) {
  printInfo();

  int result;
  int (*f)(int, int);

  f = sum;
  result = f(3, 5);

  printf("3 + 5 = %d\n", result);
  return 0;
}

4-11 C語言編譯器

GCC/CLANG

  • gcc/clang -g -O 2 -o test test.c -I... -L... -l
    -g 輸出文件中的調試信息
    -O 對輸出文件做指令優化設置優化級別,默認為1;程序發行時使用
    -o 輸出的可執行文件名
    -I (大寫的i)指定頭文件(位置)
    -L 指定庫文件位置
    -l (小寫的L)指定使用哪個庫

編譯過程

  • 預編譯,拷貝頭文件
  • 編譯
  • 鏈接,動態鏈接、靜態鏈接
// add.c
int add (int a, int b) {
  return (a + b);
}

gcc -g -c add.c // 編譯庫文件
// 生成庫文件
libtool -static -o libmylib.a add.o // 命令無效
ar rcs libtestlib.a testlib.o // 在Linux下的命令

// add.h
int add(int a, int b);
// testLib.c
#include <stdio.h>

#include "add.h"

int main(int argc, char* argv[]) {
  printf("add=$d\n", add(3, 3));
  return 0;
}

gcc -g -o testLib add.c -I . -L . -l mylib
方法二:先編譯再鏈接
gcc -g -c testLib.c
gcc -o testLib testLib.o -L . -l mylib

待學習解決
在Linux下生成靜態庫用ar命令,動態庫用gcc;
在Mac下用libtool

4-12 C語言調試器

調試器原理

  • 編譯輸出帶調試信息的程序
  • 調試信息包含:指令地址、對應源代碼及行號
  • 指令完成后,回調

Gdb/lldb

命令 gdb(Linux) lldb(mac)
設置斷點 b b
運行程序 r r
單步執行 n n
跳入函數 s s
跳出函數 finish finish
打印內容 p p
continue c c
退出 q -
gdb testLib
b main
info breakpoints // lldb: break list
實操截圖

第五章 ffmpeg 多媒體文件處理

5-1 FFmpeg 初級開發介紹

初級開發內容

  • FFmpeg 日志的使用及目錄操作
  • 介紹FFmpeg 的基本概念及常用結構體
  • 對復用/解復用及流操作的各種實戰

FFmpeg代碼結構

模塊 介紹
libavcodec 提供了一系列編碼器的實現
libavformat 實現在流協議,容器格式及其本IO訪問
libavutil 包括了hash器,解碼器和各種工具函數
libavfilter 提供了各種音視頻過濾器
libavdevice 提供了訪問捕獲設備和回放設備的接口
libswresample 實現了混音和重采樣
libswscale 實現了色彩轉換和縮放功能

5-2 FFmpeg 日志系統

  • include <libavutil/log.h> 第一步,引入頭文件
  • av_log_set_level(AV_LOG_DEBUG) 第二步,設置日志級別
  • av_log(NULL, AV_LOG_INFO, " ...%s\n", op) 第三步,打印日志,第二個參數,日志級別,第三個參數日志模板字符串,

常用日志級別

  • AV_LOG_ERROR
  • AV_LOG_WARNING
  • AV_LOG_INFO
  • AV_LOG_DEBUG
    實戰:
#include <stdio.h>

#include <libavutil/log.h>

int main(int argc, char* argv[]) {
  av_log_set_level(AV_LOG_DEBUG);
  av_log(NULL, AV_LOG_DEBUG, "Hello world!%d\n", 2020);

  return 0;
}

gcc -g -o ff_log ff_log.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil
或:
gcc -g -o ff_log ff_log.c `pkg-config --cflags --libs// 需要事先配置環境變量
--cflags: 獲取你所要指定庫的地址目錄
--libs:指定庫的名字

5-3 FFmpeg文件的刪除與重命名

FFmpeg文件與目錄操作

文件的刪除與重命名

  • avpriv_io_delete()
  • avpriv_io_move()
#include <libavutil/log.h>
#include <libavformat/avformat.h>

int main(int argc, char* argv[]) {
  int res;
  char* fileName = "./mytestfile.txt";

  res = avpriv_io_move("test1.txt", "test2.txt");
  if (res < 0) {
    av_log(NULL, AV_LOG_ERROR, "Failed to rename\n");
    return -1;
  }
  av_log(NULL, AV_LOG_INFO, "Success to rename\n");
  // url
  res = avpriv_io_delete(fileName);
  if (res < 0) {
    av_log(NULL, AV_LOG_ERROR, "Failed to delete file %s\n", fileName);
    return -1;
  }
  av_log(NULL, AV_LOG_INFO, "Success to delete %s\n", fileName);
  return 0;
}

gcc -g -o ff_file ff_file.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil -l avformat

5-4~5-5 FFmpeg操作目錄及list的實現

操作目錄的重要函數

  • avio_open_dir(context, path, )
  • avio_read_dir()
  • avio_close_dir()

操作目錄重要結構體

  • AVIODirContext
    操作目錄的上下文
  • AVIODirEntry
    目錄項。用于存放文件名、文件大小等信息

【實戰】簡單的ls命令

#include <libavutil/log.h>
#include <libavformat/avformat.h>

int main (int argc, char* argv[]) {
  int res;

  AVIODirContext *ctx = NULL;
  AVIODirEntry *entry = NULL;
  av_log_set_level(AV_LOG_INFO);

  res = avio_open_dir(&ctx, "./", NULL);
  if (res < 0) {
    av_log(NULL, AV_LOG_ERROR, "Can't open dir:%s\n", av_err2str(res));
    goto __fail;
  }

  while (1) {
    res = avio_read_dir(ctx, &entry);
    if (res < 0) {
      av_log(NULL, AV_LOG_ERROR, "Can't read dir: %s\n", av_err2str(res));
      goto __fail;
    }
    if (!entry) {
      break;
    }

    av_log(NULL, AV_LOG_INFO, "%12"PRId64" %s \n", entry -> size, entry -> name);
    avio_free_directory_entry(&entry);
  }
__fail:
  avio_close_dir(&ctx);

  return 0;
}
gcc -g -o ff_dir_list ff_file_list.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil -l avformat
./ff_dir_list
運行結果

注: PRld:是一種跨平臺的書寫方式,主要是為了通知支持32位和64位操作系統。PRld64表示64位整數,在32位系統中表示long long int,在64位系統中表示long int。相當于:
printf("%" "ld" "\n", value); // 64 bit OS
printf("%" "lld" "\n", value); // 64 bit OS

5-6 ffmpeg 處理流數據的基本概念

多媒體文件的基本概念

  • 多媒體文件其實是個容器
  • 在容器里有很多流(Stream/Track)
  • 每種流是由不同的編碼器編碼的
  • 從流中讀出的數據稱為包
  • 在一個包中包含著一個或多個幀

幾個重要的結構體

  • AVFormatContext
  • AVStream
  • AVPacket

ffmpeg 操作流數據的基本步驟

解復用 --> 獲取流 --> 讀取數據包 --> 釋放資源

5-7 ffmpeg 打印音視頻Meta信息

【實戰】打印音/視頻信息

  • av_register_all()
  • avformat_open_input()/avformat_close_input()
  • av_dump_format()
#include <libavutil/log.h>
#include <libavformat/avformat.h>

int main (int argc, char* argv[]) {
  av_log_set_level(AV_LOG_INFO);

  av_register_all();

  int res;
  AVFormatContext *fmt_ctx = NULL;
  res = avformat_open_input(&fmt_ctx, "./test.mp4", NULL, NULL);
  if (res < 0) {
    av_log(NULL, AV_LOG_ERROR, "Can't open file: %s\n", av_err2str(res));
    return -1;
  }
  av_dump_format(fmt_ctx, 0, "./test.mp4", 0);
  avformat_close_input(&fmt_ctx);

  return 0;
}

結果:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from './test.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.9.100
  Duration: 00:05:26.13, bitrate: N/A
    Stream #0:0(und): Video: h264 (avc1 / 0x31637661), none, 1920x1080, 48 kb/s, 14.90 fps, 15360 tbn (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, 2 channels, 128 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

5-8 FFmpeg抽取音頻數據

【實戰】抽取音頻數據

  • av_init_packet()
  • av_find_best_stream()
  • av_read_frame() / av_packet_unref()
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avformat.h>

int main (int argc, char* argv[]) {
  av_log_set_level(AV_LOG_INFO);

  av_register_all();
  AVPacket pkt;
  int audio_index;

  // 1. read two params from console
  char* src = NULL;
  char* dst = NULL;
  if (argc < 3) {
    av_log(NULL, AV_LOG_ERROR, "the count of params should be more than 3!\n");
    return -1;
  }
  src = argv[1];
  dst = argv[2];
  if (!src || !dst) {
    av_log(NULL, AV_LOG_ERROR, "src or dst is null!\n");
    return 1;
  }
  int res;
  AVFormatContext *fmt_ctx = NULL;
  res = avformat_open_input(&fmt_ctx, src, NULL, NULL);
  if (res < 0) {
    av_log(NULL, AV_LOG_ERROR, "Can't open file: %s\n", av_err2str(res));
    avformat_close_input(&fmt_ctx);
    return -1;
  }

  FILE* dst_fd = fopen(dst, "wb");
  if (!dst_fd) {
    av_log(NULL, AV_LOG_ERROR, "Can't open out file!\n");
    return -1;
  }
  av_dump_format(fmt_ctx, 0, src, 0);
  // 2. get stream
  res = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
  if (res < 0) {
    av_log(NULL, AV_LOG_ERROR, "Can't find the best stream!\n");
    avformat_close_input(&fmt_ctx);
    fclose(dst_fd);
    return -1;
  }
  audio_index = res;
  av_init_packet(&pkt);
  while(av_read_frame(fmt_ctx, &pkt) >= 0) {
    if (pkt.stream_index == audio_index) {
      int len = fwrite(pkt.data, 1, pkt.size, dst_fd);
      if (len != pkt.size){
        av_log(NULL, AV_LOG_WARNING, "warning, length of data is not equal size of pkt!\n");
      }
    }

    av_packet_unref(&pkt);
  }
  // 3. write audio data to aac file

  avformat_close_input(&fmt_ctx);
  if (dst_fd) {
    fclose(dst_fd);
  }

  return 0;
}

gcc -g -o ExtraAudio extraAudio.c -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -l avutil -l avformat -l avcodec

注:抽取出來的aac文件需要加adts頭才能正常播放

5-11~5-13 FFmpeg抽取視頻H264數據

  • Start code
  • SPS、PPS
  • codec -> extradata

5-14~5-15 FFmpeg將MP4轉成flv

【實戰】將MP4轉成flv

  • avformat_alloc_output_context2()/
    avformat_free_context()
  • avformat_new_stream()
  • avcodec_parameters_copy()
  • avformat_write_header()
  • av_write_frame()
    av_interleaved_write_frame()
  • av_write_trailer()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372