原文地址:https://www.cnblogs.com/vczf/p/14837759.html
解析器主要就是解析出NALU,以及解析一些SPS、PPS等信息,下面分析一下ff_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,
};
init()
static av_cold int init(AVCodecParserContext *s)
{
H264ParseContext *p = s->priv_data;
p->reference_dts = AV_NOPTS_VALUE;
p->last_frame_num = INT_MAX;
ff_h264dsp_init(&p->h264dsp, 8, 1);
return 0;
}
初始化比较简单,就是一些基本的参数设置
h264_close()
static void h264_close(AVCodecParserContext *s)
{
H264ParseContext *p = s->priv_data;
ParseContext *pc = &p->pc;
av_freep(&pc->buffer);
ff_h264_sei_uninit(&p->sei);
ff_h264_ps_uninit(&p->ps);
}
结束这里就是释放AVCodecParserContext对象
h264_parse()
static int h264_parse(AVCodecParserContext *s,
AVCodecContext *avctx,
const uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size)
{
H264ParseContext *p = s->priv_data;
ParseContext *pc = &p->pc;
int next;
// 获取extradata,一般保存SPS、PPS等信息
if (!p->got_first) {
p->got_first = 1;
if (avctx->extradata_size) {
ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size,
&p->ps, &p->is_avc, &p->nal_length_size,
avctx->err_recognition, avctx);
}
}
if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
next = buf_size;
} else {
// 查找帧结尾位置,以起始码(0x000001或0x00000001)为依据
// SPS和PPS会被一起找到,第一个数据尾:SPS+PPS
next = h264_find_frame_end(p, buf, buf_size, avctx);
// next < 0 : 非一帧,数据缓存到pc->buffer
// next >=0 : 找到一帧,将数据和之前缓存的数据打包一帧到buf
if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
*poutbuf = NULL;
*poutbuf_size = 0;
return buf_size;
}
if (next < 0 && next != END_NOT_FOUND) {
av_assert1(pc->last_index + next >= 0);
h264_find_frame_end(p, &pc->buffer[pc->last_index + next], -next, avctx); // update state
}
}
// 解析NALU,从SPS和PPS等中获取一些信息
parse_nal_units(s, avctx, buf, buf_size);
if (avctx->framerate.num)
avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
if (p->sei.picture_timing.cpb_removal_delay >= 0) {
s->dts_sync_point = p->sei.buffering_period.present;
s->dts_ref_dts_delta = p->sei.picture_timing.cpb_removal_delay;
s->pts_dts_delta = p->sei.picture_timing.dpb_output_delay;
} else {
s->dts_sync_point = INT_MIN;
s->dts_ref_dts_delta = INT_MIN;
s->pts_dts_delta = INT_MIN;
}
if (s->flags & PARSER_FLAG_ONCE) {
s->flags &= PARSER_FLAG_COMPLETE_FRAMES;
}
if (s->dts_sync_point >= 0) {
int64_t den = avctx->time_base.den * (int64_t)avctx->pkt_timebase.num;
if (den > 0) {
int64_t num = avctx->time_base.num * (int64_t)avctx->pkt_timebase.den;
if (s->dts != AV_NOPTS_VALUE) {
// got DTS from the stream, update reference timestamp
p->reference_dts = s->dts - av_rescale(s->dts_ref_dts_delta, num, den);
} else if (p->reference_dts != AV_NOPTS_VALUE) {
// compute DTS based on reference timestamp
s->dts = p->reference_dts + av_rescale(s->dts_ref_dts_delta, num, den);
}
if (p->reference_dts != AV_NOPTS_VALUE && s->pts == AV_NOPTS_VALUE)
s->pts = s->dts + av_rescale(s->pts_dts_delta, num, den);
if (s->dts_sync_point > 0)
p->reference_dts = s->dts; // new reference
}
}
// 将完整的一帧输出到poutbuf
*poutbuf = buf;
*poutbuf_size = buf_size;
return next;
}
这里主要做了几件事:
- 解析extradata
- 查找一帧结束位置
- 组装一帧NALU
- 解析NALU,获取一些SPS、PPS等信息
- 输出buf
ff_h264_decode_extradata()
int ff_h264_decode_extradata(const uint8_t *data, int size, H264ParamSets *ps,
int *is_avc, int *nal_length_size,
int err_recognition, void *logctx)
{
int ret;
if (!data || size <= 0)
return -1;
if (data[0] == 1) {
int i, cnt, nalsize;
const uint8_t *p = data;
*is_avc = 1;
if (size < 7) {
av_log(logctx, AV_LOG_ERROR, "avcC %d too short\n", size);
return AVERROR_INVALIDDATA;
}
// Decode sps from avcC
// 解码SPS
cnt = *(p + 5) & 0x1f; // Number of sps
p += 6;
for (i = 0; i < cnt; i++) {
nalsize = AV_RB16(p) + 2;
if (nalsize > size - (p - data))
return AVERROR_INVALIDDATA;
ret = decode_extradata_ps_mp4(p, nalsize, ps, err_recognition, logctx);
if (ret < 0) {
av_log(logctx, AV_LOG_ERROR,
"Decoding sps %d from avcC failed\n", i);
return ret;
}
p += nalsize;
}
// Decode pps from avcC
// 解码PPS
cnt = *(p++); // Number of pps
for (i = 0; i < cnt; i++) {
nalsize = AV_RB16(p) + 2;
if (nalsize > size - (p - data))
return AVERROR_INVALIDDATA;
ret = decode_extradata_ps_mp4(p, nalsize, ps, err_recognition, logctx);
if (ret < 0) {
av_log(logctx, AV_LOG_ERROR,
"Decoding pps %d from avcC failed\n", i);
return ret;
}
p += nalsize;
}
// Store right nal length size that will be used to parse all other nals
*nal_length_size = (data[4] & 0x03) + 1;
} else {
*is_avc = 0;
ret = decode_extradata_ps(data, size, ps, 0, logctx);
if (ret < 0)
return ret;
}
return size;
}
这里extradata存储的其实是SPS和PPS,解析可以获取SPS和PPS,像FLV这些格式SPS和PPS是存储在extradata里的
h264_find_frame_end()
static int h264_find_frame_end(H264ParseContext *p, const uint8_t *buf,
int buf_size, void *logctx)
{
int i, j;
uint32_t state;
ParseContext *pc = &p->pc;
int next_avc = p->is_avc ? 0 : buf_size;
// mb_addr= pc->mb_addr - 1;
state = pc->state;
if (state > 13)
state = 7;
if (p->is_avc && !p->nal_length_size)
av_log(logctx, AV_LOG_ERROR, "AVC-parser: nal length size invalid\n");
for (i = 0; i < buf_size; i++) {
if (i >= next_avc) {
int nalsize = 0;
i = next_avc;
for (j = 0; j < p->nal_length_size; j++)
nalsize = (nalsize << 8) | buf[i++];
if (nalsize <= 0 || nalsize > buf_size - i) {
av_log(logctx, AV_LOG_ERROR, "AVC-parser: nal size %d remaining %d\n", nalsize, buf_size - i);
return buf_size;
}
next_avc = i + nalsize;
state = 5;
}
// 这里有个状态机,通过状态机查找start_code来找一帧
// 7 - 初始化状态
// 2 - 找到1个0
// 1 - 找到2个0
// 0 - 找到大于等于3个0
// 4 - 找到2个0和1个1,即001(即找到了起始码)
// 5 - 找到至少3个0和1个1,即0001等等(即找到了起始码)
// >=8 - 找到2个Slice Header
if (state == 7) {
// 查找0的位置
i += p->h264dsp.startcode_find_candidate(buf + i, next_avc - i);
if (i < next_avc)
state = 2;
} else if (state <= 2) {
if (buf[i] == 1)
state ^= 5; // 2->7, 1->4, 0->5
else if (buf[i])
state = 7;
else
state >>= 1; // 2->1, 1->0, 0->0
} else if (state <= 5) {
int nalu_type = buf[i] & 0x1F;
if (nalu_type == H264_NAL_SEI || nalu_type == H264_NAL_SPS ||
nalu_type == H264_NAL_PPS || nalu_type == H264_NAL_AUD) {
if (pc->frame_start_found) {
i++;
goto found;
}
} else if (nalu_type == H264_NAL_SLICE || nalu_type == H264_NAL_DPA ||
nalu_type == H264_NAL_IDR_SLICE) {
state += 8;
continue;
}
state = 7;
} else {
unsigned int mb, last_mb = p->parse_last_mb;
GetBitContext gb;
p->parse_history[p->parse_history_count++] = buf[i];
init_get_bits(&gb, p->parse_history, 8*p->parse_history_count);
mb= get_ue_golomb_long(&gb);
if (get_bits_left(&gb) > 0 || p->parse_history_count > 5) {
p->parse_last_mb = mb;
if (pc->frame_start_found) {
if (mb <= last_mb) {
i -= p->parse_history_count - 1;
p->parse_history_count = 0;
goto found;
}
} else
pc->frame_start_found = 1;
p->parse_history_count = 0;
state = 7;
}
}
}
pc->state = state;
if (p->is_avc)
return next_avc;
return END_NOT_FOUND;
found:
pc->state = 7;
pc->frame_start_found = 0;
if (p->is_avc)
return next_avc;
return i - (state & 5);
}
这里主要是通过startcode去查找一帧NALU的位置。
ff_combine_frame()
int ff_combine_frame(ParseContext *pc, int next,
const uint8_t **buf, int *buf_size)
{
if (pc->overread) {
ff_dlog(NULL, "overread %d, state:%"PRIX32" next:%d index:%d o_index:%d\n",
pc->overread, pc->state, next, pc->index, pc->overread_index);
ff_dlog(NULL, "%X %X %X %X\n",
(*buf)[0], (*buf)[1], (*buf)[2], (*buf)[3]);
}
/* Copy overread bytes from last frame into buffer. */
for (; pc->overread > 0; pc->overread--)
pc->buffer[pc->index++] = pc->buffer[pc->overread_index++];
if (next > *buf_size)
return AVERROR(EINVAL);
/* flush remaining if EOF */
if (!*buf_size && next == END_NOT_FOUND)
next = 0;
pc->last_index = pc->index;
/* copy into buffer end return */
// 没找到数据缓存到pc->buffer
if (next == END_NOT_FOUND) {
void *new_buffer = av_fast_realloc(pc->buffer, &pc->buffer_size,
*buf_size + pc->index +
AV_INPUT_BUFFER_PADDING_SIZE);
if (!new_buffer) {
av_log(NULL, AV_LOG_ERROR, "Failed to reallocate parser buffer to %d\n", *buf_size + pc->index + AV_INPUT_BUFFER_PADDING_SIZE);
pc->index = 0;
return AVERROR(ENOMEM);
}
pc->buffer = new_buffer;
memcpy(&pc->buffer[pc->index], *buf, *buf_size);
pc->index += *buf_size;
return -1;
}
av_assert0(next >= 0 || pc->buffer);
*buf_size =
pc->overread_index = pc->index + next;
/* append to buffer */
// 找到就把数据组装buf里
if (pc->index) {
void *new_buffer = av_fast_realloc(pc->buffer, &pc->buffer_size,
next + pc->index +
AV_INPUT_BUFFER_PADDING_SIZE);
if (!new_buffer) {
av_log(NULL, AV_LOG_ERROR, "Failed to reallocate parser buffer to %d\n", next + pc->index + AV_INPUT_BUFFER_PADDING_SIZE);
pc->overread_index =
pc->index = 0;
return AVERROR(ENOMEM);
}
pc->buffer = new_buffer;
if (next > -AV_INPUT_BUFFER_PADDING_SIZE)
memcpy(&pc->buffer[pc->index], *buf,
next + AV_INPUT_BUFFER_PADDING_SIZE);
pc->index = 0;
*buf = pc->buffer;
}
/* store overread bytes */
for (; next < 0; next++) {
pc->state = pc->state << 8 | pc->buffer[pc->last_index + next];
pc->state64 = pc->state64 << 8 | pc->buffer[pc->last_index + next];
pc->overread++;
}
if (pc->overread) {
ff_dlog(NULL, "overread %d, state:%"PRIX32" next:%d index:%d o_index:%d\n",
pc->overread, pc->state, next, pc->index, pc->overread_index);
ff_dlog(NULL, "%X %X %X %X\n",
(*buf)[0], (*buf)[1], (*buf)[2], (*buf)[3]);
}
return 0;
}
这里主要做了两件事:
- 要是没找到就缓存到buffer
- 找到就组装一帧NALU到buffer
整理了一些音视频开发学习书籍、视频资料(音视频流媒体高级开发FFmpeg6.0/WebRTC/RTMP/RTSP/编码解码),有需要的可以自行添加学习交流群:739729163 领取!