vault backup: 2025-03-04 11:17:00
This commit is contained in:
17
20.01. Programming/FFMPEG/00. Introduction.md
Normal file
17
20.01. Programming/FFMPEG/00. Introduction.md
Normal file
@@ -0,0 +1,17 @@
|
||||
- [FFmpeg](https://www.ffmpeg.org/)
|
||||
- Open source
|
||||
- For handling multimedia files and streams
|
||||
- GPL/LGPS license
|
||||
- Inspired by MPEG(Moving Picture Experts Group), FF means "Fast Foward"
|
||||
- Libraries
|
||||
- libavcodec: Contain all the encoders and decoders
|
||||
- libavformat: Muxers and demuxers
|
||||
- libavfilter: Filters that you can use on your video and audio according to your requirements
|
||||
- libavdevice: Input and output devices
|
||||
- libavutil
|
||||
- libswscale
|
||||
- libswresample
|
||||
- Tools
|
||||
- ffmpeg: Main transcoding engine
|
||||
- ffplay: Minimum tool to play a video or audio
|
||||
- ffprobe: For inspecting medias and extract valuable information
|
||||
30
20.01. Programming/FFMPEG/01. Setup.md
Normal file
30
20.01. Programming/FFMPEG/01. Setup.md
Normal file
@@ -0,0 +1,30 @@
|
||||
FFmpeg doesn't support any official builds for any operation systems. You can build the codes from source, or use the prebuilt packages.
|
||||
|
||||
## From source
|
||||
Get source code from [snapshot](https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2) or git(`git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg`).
|
||||
|
||||
## From prebuilt packages
|
||||
### MacOS
|
||||
Use homebrew to install FFMPEG package
|
||||
```
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
### Ubuntu
|
||||
```
|
||||
sudo apt update ;\
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
### Windows
|
||||
Goto https://www.ffmpeg.org/download.html, and
|
||||
![[Pasted image 20220607105807.png|640]]
|
||||
|
||||
And then browser jumps to https://www.gyan.dev/ffmpeg/builds/, click [release build](https://www.gyan.dev/ffmpeg/builds/#release-builds), download **release-full**.
|
||||
![[Pasted image 20220607110211.png|640]]
|
||||
|
||||
Unzip the file, then put the folder to anywhere you like, e.g. `C:\libs`, so the full path of ffmpeg packages should looks like: `C:\libs\ffmpeg-5.0.1-full_build`
|
||||
|
||||
#### Add to system variable
|
||||
Add the path of bin of ffmpeg packages to `PATH` system variable.
|
||||
![[Pasted image 20220607110949.png|800]]
|
||||
7
20.01. Programming/FFMPEG/AV_CODEC_FLAG_GLOBAL_HEADER.md
Normal file
7
20.01. Programming/FFMPEG/AV_CODEC_FLAG_GLOBAL_HEADER.md
Normal file
@@ -0,0 +1,7 @@
|
||||
使用`AV_CODEC_FLAG_GLOBAL_HEADER`的好處主要體現在減少資料大小和提高傳輸效率:
|
||||
|
||||
1. **減小資料包大小:**將頭資訊放在全域頭中,而不是在每個資料包中都重複,可以減少每個資料包的大小。這對於網路傳輸和儲存來說是很重要的,因為較小的資料包通常傳輸更有效率。
|
||||
2. **提高傳輸效率:**全域頭資訊只需發送一次,而不是在每個資料包中重複。這降低了傳輸的開銷,特別是在頻寬受限或傳輸成本敏感的情況下。
|
||||
3. **最佳化儲存:**如果你將音訊視訊資料進行存儲,使用全域頭可以減少儲存需求,因為頭資訊只需儲存一次,而不是與每個資料包一起儲存。
|
||||
|
||||
整體而言,`AV_CODEC_FLAG_GLOBAL_HEADER`的使用可以改善多媒體資料的傳輸效率和儲存效率,但在實際應用中,需要確保使用該標誌的影響不會導致相容性或其他問題。
|
||||
303
20.01. Programming/FFMPEG/FFMpeg.md
Normal file
303
20.01. Programming/FFMPEG/FFMpeg.md
Normal file
@@ -0,0 +1,303 @@
|
||||
## Initialization
|
||||
```cpp
|
||||
av_register_all();
|
||||
avformat_network_init();
|
||||
```
|
||||
|
||||
## Command line
|
||||
利用FFMpeg來轉檔
|
||||
```
|
||||
ffmpeg -i <SOURCE.mp4> -t 10 -s <W>x<H> -pix_fmt <OUTPUT_FORMAT> <OUTPUT_FILENAME>
|
||||
```
|
||||
- `-i`:來源檔名
|
||||
- `-t`:影片長度(秒)
|
||||
- `-s`:影片大小,長x寬
|
||||
- `-pix_fmt`:輸出的影像格式
|
||||
|
||||
example:
|
||||
```
|
||||
ffmpeg -i test.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv
|
||||
```
|
||||
|
||||
## Open file
|
||||
使用`avformat_open_input()`來打開檔案。
|
||||
```cpp
|
||||
AVFormatContext* pFormatContext = NULL;
|
||||
char* path = "E:\\download\\Incredible Patagonia_1920x1080.mp4";
|
||||
AVDictionary* pDict = NULL;
|
||||
|
||||
int result = avformat_open_input(&pFormatContext, path, NULL, &pDict);
|
||||
```
|
||||
若是開啟失敗`result`會是一個非0的值。可以用`av_strerror()`來印出詳細的錯誤訊息。例:
|
||||
```cpp
|
||||
char errorMsg[1024] = { 0 };
|
||||
av_strerror(result, errorMsg, sizeof(errorMsg) - 1);
|
||||
printf("Open file %s error, code = %d, message = %s\n", path, result, errorMsg);
|
||||
```
|
||||
|
||||
### Get file information
|
||||
用`avformat_find_stream_info()`來把檔案的video/audio相關訊息放到`AVFormatContext`裡面。例:
|
||||
```cpp
|
||||
result = avformat_find_stream_info(pFormatContext, NULL);
|
||||
```
|
||||
也可以用
|
||||
```cpp
|
||||
av_dump_format(pFormatContext, 0, path, 0);
|
||||
```
|
||||
來直接印出相關訊息,不過這方法是Ffmpeg內定的,如果要印出自己想要的內容,就必須將`AVFormatContext`裡面的`AVStream`一一撈出來:
|
||||
```cpp
|
||||
for (int i = 0; i < pFormatContext->nb_streams; i++) {
|
||||
AVStream* avStream = pFormatContext->streams[i];
|
||||
|
||||
if (avStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
printf("Stream[%d] is a audio stream.\n", i);
|
||||
printf(" Sample rate: %d\n", avStream->codecpar->sample_rate);
|
||||
printf(" Format : %d\n", avStream->codecpar->format);
|
||||
printf(" Channels : %d\n", avStream->codecpar->channels);
|
||||
printf(" FPS : %d/%d\n", avStream->avg_frame_rate.num, avStream->avg_frame_rate.den);
|
||||
printf(" Frame Size : %d\n", avStream->codecpar->frame_size);
|
||||
printf(" Codec ID : %d\n", avStream->codecpar->codec_id);
|
||||
} else if (avStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
printf("Stream[%d] is a video stream.\n", i);
|
||||
printf(" Width : %d\n", avStream->codecpar->width);
|
||||
printf(" Height: %d\n", avStream->codecpar->height);
|
||||
printf(" FPS : %d/%d\n", avStream->avg_frame_rate.num, avStream->avg_frame_rate.den);
|
||||
printf(" Codec ID: %d\n", avStream->codecpar->codec_id);
|
||||
}
|
||||
}
|
||||
```
|
||||
這個for-loop可以找出檔案裡面的video stream index與audio stream index。
|
||||
|
||||
另一個找出video stream index與audio stream index的方法:
|
||||
```cpp
|
||||
int videoStream = av_find_best_stream(pFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
|
||||
int audioStream = av_find_best_stream(pFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
|
||||
printf("Found best video stream index: %d\n", videoStream);
|
||||
printf("Found best audio stream index: %d\n", audioStream);
|
||||
```
|
||||
|
||||
## Demux
|
||||
### 重要結構
|
||||
#### AVPackt
|
||||
##### 重要的member:
|
||||
- `AVBufferRef *buf`:buffer的reference count,基本上不會用到。
|
||||
- `int64_t pts`:顯示順序
|
||||
- `int64_t dts`:decode順序
|
||||
- `uint8_t *data`:實際存data的buffer
|
||||
- `int size`:上面那個buffer的大小
|
||||
|
||||
##### 操作function
|
||||
- `AVPacket* av_packet_alloc()`:產生一個兂的AVPacket
|
||||
- `AVPacket* av_packet_clone(const AVPacket*)`:複製一個AVPacket
|
||||
- `int av_packet_ref(AVPacket*, const AVPacket*)`:增加reference count
|
||||
- `av_packet_unref(AVPacket*)`:減少reference count
|
||||
- `void av_packet_free(AVPacket**)`:釋放AVPacket
|
||||
- `void av_init_packet(AVPacket*)`:初始化AVPacket
|
||||
- `int av_packet_from_data(AVPacket*, uint8_t*, int size)`:從自己的data
|
||||
- `av_seek_frame`:
|
||||
- `AVFormatContext*`
|
||||
- `int stream_index`:要seek的stream
|
||||
- `int64_t timestamp`:timestamp
|
||||
- `int flag`:seek的方法
|
||||
- `#define AVSEEK_FLAG_BACKWARD 1`:往後找
|
||||
- `#define AVSEEK_FLAG_BYTE 2`:以byte為單位,跳到timestamp相對應的byte
|
||||
- `#define AVSEEK_FLAG_ANY 4`:跳到任意frame,不一定要key frame
|
||||
- `#define AVSEEK_FLAG_FRAME 8`:依照frame number來找
|
||||
|
||||
## Decode
|
||||
### 重要結構
|
||||
#### AVCodecContext
|
||||
##### 重要的member
|
||||
- `int thread_count`:執行續的數量
|
||||
- `AVRational time_base`
|
||||
|
||||
##### 操作function
|
||||
- `avcodec_register_all()`:註冊所有的decoder。
|
||||
- `AVCodec* avcodec_find_decoder(enum AVCodecID)`:用codec ID來找到相對應的codec instance
|
||||
- `AVCodec* avcodec_find_decoder_by_name(const char* name)`:用codec的名子來找到相對應的codec instance
|
||||
- `AVCodecContext* avcodec_alloc_context3(const AVCodec*)`:用codec來建立`AVCodecContext`,codec只是用來解碼,解碼之後的相關資料要放在`AVCodecContext`裡面。
|
||||
- `void avcodec_free_context(AVCodecContext**)`:釋放`AVCodecContext`。
|
||||
- `int avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary*)`
|
||||
- 開始解碼
|
||||
- `AVDictionary`的選項請看`libavcodec/options_table.h`
|
||||
|
||||
#### AVFrame
|
||||
##### 重要的member
|
||||
- `uint8_t* data[AV_NUM_DATA_POINTERS]`:存放這個frame的資料
|
||||
- `int linesize[AV_NUM_DATA_POINTERS]`:存放指向各個plane的pointer
|
||||
- Plane mode
|
||||
- For video
|
||||
- 0:Y
|
||||
- 1:U
|
||||
- 2:V
|
||||
- 3:NULL
|
||||
- For audio
|
||||
- 0:Left channel
|
||||
- 1:Right channel
|
||||
- 2:NULL
|
||||
- Non plane mode
|
||||
- For video
|
||||
- 0:RGB RGB RGB
|
||||
- 1:NULL
|
||||
- `int width`:For video, frame width
|
||||
- `int height`:For video, frame height
|
||||
- `int nb_samples`:For audio
|
||||
- `int64_t pts`:這個frame的pts
|
||||
- `int64_t pkt_dts`:Packet的dts
|
||||
- `int sample_rate`:For audio,sampling rate
|
||||
- `uint64_t channel_layout`:For audio
|
||||
- `int channels`:For audio
|
||||
- `int format`:For video it's AVPixelFormat, for audio it's AVSampleFormat
|
||||
|
||||
##### 操作function
|
||||
- `AVFrame* av_frame_alloc()`:
|
||||
- `void av_frame_free(AVFrame*)`:
|
||||
- `AVFrame* av_frame_clone(const AVFrame*)`:
|
||||
- `int av_frame_ref(AVFrame* dst, const AVFrame* src)`:
|
||||
- `void av_frame_unref(AVFrame*)`:
|
||||
- `int avcodec_send_packet(AVCodecContext*, const AVPacket*)`
|
||||
- `int avcodec_receive_frame(AVCodecContext*, AVFrame*)`
|
||||
|
||||
## Frame scaling
|
||||
##### 操作function
|
||||
- `sws_getContext`
|
||||
- `sws_getCachedContext`
|
||||
```
|
||||
SwsContext* sws_getCachedContext(
|
||||
SwsContext*,
|
||||
int srcW, int srcH, enum AVPixelFormat srcFormat,
|
||||
int dstW, int dstH, enum AVPixelFormat dstFormat,
|
||||
int flags,
|
||||
SwsFilter srcFilter,
|
||||
SwsFilter dstFilter,
|
||||
const double* param)
|
||||
```
|
||||
- `sws_scale`
|
||||
```
|
||||
int sws_scale(
|
||||
SwsContext*,
|
||||
const uint8_t* const srcSlice[], // 來源buffer的pointer, 如果是plane mode,要丟srcSlice[3]
|
||||
const int srcStride[], // linesize
|
||||
int srcSliceY,
|
||||
int srcSliceH, // image height
|
||||
uint8_t* const dst[], // 目標buffer的pointer, 如果是plane mode,要丟dst[3]
|
||||
const int dstStride[]) // linesize
|
||||
```
|
||||
- `void sws_freeContext(SwsContext*)`
|
||||
|
||||
## Audio resampling
|
||||
- `SwrContext* swr_alloc()`
|
||||
- `swr_alloc_set_opts`
|
||||
```cpp
|
||||
SwrContext* swr_alloc_set_opts(
|
||||
SwrContext*,
|
||||
int64_t out_ch_layout, // Output
|
||||
AVSampleFormat outSampleFormat, // Output
|
||||
int outSampleRate, // Output
|
||||
int64_t in_ch_layout, // Input
|
||||
AVSampleFormat inSampleFormat, // Input
|
||||
int inSampleRate, // Input
|
||||
int log_offset, // 一般丟0
|
||||
void* log_ctx) // 一般丟0
|
||||
```
|
||||
- `int swr_init(SwrContext*)`
|
||||
- `void swr_free(SwrContext**)`
|
||||
- `swr_convert`
|
||||
```cpp
|
||||
int swt_convert(
|
||||
SwrContext*,
|
||||
uint8_t** outData,
|
||||
int outCount, // Sample count for one channel
|
||||
uint8_t** inData,
|
||||
int inCount) // Sample count for one channel
|
||||
```
|
||||
|
||||
## Audio 播放
|
||||
### 會用到的class
|
||||
#### QAudioFormat
|
||||
- `setSampleRate`
|
||||
- `setSampleSize`
|
||||
- `setChannelCount`
|
||||
- `setCodec("audio/pcm")`
|
||||
- `setByteOrder(QAudioFormat::LittleEndian)`
|
||||
- `setSampleType(QAudioFormat::UnSignedInt)`
|
||||
|
||||
#### QAudioOutput
|
||||
- Constuctor參數就是一個`QAudioFormat`
|
||||
- `QIODevice* start()`
|
||||
- `suspend()`
|
||||
- `resume()`
|
||||
- `bufferSize()`
|
||||
- `byteFree()`
|
||||
- `periodSize()`
|
||||
|
||||
#### QIODevice
|
||||
- `qint64 write(const char* data, qint64 len)`
|
||||
|
||||
#### QBuffer
|
||||
```cpp
|
||||
QBuffer buffer;
|
||||
|
||||
if (buffer.open(QIODevice::ReadWrite)) {
|
||||
qInfo() << "Buffer Opened!";
|
||||
QByteArray data("Hello World");
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
buffer.write(data);
|
||||
buffer.write("\r\n");
|
||||
}
|
||||
//File and device access you may need to flush the data to the device
|
||||
//buffer.flush()
|
||||
|
||||
//Move to the first position
|
||||
buffer.seek(0);
|
||||
|
||||
qInfo() << buffer.readAll(); // retuen a QVariant
|
||||
qInfo() << "closing the buffer";
|
||||
|
||||
//ALWAYS close your device!
|
||||
buffer.close();
|
||||
}
|
||||
```
|
||||
|
||||
### Example
|
||||
```cpp
|
||||
QAudioFormat audioFormat;
|
||||
audioFormat.setSampleRate(44100);
|
||||
audioFormat.setSampleSize(16);
|
||||
audioFormat.setChannelCount(2);
|
||||
audioFormat.setCodec("audio/pcm");
|
||||
audioFormat.setByteOrder(QAudioFormat::LittleEndian);
|
||||
audioFormat.setSampleType(QAudioFormat::UnSignedInt);
|
||||
|
||||
QAudioOutput* audioOutput = new QAudioOutput(audioFormat);
|
||||
QIODevice* ioDevice = audioOutput->start();
|
||||
|
||||
int size = audioOutput->periodSize();
|
||||
char* buffer = new char[size];
|
||||
FILE *pFile = fopen("E:\\download\\out.pcm", "rb");
|
||||
while (!feof(pFile)) {
|
||||
if (audioOutput->bytesFree() < size) {
|
||||
QThread::msleep(1);
|
||||
}
|
||||
|
||||
qint64 len = fread(buffer, 1, size, pFile);
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
ioDevice->write(buffer, len);
|
||||
}
|
||||
fclose(pFile);
|
||||
delete buffer;
|
||||
buffer = NULL;
|
||||
```
|
||||
|
||||
### 使用FFmpeg decode audio
|
||||
- [音视频开发之旅(60) -调试分析FFmpeg (解封装部分的)常用结构体](https://zhuanlan.zhihu.com/p/441055685)
|
||||
|
||||
## 參考資料
|
||||
- [mpv.io](https://mpv.io/)
|
||||
- [《FFmpeg基础知识》](https://opensourcelibs.com/lib/zhangfangtaozft-ffmpeg)
|
||||
- [ffmpeg - 专题 - 简书](https://www.jianshu.com/c/21a6fafc8ee3)
|
||||
- [FFmpeg打开输入文件 · FFmpeg原理](https://ffmpeg.xianwaizhiyin.net/api-ffmpeg/input.html)
|
||||
- [FFplay视频同步分析 - 掘金](https://juejin.cn/post/7156903872789741581)
|
||||
43
20.01. Programming/FFMPEG/Profiles & levels.md
Normal file
43
20.01. Programming/FFMPEG/Profiles & levels.md
Normal file
@@ -0,0 +1,43 @@
|
||||
- [H.264 profiles and levels](https://blog.mediacoderhq.com/h264-profiles-and-levels/)
|
||||
|
||||
H.264 有四種畫質級別,分別是`baseline`, `extended`, `main`, `high`:
|
||||
|
||||
- baseline profile:基本畫質。支援I/P 幀,只支援無交錯(Progressive)和CAVLC
|
||||
- extended profile:進階畫質。支援I/P/B/SP/SI 幀,只支援無交錯(Progressive)和CAVLC;(用的少)
|
||||
- main profile:主流畫質。提供I/P/B 幀,支援無交錯(Progressive)和交錯(Interlaced),也支援CAVLC 和CABAC 的支援
|
||||
- high profile:高階畫質。在main profile 的基礎上增加了8x8 內部預測、自訂量化、無損視訊編碼和更多的YUV 格式
|
||||
|
||||
H.264 baseline profile、extended profile 和 main profile 都是針對`8位`樣本資料、4:2:0 格式(YUV) 的視訊序列。在相同配置情況下,high profile(HP)可以比main profile(MP 降低10% 的碼率)
|
||||
|
||||
- `profile`: 規定了一個演算法特徵和限制的子集,任何遵守某個profile 的解碼器都應該支援與其對應的子集,是對視訊壓縮_特性的描述_(CABAC、顏色採樣數等)
|
||||
- `level`: 規定了一組對標準中語法成員(syntax element)所採用的各種參數值的限制,對_視頻本身特性的描述_(fps (幀率)、碼率、分辨率)
|
||||
> 總的來說就是,profile 越高,表示採用了越高級的壓縮特性;level 越高,表示影片的幀率、碼率、解析度越高
|
||||
|
||||
## profile
|
||||
![[Pasted image 20231225164349.png]]
|
||||
|
||||
- Baseline Profile (BP):主要用於計算資源有限的低成本應用程序,此設定檔廣泛用於視訊會議和行動應用程式。
|
||||
- Main Profile (MP):最初打算作為廣播和儲存應用程式的主流消費者個人資料,當為這些應用程式開發High profile 時,此設定檔的重要性逐漸消失。
|
||||
- Extended Profile (XP):旨在作為串流視訊設定文件,此設定檔具有相對較高的壓縮能力和一些額外的技巧,以提高資料遺失和伺服器串流切換的穩健性。
|
||||
- High Profile (HiP):廣播和光碟儲存應用程式的主要設定文件,特別是高清電視應用程式(例如,HD DVD 和藍光光碟採用的設定檔)。
|
||||
- High 10 Profile (Hi10P): 超越當今的主流消費產品功能,此配置文件建立在High Profile 之上—— 增加了對解碼圖像精度的每個樣本高達10 位的支援。
|
||||
- High 4:2:2 Profile (Hi422P):主要針對使用隔行視頻的專業應用程序,此配置文件建立在High 10 Profile 之上—— 增加了對4:2:2 色度子採樣格式的支持,同時使用多達10 位元每個解碼影像精度的樣本。
|
||||
- High 4:4:4 Predictive Profile (Hi444PP):此設定檔建立在高4:2:2 設定檔之上- 支援高達4:4:4 色度取樣,每個取樣高達14 位,此外還支援高效率無損區域編碼並將每張圖片編碼為三個獨立的顏色平面。
|
||||
|
||||
## Level
|
||||
![[Pasted image 20231225164413.png]]
|
||||
|
||||
level 的計算方法:
|
||||
例如解析度: `1920x1080`, 60fps 的 H.264 碼流是哪個 level 呢?
|
||||
|
||||
H.264 的 marco block 大小為 `16×16` in H.264, 每frame的 marco block 的個數為**解析度除以16**。
|
||||
- `max-fs = ceil( width / 16.0 ) * ceil( height / 16.0 )`
|
||||
計算得:8100
|
||||
|
||||
每秒 marco block 數量為每幀 marco block 數乘以幀率.
|
||||
- max-mbps =max-fs * 參考幀率(60)
|
||||
計算得:486000
|
||||
|
||||
![[Pasted image 20231225164717.png]]
|
||||
|
||||
因此對比表上的數據 `1920x1080@60fps`,level 應該選擇`4.2`
|
||||
201
20.01. Programming/FFMPEG/Structure.md
Normal file
201
20.01. Programming/FFMPEG/Structure.md
Normal file
@@ -0,0 +1,201 @@
|
||||
- [FFmpeg 使用筆記 - 爱吃土豆的男孩 - 博客园](https://www.cnblogs.com/dk666/articles/7327265.html)
|
||||
|
||||
## AVInputFormat/AVOutputFormat
|
||||
|
||||
multimedia 檔案的 Muxer/Demuxer,將多個 stream;如 auodi 、video 等,以單個媒體承載。媒體可以是檔案、 network session 等等。每一個 `AVFormat` 代表一種格式,像是`.avi` 、`.wmv` 、`.dat` 等等各種格式,使用不同的方式 mux/demux 多個 stream。每一 `AVFormat` 都實作特定的格式,使用時必需選擇和目的格式相符的 AVFormat 實作,才能正確的讀取 stream 的內容。
|
||||
|
||||
## AVFormatContext
|
||||
|
||||
`AVFormatContext` 是 `AVFormat` 的一個 instance ,用以存放 `AVFormat` 的執行狀態。使用 FFmpeg 處理檔案時,必需為 `AVInputFormat`/`AVOutputFormat` 建立一個 `AVFormatContext` 。同一個 format 可以建立多個 `AVFormatContext` ,各立獨立,不相互干擾,以同時處理多個檔案。
|
||||
|
||||
## AVStream
|
||||
|
||||
`AVStream` 用以對應到 `AVFormatContext` 裡的一個 stream 。因此同一個 `AVFormatContext` 管理了多個 `AVStream` ,以對應到存在檔案或透過 network session 傳送的數個 stream。
|
||||
|
||||
## AVPacket
|
||||
|
||||
在 stream 裡的資料,是以 packet 為單位進行傳送/儲存。因此,一般多媒體檔,是將檔案當成 network session 一般處理。每一個 `AVPacket` 標注了所屬的 stream ,透過 `AVPacket` 的包裝,多個 stream 可以同時在一個媒介上傳送/儲存。因此,我們可以讀 stream 是由一系列的 packet 所組成。
|
||||
|
||||
## AVCodec
|
||||
|
||||
一個影音媒體檔裡,可以承載多個 stream。而 stream 內有 packet。 packet 的 payload ,也就是資料,是透過 codec 編碼/壓縮的。因此,必需透過 codec 進行讀取/寫入時的 decode/encode。AVCodec 就是實作 codec 演算法。同樣的,codec 的演算法有各式各樣。使用時,必需選正確的 `AVCodec` ,才能正確的 decode/encode。
|
||||
|
||||
## AVCodecContext
|
||||
|
||||
`AVCodecContext` 是 `AVCodec` 的一個 instance ,用以存放 `AVCodec` 的執行狀態。同樣可以建立多個 `AVCodecContext` ,同時處理多個相同格式、不同格式的 stream 。
|
||||
|
||||
## AVFrame
|
||||
|
||||
`AVFrame` 的頭幾個欄位和 `AVPicture` 相同,也就是 `AVPicture` 為 `AVFrame` 的一小部分。
|
||||
|
||||
## 寫入多媒體檔
|
||||
|
||||
步驟
|
||||
- Select an `AVFormat`
|
||||
- allocate a `AVFormatContext`
|
||||
- initialize `AVFormatContext` with selected `AVFormat`
|
||||
- create and initialize an `AVStream` with SVGFormatContext and codec_id specified by `AVFormat`
|
||||
- AVStream::codec is `AVCodecContext`
|
||||
- initialize `AVCodecContext` with the `AVCodec` specified by codec_id
|
||||
- `avcodec_open()`
|
||||
- initialize output stream of `AVFormatContext`
|
||||
- `av_write_header()`
|
||||
- write frames
|
||||
- create an `AVFrame`
|
||||
- initialize an `AVPacket` with `AVFrame`
|
||||
- `av_write_frame()`
|
||||
- close streams
|
||||
- `av_write_trailer()`
|
||||
- free streams
|
||||
- close output stream
|
||||
- free `AVFromatContext`
|
||||
|
||||
## 讀取多媒體檔
|
||||
|
||||
產生檔案時,目的格式是由我們所選擇,因此問題往往較單純一點。但讀取檔案時,我們不確定檔案的格式,必需進行格式判斷後,才確定使用哪一個 AVInputFormat 。
|
||||
- `av_open_input_file()`
|
||||
- Read probe buffer
|
||||
- `get_buffer()`
|
||||
- `av_probe_input_format()` or `av_probe_input_format2()`
|
||||
- return `AVFormat`
|
||||
- Allocate `AVFormatContext`
|
||||
- `av_open_input_stream()`
|
||||
- av_find_stream_info
|
||||
- setup codec for streams
|
||||
- `avcodec_find_decoder()`
|
||||
- `avcodec_open()`
|
||||
- 讀取 frames
|
||||
- `av_read_frame()`
|
||||
- prepare an `AVFrame`
|
||||
- `avcodec_get_frame_defaults()`
|
||||
- decode the frame in pkt to `AVFrame`
|
||||
- `avcodec_decode_video()`
|
||||
- got_picture is true if a picture being returned by the AVFrame.
|
||||
- close codec
|
||||
|
||||
## 範例:影片讀取
|
||||
|
||||
```cpp
|
||||
#include <stdio.h>;
|
||||
#include <string.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
const char *fname;
|
||||
AVFormatContext *ic;
|
||||
AVStream *is, *tis;
|
||||
AVCodec *codec;
|
||||
AVFormatParameters params, *ap = ¶ms;
|
||||
AVPacket pkt;
|
||||
AVFrame frame;
|
||||
AVPicture *pic;
|
||||
int got_picture;
|
||||
int i, r;
|
||||
|
||||
av_register_all();
|
||||
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <media file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fname = argv[1];
|
||||
|
||||
memset(ap, 0, sizeof(AVFormatParameters));
|
||||
ap->video_codec_id = CODEC_ID_NONE;
|
||||
printf("codec id %X\n", ap->video_codec_id);
|
||||
|
||||
r = av_open_input_file(&ic, fname, NULL, 0, ap);
|
||||
if(r != 0 || ic == NULL) {
|
||||
fprintf(stderr, "can not open input file %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
av_find_stream_info(ic);
|
||||
|
||||
is = NULL;
|
||||
for(i = 0; i < ic->nb_streams; i++) {
|
||||
tis = ic->streams[i];
|
||||
if(tis->codec->codec_type == CODEC_TYPE_VIDEO) {
|
||||
printf("channel %d of %d\n", i, ic->nb_streams);
|
||||
is = tis;
|
||||
}
|
||||
}
|
||||
|
||||
codec = avcodec_find_decoder(is->codec->codec_id);
|
||||
if(codec == NULL) {
|
||||
fprintf(stderr, "can not find codec %s\n", is->codec->codec_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = avcodec_open(is->codec, codec);
|
||||
if(r != 0) {
|
||||
fprintf(stderr, "can not initialize a AVCodecContext for codec %s\n",
|
||||
codec->name);
|
||||
return 1;
|
||||
}
|
||||
printf("Codec %s (%d x %d)\n", codec->name, is->codec->width,
|
||||
is->codec->height);
|
||||
|
||||
for(i = 0; i < 10;) {
|
||||
r = av_read_frame(ic, &pkt);
|
||||
if(r != 0) {
|
||||
fprintf(stderr, "no more frame\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(pkt.stream_index != is->index)
|
||||
continue;
|
||||
|
||||
if(pkt.pts != AV_NOPTS_VALUE)
|
||||
printf("Frame %d@%d: pts=%lld, dts=%lld, size=%d, data=%x\n",
|
||||
i, pkt.stream_index, pkt.pts, pkt.dts, pkt.size, pkt.data);
|
||||
else
|
||||
printf("Frame %d@%d: pts=N/A, dts=%lld, size=%d, data=%x\n",
|
||||
i, pkt.stream_index, pkt.dts, pkt.size, pkt.data);
|
||||
av_pkt_dump(stdout, &pkt, 0);
|
||||
|
||||
avcodec_get_frame_defaults(&frame);
|
||||
r = avcodec_decode_video(is->codec, &frame, &got_picture,
|
||||
pkt.data, pkt.size);
|
||||
if(r < 0) {
|
||||
printf("decoding error\n");
|
||||
return 1;
|
||||
}
|
||||
if(got_picture) {
|
||||
printf("\tlinesize[4]={%d %d %d %d}, data[4]={%x %x %x %x)}\n",
|
||||
frame.linesize[0], frame.linesize[1],
|
||||
frame.linesize[2], frame.linesize[3],
|
||||
frame.data[0], frame.data[1],
|
||||
frame.data[2], frame.data[3]);
|
||||
}
|
||||
|
||||
av_free_packet(&pkt);
|
||||
i++;
|
||||
}
|
||||
|
||||
avcodec_close(is->codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
FFMPEG中结构体很多。最关键的结构体可以分成以下几类:
|
||||
|
||||
1. 解协议(http, rtsp, rtmp, mms)
|
||||
`AVIOContext`,`URLProtocol`,`URLContext` 主要存储视音频使用的协议的类型以及状态。`URLProtocol` 存储输入视音频使用的封装格式。每种协议都对应一个 `URLProtocol` 结构。(注意:FFMPEG中文件也被当做一种协议“file”)
|
||||
|
||||
2. 解封装(flv, avi, rmvb, mp4)
|
||||
`AVFormatContext` 主要存储视音频封装格式中包含的信息;`AVInputFormat` 存储输入视音频使用的封装格式。每种视音频封装格式都对应一个 `AVInputFormat` 结构。
|
||||
|
||||
3. 解码(h264, mpeg2, aac, mp3)
|
||||
每个 `AVStream` 存储一个视频/音频流的相关数据;每个 `AVStream` 对应一个 `AVCodecContext`,存储该视频/音频流使用解码方式的相关数据;每个 `AVCodecContext` 中对应一个 `AVCodec`,包含该视频/音频对应的解码器。每种解码器都对应一个 `AVCodec` 结构。
|
||||
|
||||
4. 存数据
|
||||
视频的话,每个结构一般是存一帧;音频可能有好几帧
|
||||
解码前数据:`AVPacket`
|
||||
解码后数据:`AVFrame`
|
||||
他们之间的对应关系如下所示:
|
||||
![[Pasted image 20231220095827.png]]
|
||||
@@ -0,0 +1,7 @@
|
||||
- [Encode/H.264 – FFmpeg](https://trac.ffmpeg.org/wiki/Encode/H.264)
|
||||
|
||||
```cpp
|
||||
av_opt_set(avStream->codecContext->priv_data, "profile", "baseline", 0);
|
||||
```
|
||||
|
||||
[profile 的選項](https://trac.ffmpeg.org/wiki/Encode/H.264#Profile)
|
||||
8
20.01. Programming/FFMPEG/av_opt_set.md
Normal file
8
20.01. Programming/FFMPEG/av_opt_set.md
Normal file
@@ -0,0 +1,8 @@
|
||||
`av_opt_set()` 可以設定很多東西,例如:
|
||||
```cpp
|
||||
av_opt_set(codecContext, "preset", "ultrafaset", 0); // 速度慢,高品質
|
||||
av_opt_set(codecContext, "tune", "fastdecode", 0);
|
||||
av_opt_set(avStream->codecContext->priv_data, "profile", "baseline", 0);
|
||||
```
|
||||
|
||||
詳細請看[Encode/H.264 – FFmpeg](https://trac.ffmpeg.org/wiki/Encode/H.264)。
|
||||
45
20.01. Programming/FFMPEG/ffmpeg command.md
Normal file
45
20.01. Programming/FFMPEG/ffmpeg command.md
Normal file
@@ -0,0 +1,45 @@
|
||||
## 列出 hardware acceleration
|
||||
```bash
|
||||
$ ffmpeg -hide_banner -hwaccels
|
||||
Hardware acceleration methods:
|
||||
cuda
|
||||
dxva2
|
||||
qsv
|
||||
d3d11va
|
||||
opencl
|
||||
vulkan
|
||||
```
|
||||
|
||||
## 列出 codec
|
||||
```bash
|
||||
$ ffmpeg -hide_banner -codecs
|
||||
Codecs:
|
||||
D..... = Decoding supported
|
||||
.E.... = Encoding supported
|
||||
..V... = Video codec
|
||||
..A... = Audio codec
|
||||
..S... = Subtitle codec
|
||||
...I.. = Intra frame-only codec
|
||||
....L. = Lossy compression
|
||||
.....S = Lossless compression
|
||||
-------
|
||||
D.VI.S 012v Uncompressed 4:2:2 10-bit
|
||||
D.V.L. 4xm 4X Movie
|
||||
D.VI.S 8bps QuickTime 8BPS video
|
||||
.EVIL. a64_multi Multicolor charset for Commodore 64 (encoders: a64multi )
|
||||
.EVIL. a64_multi5 Multicolor charset for Commodore 64, extended with 5th color (colram) (encoders: a64multi5 )
|
||||
D.V..S aasc Autodesk RLE
|
||||
```
|
||||
|
||||
### 只列出 encoder 或是 decoder
|
||||
```bash
|
||||
ffmpeg -hide_banner -encoders
|
||||
ffmpeg -hide_banner -decoders
|
||||
```
|
||||
|
||||
### 列出某個 encoder 或 decoder 的詳細資料
|
||||
```bash
|
||||
$ ffmpeg -hide_banner -h encoder=<codec_name>
|
||||
or
|
||||
$ ffmpeg -hide_banner -h decoder=<codec_name>
|
||||
```
|
||||
Reference in New Issue
Block a user