FFmpeg音視頻核心技術(shù)精講與實(shí)戰(zhàn)

常見(jiàn)問(wèn)題

第一章 課程導(dǎo)學(xué)與準(zhǔn)備工作

1-1 課程導(dǎo)學(xué)

1-2 音視頻的應(yīng)用范圍與播放器架構(gòu)講解

未來(lái)+高薪

  • 音視頻具有更加廣闊的未來(lái)
  • 優(yōu)秀音視頻技術(shù)人才奇缺
  • 自學(xué)成本高

課程安排

  • FFmpeg 常用命令

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

    1. C語(yǔ)言回顧
    2. FFmpeg核心概念與常用結(jié)構(gòu)體
    3. 實(shí)戰(zhàn)-多媒體文件的分解與復(fù)用
    4. 實(shí)戰(zhàn)-多媒體格式的互轉(zhuǎn)
    5. 實(shí)戰(zhàn)-從MP4剪一段視頻
    6. 作業(yè)-實(shí)現(xiàn)一個(gè)簡(jiǎn)單的小咖秀
  • 音視頻編解碼實(shí)戰(zhàn)

    1. 實(shí)戰(zhàn)-H264解碼
    2. 實(shí)戰(zhàn)-H264編碼
    3. 實(shí)戰(zhàn)-音頻AAC解碼
    4. 實(shí)戰(zhàn)-音頻AAC編碼
    5. 實(shí)戰(zhàn)-視頻轉(zhuǎn)圖片
  • 音視頻渲染實(shí)戰(zhàn)

    1. SDL事件處理
    2. SDL視頻紋理渲染
    3. SDL音頻渲染
    4. 實(shí)戰(zhàn)1-實(shí)現(xiàn)YUV視頻播放器
    5. 實(shí)戰(zhàn)2-YUV視頻倍速播放
    6. 實(shí)戰(zhàn)3-實(shí)現(xiàn)PCM播放器
  • FFmpeg開(kāi)發(fā)播放器核心功能

    1. 實(shí)戰(zhàn)-實(shí)現(xiàn)MP4文件的視頻播放
    2. 實(shí)戰(zhàn)-實(shí)現(xiàn)MP4文件的音頻播放
    3. 實(shí)戰(zhàn)-實(shí)現(xiàn)一個(gè)初級(jí)播放器
    4. 實(shí)戰(zhàn)-音視頻同步
    5. 實(shí)戰(zhàn)-實(shí)現(xiàn)播放器內(nèi)核
  • Android Q中實(shí)戰(zhàn)FFmpeg

    1. 編譯Android端可以使用的FFmpeg
    2. Java與C語(yǔ)言相互調(diào)用
    3. 實(shí)戰(zhàn)-Android調(diào)用FFmpeg

適合人群

  • 從事音視頻相關(guān)工作,想提高技能的人
  • 想轉(zhuǎn)行到音視頻行業(yè)的人
  • 剛剛畢業(yè)或即將畢業(yè)還沒(méi)有確定未來(lái)方向的人

學(xué)習(xí)建議

  • 牢牢抓住音視頻的處理機(jī)制,了解其本質(zhì)
  • 勤加練習(xí),熟能生巧
  • 帶著問(wèn)題去學(xué)習(xí),事半功倍

1-3 音視頻的應(yīng)用范圍與播放器架構(gòu)講解

音視頻的廣泛應(yīng)用

  • 直播類:音視頻會(huì)議、教育直播、娛樂(lè)/游戲直播等
  • 短視頻類:抖音、快手、小咖秀等
  • 網(wǎng)絡(luò)視頻:優(yōu)酷、騰訊視頻、愛(ài)奇藝等
  • 視頻通話:微信、QQ、Skype等
  • 視頻監(jiān)控
  • 人工智能:人臉識(shí)別,智能音箱等,更關(guān)注算法

播放器架構(gòu)

播放器架構(gòu)

