第一章 課程導(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 常用命令
- 視頻錄制命令
- 多媒體文件的分解、復(fù)用命令
- 裁剪與合并命令
- 圖片、視頻互轉(zhuǎn)命令
- 直播相關(guān)命令
- 各種濾鏡命令
-
FFmpeg基本開(kāi)發(fā)
- C語(yǔ)言回顧
- FFmpeg核心概念與常用結(jié)構(gòu)體
- 實(shí)戰(zhàn)-多媒體文件的分解與復(fù)用
- 實(shí)戰(zhàn)-多媒體格式的互轉(zhuǎn)
- 實(shí)戰(zhàn)-從MP4剪一段視頻
- 作業(yè)-實(shí)現(xiàn)一個(gè)簡(jiǎn)單的小咖秀
-
音視頻編解碼實(shí)戰(zhàn)
- 實(shí)戰(zhàn)-H264解碼
- 實(shí)戰(zhàn)-H264編碼
- 實(shí)戰(zhàn)-音頻AAC解碼
- 實(shí)戰(zhàn)-音頻AAC編碼
- 實(shí)戰(zhàn)-視頻轉(zhuǎn)圖片
-
音視頻渲染實(shí)戰(zhàn)
- SDL事件處理
- SDL視頻紋理渲染
- SDL音頻渲染
- 實(shí)戰(zhàn)1-實(shí)現(xiàn)YUV視頻播放器
- 實(shí)戰(zhàn)2-YUV視頻倍速播放
- 實(shí)戰(zhàn)3-實(shí)現(xiàn)PCM播放器
-
FFmpeg開(kāi)發(fā)播放器核心功能
- 實(shí)戰(zhàn)-實(shí)現(xiàn)MP4文件的視頻播放
- 實(shí)戰(zhàn)-實(shí)現(xiàn)MP4文件的音頻播放
- 實(shí)戰(zhàn)-實(shí)現(xiàn)一個(gè)初級(jí)播放器
- 實(shí)戰(zhàn)-音視頻同步
- 實(shí)戰(zhàn)-實(shí)現(xiàn)播放器內(nèi)核
-
Android Q中實(shí)戰(zhàn)FFmpeg
- 編譯Android端可以使用的FFmpeg
- Java與C語(yǔ)言相互調(diào)用
- 實(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)
渲染流程
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
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命令分類
- 基本信息查詢命令
- 錄制命令
- 分解、復(fù)用命令
- 處理原始數(shù)據(jù)命令
- 裁剪與合并命令
- 圖片、視頻互轉(zhuǎn)命令
- 直播相關(guān)命令
- 各種濾鏡命令
2-2 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ù)用
多媒體格式轉(zhuǎn)換
ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv
-i
:輸入文件
-vcodec
:設(shè)置視頻編碼處理方式為copy復(fù)制
-acodec
:設(shè)置音頻編碼處理方式為copy復(fù)制
抽取視頻數(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)存管理
實(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ù)組
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;
}
#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 庫(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
第五章 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
注: 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()