Files
Obsidian-Main/05. 資料收集/Programming/FFMPEG/Structure.md

201 lines
7.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
- [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]]