渲染流程

2019_8_16_15_40_52.png

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

FFmpeg從何而來(lái)?

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

法布里斯·貝拉

  • 高中就讀期間開(kāi)發(fā)了著名的可執(zhí)行壓縮程序LzEXE
  • 2000年創(chuàng)建了FFmpeg項(xiàng)目
  • 2011年,他用JavaScript寫(xiě)了一個(gè)Linux虛擬機(jī)(JSLinux)
  • 他還是QEMU,TinyCC的作者

FFmpeg都能做啥?

  • FFmpeg是一個(gè)非常優(yōu)秀的多媒體框架
  • FFmpeg可以運(yùn)行在Linux,Mac,Windows等品臺(tái)上
  • 能夠解碼,編碼,轉(zhuǎn)碼,復(fù)用,解復(fù)用,過(guò)濾音視頻數(shù)據(jù)

恥辱柱

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

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

遇到報(bào)錯(cuò):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

遇到報(bào)錯(cuò):
說(shuō)某個(gè)靜態(tài)庫(kù)未找到,但你確實(shí)可以找到。
解決方法:
/etc/ld.so.conf文件中加入兩行:

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

這樣操作后就正常了

安裝過(guò)程中還安裝了:
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系統(tǒng)特有的庫(kù),可不用裝或者用替代品

1-6 Windows下安裝FFmpeg

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

1-7 FFmpeg 命令大全文檔

第二章 FFmpeg常用命令實(shí)戰(zhàn)

2-1 FFmpeg常用命令

FFmpeg命令分類

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

2-2 FFmpeg處理流程

FFmpeg處理音視頻流程

FFmpeg處理音視頻流程

2-3 FFmpeg基本信息查詢命令實(shí)戰(zhàn)

FFmpeg基本信息查詢命令

-version 顯示版本

分解與復(fù)用命令
-demuxers 顯示可用的demuxers
-muxers 顯示可用的muxers

查詢?cè)O(shè)備
-devices 顯示可用設(shè)備

編解碼相關(guān)
-codecs 顯示所有編解碼器
-decoders 顯示所有的解碼器
-encoders 顯示所有的編碼器

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

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

網(wǎng)絡(luò)協(xié)議
-protocols 顯示可用的協(xié)議

濾鏡
-filters 顯示可用的過(guò)濾器

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

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

2-4 FFmpeg錄制命令實(shí)戰(zhàn)

FFmpeg錄屏命令

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

  • 查詢可用設(shè)備:
    ffmpeg -f avfoundation -list_devices true -i '' 試了沒(méi)用

FFmpeg錄音命令

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

課后答疑任務(wù):同時(shí)錄制視頻與音頻

2-5 FFmpeg分解與復(fù)用命令實(shí)戰(zhàn)

分解與復(fù)用

分解與復(fù)用

多媒體格式轉(zhuǎn)換

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

image.png

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

2-6 ffmpeg 處理原始數(shù)據(jù)命令實(shí)戰(zhàn)

提取YUV數(shù)據(jù)

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

FFmpeg提取PCM數(shù)據(jù)

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

2-7 ffmpeg濾鏡命令實(shí)戰(zhàn)

回顧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代表剪切的起始位置,默認(rèn)為視頻中心點(diǎn))
in_w 視頻本身寬度
in_h 視頻本身高度
-c:v 指定視頻解碼器為libx264(codec video)
-c:a 指定音頻處理方式為copy(codec audio)

2-8 FFmpeg音視頻裁剪與合并命令實(shí)戰(zhàn)

音視頻裁剪

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

音視頻合并

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

2-9 ffmpeg 圖片與視頻互轉(zhuǎn)實(shí)戰(zhàn)

FFmpeg視頻轉(zhuǎn)圖片

ffmpeg -i input.flv -r 1 -f image2 image-%3d.jpeg
-r 指定轉(zhuǎn)換圖片的幀率
-f 指定輸入文件轉(zhuǎn)成的格式
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 圖片轉(zhuǎn)視頻

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

