diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 9578f9a..650de65 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -41,7 +41,7 @@ "state": { "type": "markdown", "state": { - "file": "01. 個人/00. Informations/Datas.md", + "file": "04. Programming/FFMPEG/Structure.md", "mode": "source", "source": true } @@ -77,7 +77,7 @@ "state": { "type": "search", "state": { - "query": "numpy", + "query": "ffmpeg", "matchingCase": false, "explainSearch": false, "collapseAll": false, @@ -114,7 +114,7 @@ "state": { "type": "backlink", "state": { - "file": "01. 個人/00. Informations/Datas.md", + "file": "04. Programming/FFMPEG/Structure.md", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -139,7 +139,7 @@ "state": { "type": "outline", "state": { - "file": "01. 個人/00. Informations/Datas.md" + "file": "04. Programming/FFMPEG/Structure.md" } } }, @@ -167,7 +167,7 @@ "state": { "type": "file-properties", "state": { - "file": "01. 個人/00. Informations/Datas.md" + "file": "04. Programming/FFMPEG/Structure.md" } } } @@ -212,6 +212,15 @@ }, "active": "d5379d7d99bdf586", "lastOpenFiles": [ + "attachments/Pasted image 20231220095827.png", + "04. Programming/FFMPEG/FFMpeg.md", + "04. Programming/FFMPEG/Structure.md", + "04. Programming/FFMPEG/01. Setup.md", + "04. Programming/FFMPEG/00. Introduction.md", + "05. 資料收集/Tool Setup/Software/Windows 11 Setup.md", + "05. 資料收集/Tool Setup/Software/Windows 10 Setup.md", + "04. Programming/OpenCV.md", + "01. 個人/00. Informations/Datas.md", "01. 個人/01. Daily/2023-12-18(週一).md", "00. Inbox/01. TODO.md", "00. Inbox/numpy axis 運算.md", @@ -232,18 +241,9 @@ "00. Inbox/White noise.md", "06. Archives(歸檔)/想要的鏡頭/變焦/Fujifilm XF 16-80mm F4 R OIS WR.md", "06. Archives(歸檔)/想要的鏡頭/定焦/Fujifilm XF18mm F1.4 R LM WR.md", - "README.md", "未命名 1.canvas", "未命名.canvas", - "01. 個人/01. Daily/2023-12-08(週五).md", - "04. Programming/Machine Learning.md", - "01. 個人/01. Daily/2023-12-05(週二).md", - "01. 個人/01. Daily/2023-12-01(週五).md", - "01. 個人/01. Daily/2023/11/2023-11-01(週三).md", "01. 個人/01. Daily/2023/11", - "01. 個人/01. Daily/2023/未命名.md", - "01. 個人/01. Daily/2023/11/2023-11-30(週四).md", - "01. 個人/01. Daily/2023/11/2023-11-29(週三).md", "01. 個人/01. Daily/2023/10", "00. Inbox/vc-fwUpdate code trace(Meetup).canvas", "01. 個人/01. Daily/2023/09", diff --git a/04. Programming/FFMPEG/Structure.md b/04. Programming/FFMPEG/Structure.md new file mode 100644 index 0000000..6022a99 --- /dev/null +++ b/04. Programming/FFMPEG/Structure.md @@ -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 ; +#include +#include +#include + +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 \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]] \ No newline at end of file diff --git a/attachments/Pasted image 20231220095827.png b/attachments/Pasted image 20231220095827.png new file mode 100644 index 0000000..6cb64ec Binary files /dev/null and b/attachments/Pasted image 20231220095827.png differ