vault backup: 2025-03-04 11:17:00

This commit is contained in:
2025-03-04 11:17:00 +08:00
parent d1e51bfd2f
commit ff12c4f4ca
161 changed files with 1 additions and 2 deletions

View 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

View 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]]

View File

@@ -0,0 +1,7 @@
使用`AV_CODEC_FLAG_GLOBAL_HEADER`的好處主要體現在減少資料大小和提高傳輸效率:
1. **減小資料包大小:**將頭資訊放在全域頭中,而不是在每個資料包中都重複,可以減少每個資料包的大小。這對於網路傳輸和儲存來說是很重要的,因為較小的資料包通常傳輸更有效率。
2. **提高傳輸效率:**全域頭資訊只需發送一次,而不是在每個資料包中重複。這降低了傳輸的開銷,特別是在頻寬受限或傳輸成本敏感的情況下。
3. **最佳化儲存:**如果你將音訊視訊資料進行存儲,使用全域頭可以減少儲存需求,因為頭資訊只需儲存一次,而不是與每個資料包一起儲存。
整體而言,`AV_CODEC_FLAG_GLOBAL_HEADER`的使用可以改善多媒體資料的傳輸效率和儲存效率,但在實際應用中,需要確保使用該標誌的影響不會導致相容性或其他問題。

View 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
- 0Y
- 1U
- 2V
- 3NULL
- For audio
- 0Left channel
- 1Right channel
- 2NULL
- Non plane mode
- For video
- 0RGB RGB RGB
- 1NULL
- `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 audiosampling 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)

View 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 profileHP可以比main profileMP 降低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`

View 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 = &params;
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]]

View File

@@ -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)

View 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)。

View 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>
```