此前寫了好幾篇ffmpeg源代碼分析文章,列表如下:
圖解FFMPEG打開媒體的函數avformat_open_input
ffmpeg 源代碼簡單分析 : av_register_all()
ffmpeg 源代碼簡單分析 : avcodec_register_all()
ffmpeg 源代碼簡單分析 : av_read_frame()
ffmpeg 源代碼簡單分析 : avcodec_decode_video2()
============================
ffmpeg中的av_read_frame()的作用是讀取碼流中的音頻若干幀或者視頻一幀。例如,解碼視頻的時候,每解碼一個視頻幀,需要先調用 av_read_frame()獲得一幀視頻的壓縮數據,然後才能對該數據進行解碼(例如H.264中一幀壓縮數據通常對應一個NAL)。
對該函數源代碼的分析是很久之前做的了,現在翻出來,用博客記錄一下。
上代碼之前,先參考了其他人對av_read_frame()的解釋,在此做一個參考:
通過av_read_packet(***),讀取一個包,需要說明的是此函數必須是包含整數幀的,不存在半幀的情況,以ts流爲例,是讀取一個完整的PES包(一個完整pes包包含若干視頻或音頻es包),讀取完畢後,通過av_parser_parse2(***)分析出視頻一幀(或音頻若干幀),返回,下次進入循環的時候,如果上次的數據沒有完全取完,則st = s->cur_st;不會是NULL,即再此進入av_parser_parse2(***)流程,而不是下面的av_read_packet(**)流程,這樣就保證了,如果讀取一次包含了N幀視頻數據(以視頻爲例),則調用av_read_frame(***)N次都不會去讀數據,而是返回第一次讀取的數據,直到全部解析完畢。
av_read_frame()的源代碼如下:
- //獲取一個AVPacket
- /*
- * av_read_frame - 新版本的ffmpeg用的是av_read_frame,而老版本的是av_read_packet
- * 。區別是av_read_packet讀出的是包,它可能是半幀或多幀,不保證幀的完整性。av_read_frame對
- * av_read_packet進行了封裝,使讀出的數據總是完整的幀
- */
- int av_read_frame(AVFormatContext *s, AVPacket *pkt)
- {
- const int genpts = s->flags & AVFMT_FLAG_GENPTS;
- int eof = 0;
- if (!genpts)
- /**
- * This buffer is only needed when packets were already buffered but
- * not decoded, for example to get the codec parameters in MPEG
- * streams.
- * 一般情況下會調用read_frame_internal(s, pkt)
- * 直接返回
- */
- return s->packet_buffer ? read_from_packet_buffer(s, pkt) :
- read_frame_internal(s, pkt);
- for (;;) {
- int ret;
- AVPacketList *pktl = s->packet_buffer;
- if (pktl) {
- AVPacket *next_pkt = &pktl->pkt;
- if (next_pkt->dts != AV_NOPTS_VALUE) {
- int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;
- while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {
- if (pktl->pkt.stream_index == next_pkt->stream_index &&
- (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0) &&
- av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame
- next_pkt->pts = pktl->pkt.dts;
- }
- pktl = pktl->next;
- }
- pktl = s->packet_buffer;
- }
- /* read packet from packet buffer, if there is data */
- if (!(next_pkt->pts == AV_NOPTS_VALUE &&
- next_pkt->dts != AV_NOPTS_VALUE && !eof))
- return read_from_packet_buffer(s, pkt);
- }
- ret = read_frame_internal(s, pkt);
- if (ret < 0) {
- if (pktl && ret != AVERROR(EAGAIN)) {
- eof = 1;
- continue;
- } else
- return ret;
- }
- if (av_dup_packet(add_to_pktbuf(&s->packet_buffer, pkt,
- &s->packet_buffer_end)) < 0)
- return AVERROR(ENOMEM);
- }
- }
一般情況下,av_read_frame()會調用read_frame_internal(),其代碼如下所示:
- //av_read_frame對他進行了封裝
- static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
- {
- AVStream *st;
- int len, ret, i;
- //初始化
- av_init_packet(pkt);
- for(;;) {
- /* 選擇當前的 input stream */
- st = s->cur_st;
- if (st) {
- //不需要解析。不清楚哪些數據屬於這類
- if (!st->need_parsing || !st->parser) {
- /* no parsing needed: we just output the packet as is */
- /* raw data support */
- *pkt = st->cur_pkt;
- st->cur_pkt.data= NULL;
- st->cur_pkt.side_data_elems = 0;
- st->cur_pkt.side_data = NULL;
- compute_pkt_fields(s, st, NULL, pkt);
- s->cur_st = NULL;
- if ((s->iformat->flags & AVFMT_GENERIC_INDEX) &&
- (pkt->flags & AV_PKT_FLAG_KEY) && pkt->dts != AV_NOPTS_VALUE) {
- ff_reduce_index(s, st->index);
- av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
- }
- break;
- } //需要解析
- else if (st->cur_len > 0 && st->discard < AVDISCARD_ALL) {
- //解析
- len = av_parser_parse2(st->parser, st->codec, &pkt->data, &pkt->size,
- st->cur_ptr, st->cur_len,
- st->cur_pkt.pts, st->cur_pkt.dts,
- st->cur_pkt.pos);
- st->cur_pkt.pts = AV_NOPTS_VALUE;
- st->cur_pkt.dts = AV_NOPTS_VALUE;
- /* increment read pointer */
- st->cur_ptr += len;
- st->cur_len -= len;
- /* return packet if any */
- if (pkt->size) {
- got_packet:
- pkt->duration = 0;
- pkt->stream_index = st->index;
- pkt->pts = st->parser->pts;
- pkt->dts = st->parser->dts;
- pkt->pos = st->parser->pos;
- if(pkt->data == st->cur_pkt.data && pkt->size == st->cur_pkt.size){
- s->cur_st = NULL;
- pkt->destruct= st->cur_pkt.destruct;
- st->cur_pkt.destruct= NULL;
- st->cur_pkt.data = NULL;
- assert(st->cur_len == 0);
- }else{
- pkt->destruct = NULL;
- }
- compute_pkt_fields(s, st, st->parser, pkt);
- if((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY){
- int64_t pos= (st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) ? pkt->pos : st->parser->frame_offset;
- ff_reduce_index(s, st->index);
- av_add_index_entry(st, pos, pkt->dts,
- 0, 0, AVINDEX_KEYFRAME);
- }
- break;
- }
- } else {
- /* free packet */
- av_free_packet(&st->cur_pkt);
- s->cur_st = NULL;
- }
- } else {
- AVPacket cur_pkt;
- /* read next packet */
- //讀取AVPacket,老版本里只有av_read_packet,現在被封裝了
- ret = av_read_packet(s, &cur_pkt);
- if (ret < 0) {
- if (ret == AVERROR(EAGAIN))
- return ret;
- /* return the last frames, if any */
- for(i = 0; i < s->nb_streams; i++) {
- st = s->streams[i];
- if (st->parser && st->need_parsing) {
- av_parser_parse2(st->parser, st->codec,
- &pkt->data, &pkt->size,
- NULL, 0,
- AV_NOPTS_VALUE, AV_NOPTS_VALUE,
- AV_NOPTS_VALUE);
- if (pkt->size)
- goto got_packet;
- }
- }
- /* no more packets: really terminate parsing */
- return ret;
- }
- st = s->streams[cur_pkt.stream_index];
- st->cur_pkt= cur_pkt;
- if(st->cur_pkt.pts != AV_NOPTS_VALUE &&
- st->cur_pkt.dts != AV_NOPTS_VALUE &&
- st->cur_pkt.pts < st->cur_pkt.dts){
- av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n",
- st->cur_pkt.stream_index,
- st->cur_pkt.pts,
- st->cur_pkt.dts,
- st->cur_pkt.size);
- // av_free_packet(&st->cur_pkt);
- // return -1;
- }
- if(s->debug & FF_FDEBUG_TS)
- av_log(s, AV_LOG_DEBUG, "av_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n",
- st->cur_pkt.stream_index,
- st->cur_pkt.pts,
- st->cur_pkt.dts,
- st->cur_pkt.size,
- st->cur_pkt.duration,
- st->cur_pkt.flags);
- s->cur_st = st;
- st->cur_ptr = st->cur_pkt.data;
- st->cur_len = st->cur_pkt.size;
- if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
- st->parser = av_parser_init(st->codec->codec_id);
- if (!st->parser) {
- av_log(s, AV_LOG_VERBOSE, "parser not found for codec "
- "%s, packets or times may be invalid.\n",
- avcodec_get_name(st->codec->codec_id));
- /* 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;
- }
- }
- }
- }
- if(s->debug & FF_FDEBUG_TS)
- av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n",
- pkt->stream_index,
- pkt->pts,
- pkt->dts,
- pkt->size,
- pkt->duration,
- pkt->flags);
- return 0;
- }
一般的碼流都需要解析,這是需要調用av_paser_parse2(),它的代碼如下所示:
- //解析。例如解析264裏的NAL等等
- 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)
- {
- int index, i;
- uint8_t dummy_buf[FF_INPUT_BUFFER_PADDING_SIZE];
- if(!(s->flags & PARSER_FLAG_FETCHED_OFFSET)) {
- s->next_frame_offset =
- s->cur_offset = pos;
- s->flags |= PARSER_FLAG_FETCHED_OFFSET;
- }
- if (buf_size == 0) {
- /* padding is always necessary even if EOF, so we add it here */
- memset(dummy_buf, 0, sizeof(dummy_buf));
- buf = dummy_buf;
- } else if (s->cur_offset + buf_size !=
- s->cur_frame_end[s->cur_frame_start_index]) { /* skip remainder packets */
- /* add a new packet descriptor */
- i = (s->cur_frame_start_index + 1) & (AV_PARSER_PTS_NB - 1);
- s->cur_frame_start_index = i;
- s->cur_frame_offset[i] = s->cur_offset;
- s->cur_frame_end[i] = s->cur_offset + buf_size;
- s->cur_frame_pts[i] = pts;
- s->cur_frame_dts[i] = dts;
- s->cur_frame_pos[i] = pos;
- }
- if (s->fetch_timestamp){
- s->fetch_timestamp=0;
- s->last_pts = s->pts;
- s->last_dts = s->dts;
- s->last_pos = s->pos;
- ff_fetch_timestamp(s, 0, 0);
- }
- /* WARNING: the returned index can be negative */
- //H264裏對應的就是parser_parse=h264_parse,
- index = s->parser->parser_parse(s, avctx, (const uint8_t **)poutbuf, poutbuf_size, buf, buf_size);
- //av_log(NULL, AV_LOG_DEBUG, "parser: in:%"PRId64", %"PRId64", out:%"PRId64", %"PRId64", in:%d out:%d id:%d\n", pts, dts, s->last_pts, s->last_dts, buf_size, *poutbuf_size, avctx->codec_id);
- /* update the file pointer */
- if (*poutbuf_size) {
- /* fill the data for the current frame */
- s->frame_offset = s->next_frame_offset;
- /* offset of the next frame */
- s->next_frame_offset = s->cur_offset + index;
- s->fetch_timestamp=1;
- }
- if (index < 0)
- index = 0;
- s->cur_offset += index;
- return index;
- }
從index = s->parser->parser_parse(s, avctx, (const uint8_t **)poutbuf, poutbuf_size, buf, buf_size);這句代碼可以看出,最終調用了相應解碼器的parser_parse()函數。
有點累了,先不做詳細分析,以後有機會再補上。
原文地址:http://blog.csdn.net/leixiaohua1020/article/details/12678577