This is my study note of FFMPEG.

Basic structure

1、AVCodeContext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct AVCodecContext
{
int bit_rate; // 码率
int frame_number; // 帧号,指定解码顺序
unsigned char *extradata; // 主要保存SPS、PPS等信息
int extradata_size;
int width, height;
enum PixelFormat pix_fmt; // 每个像素的颜色数据的格式,图像颜色
int sample_rate; // 采样率
int channels; // 通道数
int bits_per_sample; // 采样深度
int block_align; // 数据块对齐单位,一次采样的大小,值为声道数 * 量化位数 / 8,在播放时需要一次处理多个该值大小的字节数据。
struct AVCodec *codec; // 编解码器
void *priv_data; // 指向由协议操控的空间
int(*get_buffer)(struct AVCodecContext *c, AVFrame *pic);
void(*release_buffer)(struct AVCodecContext *c, AVFrame *pic);
int(*reget_buffer)(struct AVCodecContext *c, AVFrame *pic);
}AVCodecContext;

2、AVFilter

Alt text1

  • AVFilter可以看作是一对的avfilter节点链结组成
  • AVFilter Graph可以看作为链的管理者
  • 每个avfilter节点都会对数据进行处理,处理结束后发送到下个结点
  • 第一个节点音视频名称为buffer/abuffer,最后一个节点名称为buffersink/abuffersink
  • 至少包括两个节点
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    avfilter_get_by_name();                          // 创建avfliter
    avfilter_inout_alloc(); // 创建中间结构体AVFilterInOut
    int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
    const char *name, const char *args, void *opaque,
    AVFilterGraph *graph_ctx);
    // 创建一个滤波器实例AVFilterContext,并添加到AVFilterGraph中
    int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
    AVFilterInOut **inputs, AVFilterInOut **outputs,
    void *log_ctx);
    // 将一个字符串描述的 filter graph 加入到一个已经存在的 graph 中
    avfilter_graph_config // 为滤镜图中的所有滤镜建立连接

3、av_bitstream_filter

将文件分为音频和视频,最简单的操作就是将avpacket直接存储,但是面对某些格式文件,例如MP4等,我们还需要对
avpackage进行一部分操作,以MP4分离h264为例,我们需要对每个avpackage添加SPS、PPS等信息,并且将avpackage
首四个字节数据替换为0x0001。
1
2
3
av_bitstream_filter_init();                     // 声明bitstream filter
av_bitstream_filter_filter(); // 过滤数据
av_bitstream_filter_close(); // 关闭bitstream filter
  • 每个AVPacket中的数据(data字段)经过bitstream filter“过滤”一遍

Uncommon parameters

1、AVRational

1
2
3
4
typedef struct AVRational{
int num; // 时间刻度分子
int den; // 时间刻度分母
} AVRational
  • 时间刻度为num/den,以25帧为例,num = 1, den = 25

2、Paramers

  • GOP 指的就是两个I帧之间的间隔
  • max_b_frames设置相邻两个非B帧之间最多出现的B帧数量
  • pts显示时间戳,第n帧的pts = n * ((1 / timbase)/ fps)

3、av_image_alloc

1
int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt, int align);
  • pointers[4]:保存图像通道的地址。如果是RGB,则前三个指针分别指向R,G,B的内存地址。第四个指针保留不⽤
  • linesizes[4]:保存图像每个通道的内存对齐的步长,即一行的对齐内存的宽度,此值大小等于图像宽度

3、av_parser_parse2

1
2
3
4
5
int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx,
uint8_t **poutb
uf, int *poutbuf_size,
const uint8_t *buf, int buf_size,
int64_t pts, int64_t dts, int64_t pos)
  • 拿到AVPaket数据,将一个个AVPaket数据解析组成完整的一帧未解码的压缩数据

4、avpicture_fill

1
2
int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
enum AVPixelFormat pix_fmt, int width, int height)
  • 将ptr的数据给予picture,但是不是进行拷贝工作,而是将picture的指针指向ptr

5、Avframe::data[4]

对于planar模式的YUV:

  • data[0]指向Y分量的开始位置
  • data[1]指向U分量的开始位置
  • data[2]指向V分量的开始位置

对于packed模式YUV:

  • data[0]指向数据的开始位置
  • data[1]和data[2]都为NULL

6、avpicture_get_size

1
av_image_fill_arrays(pictureYUV->data, pictureYUV->linesize, buffer_YUV, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1);
  • 是一个函数,用于计算给定图像的大小(以字节为单位),以便在分配足够的内存来存储该图像。

7、av_find_best_stream

以前没有av_find_best_stream时,寻找stream操作如下:

1
2
3
4
5
6
7
for(int i=0; i< 100; i++)
{
if(pformat->stream[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
vedio_index = i;
if(pformat->stream[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
audio_index = i;
}

8、AVFMT_GLOBALHEADER

打开媒体格式上下文后,如果输出媒体格式有 AVFMT_GLOBALHEADER 这个标记,那么音视频编码器创建的时候也需要设置 AV_CODEC_FLAG_GLOBAL_HEADER 标记。

1
2
3
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
// ofmt_ctx为输出媒体上下文, out_stream->codec为对应流编码器

9、AVFMT_NOFILE

  • 根据该值是否为AVFMT_NOFILE,决定是否通过avio_open打开
    1
    2
    if (!(ofmt_a->flags & AVFMT_NOFILE))
    // 此时根据ofmt_a->flags与AVFMT_NOFILE想与的结果判断是否开启

10、av_rescale_q_rnd

1
int64_t av_rescale_q_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd); 
  • 计算 “a * b / c” 的值并分五种方式来取整
  • a 表式要换算的值,b 表式原来的时间基,c表示要转换的时间基

11、codec_tag

  • 若AVStream->codecpar->codec_tag有值,则会校验AVStream->codecpar->codec_tag是否在封装格式支持的codec_tag列表中,若不在,就会打印错误信息
  • 若AVStream->codecpar->codec_tag为0,则会根据AVCodecID从封装格式的codec_tag列表中,找一个匹配的codec_tag