FFMPeg程式碼分析:av_read_frame()函式的內部構造
阿新 • • 發佈:2019-02-20
上文中貼出了av_read_frame()函式的實現,現在更細緻地分析一下其內部的實現流程。
av_read_frame()開始後,通常會呼叫read_frame_internal(s, pkt)函式:
首先呼叫av_init_packet(pkt)對pkt進行初始化:static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) { int ret = 0, i, got_packet = 0; av_init_packet(pkt); while (!got_packet && !s->parse_queue) { AVStream *st; AVPacket cur_pkt; /* read next packet */ ret = ff_read_packet(s, &cur_pkt); if (ret < 0) { if (ret == AVERROR(EAGAIN)) return ret; /* flush the parsers */ for(i = 0; i < s->nb_streams; i++) { st = s->streams[i]; if (st->parser && st->need_parsing) parse_packet(s, NULL, st->index); } /* all remaining packets are now in parse_queue => * really terminate parsing */ break; } ret = 0; st = s->streams[cur_pkt.stream_index]; if (cur_pkt.pts != AV_NOPTS_VALUE && cur_pkt.dts != AV_NOPTS_VALUE && cur_pkt.pts < cur_pkt.dts) { av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n", cur_pkt.stream_index, av_ts2str(cur_pkt.pts), av_ts2str(cur_pkt.dts), cur_pkt.size); } if (s->debug & FF_FDEBUG_TS) av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", cur_pkt.stream_index, av_ts2str(cur_pkt.pts), av_ts2str(cur_pkt.dts), cur_pkt.size, cur_pkt.duration, cur_pkt.flags); 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; } else if(st->need_parsing == AVSTREAM_PARSE_FULL_RAW) { st->parser->flags |= PARSER_FLAG_USE_CODEC_TS; } } if (!st->need_parsing || !st->parser) { /* no parsing needed: we just output the packet as is */ *pkt = cur_pkt; compute_pkt_fields(s, st, NULL, pkt); 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); } got_packet = 1; } else if (st->discard < AVDISCARD_ALL) { if ((ret = parse_packet(s, &cur_pkt, cur_pkt.stream_index)) < 0) return ret; } else { /* free packet */ av_free_packet(&cur_pkt); } if (pkt->flags & AV_PKT_FLAG_KEY) st->skip_to_keyframe = 0; if (st->skip_to_keyframe) { av_free_packet(&cur_pkt); if (got_packet) { *pkt = cur_pkt; } got_packet = 0; } } if (!got_packet && s->parse_queue) ret = read_from_packet_buffer(&s->parse_queue, &s->parse_queue_end, pkt); if(s->debug & FF_FDEBUG_TS) av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%s, dts=%s, size=%d, duration=%d, flags=%d\n", pkt->stream_index, av_ts2str(pkt->pts), av_ts2str(pkt->dts), pkt->size, pkt->duration, pkt->flags); return ret; }
該函式將pts、dts設為AV_NOPTS_VALUE,將pos初始化為-1,將其他引數設為0或空值;隨後在一個while迴圈中,呼叫ff_read_packet函式讀取資料:void av_init_packet(AVPacket *pkt) { pkt->pts = AV_NOPTS_VALUE; pkt->dts = AV_NOPTS_VALUE; pkt->pos = -1; pkt->duration = 0; pkt->convergence_duration = 0; pkt->flags = 0; pkt->stream_index = 0; #if FF_API_DESTRUCT_PACKET FF_DISABLE_DEPRECATION_WARNINGS pkt->destruct = NULL; FF_ENABLE_DEPRECATION_WARNINGS #endif pkt->buf = NULL; pkt->side_data = NULL; pkt->side_data_elems = 0; }
該函式從AVFormatContext指標格式的媒體檔案控制代碼中讀取待解碼資料,並儲存於AVPacket例項中。當讀取成功時返回0,讀取失敗則返回AVERROR值。int ff_read_packet(AVFormatContext *s, AVPacket *pkt) { int ret, i, err; AVStream *st; for(;;){ AVPacketList *pktl = s->raw_packet_buffer; if (pktl) { *pkt = pktl->pkt; st = s->streams[pkt->stream_index]; if (s->raw_packet_buffer_remaining_size <= 0) { if ((err = probe_codec(s, st, NULL)) < 0) return err; } if(st->request_probe <= 0){ s->raw_packet_buffer = pktl->next; s->raw_packet_buffer_remaining_size += pkt->size; av_free(pktl); return 0; } } pkt->data = NULL; pkt->size = 0; av_init_packet(pkt); ret= s->iformat->read_packet(s, pkt); if (ret < 0) { if (!pktl || ret == AVERROR(EAGAIN)) return ret; for (i = 0; i < s->nb_streams; i++) { st = s->streams[i]; if (st->probe_packets) { if ((err = probe_codec(s, st, NULL)) < 0) return err; } av_assert0(st->request_probe <= 0); } continue; } if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) && (pkt->flags & AV_PKT_FLAG_CORRUPT)) { av_log(s, AV_LOG_WARNING, "Dropped corrupted packet (stream = %d)\n", pkt->stream_index); av_free_packet(pkt); continue; } if(!(s->flags & AVFMT_FLAG_KEEP_SIDE_DATA)) av_packet_merge_side_data(pkt); if(pkt->stream_index >= (unsigned)s->nb_streams){ av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index); continue; } st= s->streams[pkt->stream_index]; pkt->dts = wrap_timestamp(st, pkt->dts); pkt->pts = wrap_timestamp(st, pkt->pts); force_codec_ids(s, st); /* TODO: audio: time filter; video: frame reordering (pts != dts) */ if (s->use_wallclock_as_timestamps) pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base); if(!pktl && st->request_probe <= 0) return ret; add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end); s->raw_packet_buffer_remaining_size -= pkt->size; if ((err = probe_codec(s, st, pkt)) < 0) return err; } }
該函式呼叫av_init_packet對引數pkg初始化,並呼叫iformat的方法read_packet讀取資料。在本demo中,AVFormatContext例項中iformat成員為ff_mov_demuxer,如下所示:
AVInputFormat ff_mov_demuxer = {
.name = "mov,mp4,m4a,3gp,3g2,mj2",
.long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
.priv_data_size = sizeof(MOVContext),
.read_probe = mov_probe,
.read_header = mov_read_header,
.read_packet = mov_read_packet,
.read_close = mov_read_close,
.read_seek = mov_read_seek,
.priv_class = &mov_class,
.flags = AVFMT_NO_BYTE_SEEK,
};
其read_packet指標指向mov_read_packet函式,這個函式的內容也比較長,暫時就不貼了,有時間慢慢分析。