2-10 ffmpeg 直播相關(guān)的命令實(shí)戰(zhàn)

直播推/拉流

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

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

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

第三章 基礎(chǔ)開(kāi)發(fā)

3-1 FFmpeg 基礎(chǔ)開(kāi)發(fā)概述

基礎(chǔ)開(kāi)發(fā)內(nèi)容

  • Vim編輯器
  • C語(yǔ)言回顧,重點(diǎn)介紹指針概念
  • Linux/Mac C語(yǔ)言的編譯與調(diào)試
  • Linux/Mac 常用開(kāi)發(fā)工具介紹

3-2 Vim 模式及創(chuàng)建文件

Vim 處理模式

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

Vim 常用命令

  • 創(chuàng)建文件: vim filename
  • 保存文件: :w
  • 關(guān)閉文件: :q

Vim 拷貝、粘貼與刪除

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

Vim 光標(biāo)移動(dòng)

  • 左下上右 h/j/k/l
  • 調(diào)到文件頭 gg
  • 跳到文件尾 G

Vim 行內(nèi)光標(biāo)移動(dòng)

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

Vim 查找與替換

  • 查找關(guān)鍵字 /關(guān)鍵字
  • 查找與替換: :%s/關(guān)鍵字/替換字/gc
    % 表示查找文檔內(nèi)所有的行,如果需要查找某個(gè)范圍內(nèi)的則用:行號(hào),行號(hào)表示
    s 代表Search
    g 查找所有
    c 每次替換需要確認(rèn)

Vim 多窗口

  • 分窗口: :split/vsplit
  • 窗口之間的跳轉(zhuǎn): Ctrl + ww/w[hjkl]
  • 關(guān)閉窗口::close
  • 最大化窗口:Ctrl + w + |
  • 上下屏窗口調(diào)整: Ctrl + w + _
  • 窗口平均分配:Ctrl + w + =

第四章 C語(yǔ)言基礎(chǔ)

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可執(zhí)行程序

常用基本類型

  • short、int、long(字節(jié):2,4,4)
  • float、double(32bit,32bit,前者精度低,后者精度高)
  • char(單個(gè)字符)
  • void 無(wú)符號(hào)型

4-2 C語(yǔ)言中的常量與變量

變量與常量

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

內(nèi)存管理

內(nèi)存管理

實(shí)踐

#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語(yǔ)言中的指針

指針與數(shù)組

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

指針

指針

數(shù)組

數(shù)組

4-4 C語(yǔ)言中的指針-2(實(shí)踐)

#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;
}
運(yùn)行結(jié)果
#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;
}
運(yùn)行結(jié)果

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

4-5 C語(yǔ)言結(jié)構(gòu)體

結(jié)構(gòu)體

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語(yǔ)言中的if_else

算術(shù)運(yùn)算與比較運(yùn)算

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

if/else 語(yǔ)句

if (a>b){

} else {

}

4-7 C語(yǔ)言中的for_while

for語(yǔ)句

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

while 語(yǔ)句

while(1){

}

4-8 C語(yǔ)言中的函數(shù)

函數(shù)

void func (int a) {

}

實(shí)操:

#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語(yǔ)言中的文件操作

文件操作

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

4-10 再論C語(yǔ)言指針

指針的物理意義

  • 它就是內(nèi)存中的一個(gè)地址
  • 指針本身運(yùn)算
  • 指針?biāo)赶騼?nèi)容的操作

操作系統(tǒng)是如何管理內(nèi)存的?

  • ??臻g,局部變量,如函數(shù)內(nèi)
  • 堆空間,存放程序
  • 內(nèi)存映射,數(shù)據(jù)庫(kù)廣泛應(yīng)用

內(nèi)存的分配與釋放

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

