zoukankan      html  css  js  c++  java
  • FFmpeg的H264解码源码分析:解析器

    解析器主要就是解析出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;
    }

    这里主要做了几件事:

    1. 解析extradata
    2. 查找一帧结束位置
    3. 组装一帧NALU
    4. 解析NALU,获取一些SPS、PPS等信息
    5. 输出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
    ", 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
    ", 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
    ", 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
    ");
    
        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
    ", 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
    ",
                    pc->overread, pc->state, next, pc->index, pc->overread_index);
            ff_dlog(NULL, "%X %X %X %X
    ",
                    (*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
    ", *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
    ", 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
    ",
                    pc->overread, pc->state, next, pc->index, pc->overread_index);
            ff_dlog(NULL, "%X %X %X %X
    ",
                    (*buf)[0], (*buf)[1], (*buf)[2], (*buf)[3]);
        }
    
        return 0;
    }

    这里主要做了两件事:

    1. 要是没找到就缓存到buffer
    2. 找到就组装一帧NALU到buffer
  • 相关阅读:
    Java基础-集合框架的学习大纲
    Java多线程并发学习-进阶大纲
    Spring学习大纲
    Netty学习大纲
    分布式学习大纲
    数据库学习大纲
    缓存学习大纲
    JVM-jvm学习大纲(0)
    多线程学习-基础(十三)(学习参考·网摘) ArrayBlockingQueue源代碼解析(base jdk 1.8)
    C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法
  • 原文地址:https://www.cnblogs.com/vczf/p/14837759.html
Copyright © 2011-2022 走看看