avcodec_register_all被av_register_all调用了,主要是注册编解码器。
#define REGISTER_ENCODER(X,x) { \
extern AVCodec ff_##x##_encoder; \
if(CONFIG_##X##_ENCODER) avcodec_register(&ff_##x##_encoder); }
#define REGISTER_DECODER(X,x) { \
extern AVCodec ff_##x##_decoder; \
if(CONFIG_##X##_DECODER) avcodec_register(&ff_##x##_decoder); }
#define REGISTER_ENCDEC(X,x) REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)
#define REGISTER_PARSER(X,x) { \
extern AVCodecParser ff_##x##_parser; \
if(CONFIG_##X##_PARSER) av_register_codec_parser(&ff_##x##_parser); }
void avcodec_register_all(void)
{
。。。
REGISTER_DECODER (H264, h264);
REGISTER_ENCODER (LIBX264, libx264);
REGISTER_PARSER (H264, h264);
。。。
}
用h264举个例子,在ffmpeg中,h264编码用的libx264,解码用的自身的h264解码。在解码之前需要调用h264 parser。h264自身封装了一层NAL, parser是解析NAL的,解析后才是真正的h264编码数据。
parser
AVCodecParser ff_h264_parser = {
.codec_ids = { AV_CODEC_ID_H264 },
.priv_data_size = sizeof(H264ParseContext),
.parser_init = init,
.parser_parse = h264_parse,
.parser_close = h264_close,
.split = h264_split,
};
其中的parser_init函数原型是:
//参数为 AVCodecParserContext
int (*parser_init)(AVCodecParserContext *s);
在 av_parser_init 中被调用,根据 codec_id 来选择AVCodecParser。
AVCodecParserContext *av_parser_init(int codec_id)
{
AVCodecParserContext *s = NULL;
AVCodecParser *parser;
int ret;
if (codec_id == AV_CODEC_ID_NONE)
return NULL;
for (parser = av_first_parser; parser; parser = parser->next) {
if (parser->codec_ids[0] == codec_id ||
parser->codec_ids[1] == codec_id ||
parser->codec_ids[2] == codec_id ||
parser->codec_ids[3] == codec_id ||
parser->codec_ids[4] == codec_id)
goto found;
}
return NULL;
found:
s = av_mallocz(sizeof(AVCodecParserContext));
s->parser = parser;
s->priv_data = av_mallocz(parser->priv_data_size);
//在这里调用了parser_init
if (parser->parser_init) {
ret = parser->parser_init(s);
if (ret != 0)
goto err_out;
}
return s;
....
}
parser_parse的函数原型是:
//输入参数:parser context和codec context,内存:buf+buf_size
//输出参数:内存:*poutbuf + *poutbuf_size
int (*parser_parse)(AVCodecParserContext *s,
AVCodecContext *avctx,
const uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size);
在 av_parser_parse2 中调用,av_parser_parse2的参数比parser_parser的参数多了pts,dts,pos。
int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx,
uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size,
int64_t pts, int64_t dts, int64_t pos)
{
......
/* WARNING: the returned index can be negative */
index = s->parser->parser_parse(s, avctx, (const uint8_t **) poutbuf,
poutbuf_size, buf, buf_size);
......
return index;
}
在libavcodec/parser.c中封装了对于解析器的调用,av_parser_init和av_parser_parse2函数都在这个文件中。下面看一下两个函数的调用。从read_frame_internal入手,read_frame_internal返回的是一个帧的数据,这是一个完整的帧。那获取到一个完整的帧,肯定是需要调用parser的,只有通过parser才能解析出一个完整的帧。
av_parser_init 使用
read_frame_internal 进入 while循环:调用 ff_read_packet 来读取包,这个包只能说是数据包,没有分界线。后根据need_parsing来判断是否进行初始化av_parser_init。
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) {
int ret = 0, i, got_packet = 0;
av_init_packet(pkt);
while (!got_packet && !s->internal->parse_queue) {
AVStream *st;
AVPacket cur_pkt;
/* read next packet */
ret = ff_read_packet(s, &cur_pkt);
st = s->streams[cur_pkt.stream_index];
if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
st->parser = av_parser_init(st->codec->codec_id);
if (!st->parser) {
st->need_parsing = AVSTREAM_PARSE_NONE;
} else if (st->need_parsing == AVSTREAM_PARSE_HEADERS)
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_ONCE)
st->parser->flags |= PARSER_FLAG_ONCE;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW)
st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
}
}
......
}
那need_parsing在哪里配置,在AVInputFormat中进行赋值的。以flv举例:
static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream,
int flv_codecid, int read)
{
AVCodecContext *vcodec = vstream->codec;
switch (flv_codecid) {
case FLV_CODECID_H264:
vcodec->codec_id = AV_CODEC_ID_H264;
vstream->need_parsing = AVSTREAM_PARSE_HEADERS;
return 3; // not 4, reading packet type will consume one byte
}
return 0;
}
当h264时,设置了need_parsing为AVSTREAM_PARSE_HEADERS,代表是需要解析头部。
parse_packet 调用
parser初始化后,该调用parse_packet了。这个里面使用起来很简单。
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
int ret = 0, i, got_packet = 0;
av_init_packet(pkt);
while (!got_packet && !s->internal->parse_queue) {
AVStream *st;
AVPacket cur_pkt;
/* read next packet */
ret = ff_read_packet(s, &cur_pkt);
st = s->streams[cur_pkt.stream_index];
if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
st->parser = av_parser_init(st->codec->codec_id);
if (!st->parser) {
/* no parser available: just output the raw packets */
st->need_parsing = AVSTREAM_PARSE_NONE;
} else if (st->need_parsing == AVSTREAM_PARSE_HEADERS)
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_ONCE)
st->parser->flags |= PARSER_FLAG_ONCE;
else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW)
st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
}
if (!st->need_parsing || !st->parser) {
} else if (st->discard < AVDISCARD_ALL) {
//在这里
if ((ret = parse_packet(s, &cur_pkt, cur_pkt.stream_index)) < 0)
return ret;
} else {
/* free packet */
av_packet_unref(&cur_pkt);
}
......
}
}
有一个h264的问题,一个帧里面包含了为多个slice,而parser是一个个slice来解析的。那parser返回的数据包是一个slice吗?
这个就的分析h264的parser具体逻辑啦。