內(nèi)存泄露與野指針

  • 不斷的向系統(tǒng)申請(qǐng)內(nèi)存
  • 申請(qǐng)的內(nèi)存不用,也不釋放
  • 釋放看還使用該指針,占用別人的內(nèi)存稱為野指針

函數(shù)指針

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

實(shí)踐:

#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語(yǔ)言編譯器

GCC/CLANG

  • gcc/clang -g -O 2 -o test test.c -I... -L... -l
    -g 輸出文件中的調(diào)試信息
    -O 對(duì)輸出文件做指令優(yōu)化設(shè)置優(yōu)化級(jí)別,默認(rèn)為1;程序發(fā)行時(shí)使用
    -o 輸出的可執(zhí)行文件名
    -I (大寫(xiě)的i)指定頭文件(位置)
    -L 指定庫(kù)文件位置
    -l (小寫(xiě)的L)指定使用哪個(gè)庫(kù)

編譯過(guò)程

  • 預(yù)編譯,拷貝頭文件
  • 編譯
  • 鏈接,動(dòng)態(tài)鏈接、靜態(tài)鏈接
// add.c
int add (int a, int b) {
  return (a + b);
}

gcc -g -c add.c // 編譯庫(kù)文件
// 生成庫(kù)文件
libtool -static -o libmylib.a add.o // 命令無(wú)效
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

待學(xué)習(xí)解決
在Linux下生成靜態(tài)庫(kù)用ar命令,動(dòng)態(tài)庫(kù)用gcc;
在Mac下用libtool

4-12 C語(yǔ)言調(diào)試器

調(diào)試器原理

  • 編譯輸出帶調(diào)試信息的程序
  • 調(diào)試信息包含:指令地址、對(duì)應(yīng)源代碼及行號(hào)
  • 指令完成后,回調(diào)

Gdb/lldb

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

第五章 ffmpeg 多媒體文件處理

5-1 FFmpeg 初級(jí)開(kāi)發(fā)介紹

初級(jí)開(kāi)發(fā)內(nèi)容

  • FFmpeg 日志的使用及目錄操作
  • 介紹FFmpeg 的基本概念及常用結(jié)構(gòu)體
  • 對(duì)復(fù)用/解復(fù)用及流操作的各種實(shí)戰(zhàn)

FFmpeg代碼結(jié)構(gòu)

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

5-2 FFmpeg 日志系統(tǒng)

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

常用日志級(jí)別

  • AV_LOG_ERROR
  • AV_LOG_WARNING
  • AV_LOG_INFO
  • AV_LOG_DEBUG
    實(shí)戰(zhàn):
#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// 需要事先配置環(huán)境變量
--cflags: 獲取你所要指定庫(kù)的地址目錄
--libs:指定庫(kù)的名字

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操作目錄及l(fā)ist的實(shí)現(xiàn)

操作目錄的重要函數(shù)

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

操作目錄重要結(jié)構(gòu)體

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

【實(shí)戰(zhàn)】簡(jiǎn)單的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
運(yùn)行結(jié)果

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

5-6 ffmpeg 處理流數(shù)據(jù)的基本概念

多媒體文件的基本概念

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

幾個(gè)重要的結(jié)構(gòu)體

  • AVFormatContext
  • AVStream
  • AVPacket

ffmpeg 操作流數(shù)據(jù)的基本步驟

解復(fù)用 --> 獲取流 --> 讀取數(shù)據(jù)包 --> 釋放資源

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

【實(shí)戰(zhàn)】打印音/視頻信息

  • 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;
}

結(jié)果:

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抽取音頻數(shù)據(jù)

【實(shí)戰(zhàn)】抽取音頻數(shù)據(jù)

  • 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

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

5-11~5-13 FFmpeg抽取視頻H264數(shù)據(jù)

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

5-14~5-15 FFmpeg將MP4轉(zhuǎn)成flv

【實(shí)戰(zhàn)】將MP4轉(zhuǎn)成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()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374