ijkplayer框架簡析 -- avformat_find_stream_info
avformat_find_stream_info()
主要是讀媒體檔案的 packet ,然後從中提取出流的資訊
avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) { int i, count = 0, ret = 0, j; int64_t read_size; AVStream *st; AVCodecContext *avctx; AVPacket pkt1, *pkt; int64_t old_offset= avio_tell(ic->pb); // new streams might appear, no options for those int orig_nb_streams = ic->nb_streams; int flush_codecs; int64_t max_analyze_duration = ic->max_analyze_duration; int64_t max_stream_analyze_duration; int64_t max_subtitle_analyze_duration; int64_t probesize = ic->probesize; int eof_reached = 0; int *missing_streams = av_opt_ptr(ic->iformat->priv_class, ic->priv_data, "missing_streams"); flush_codecs = probesize > 0; av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN); max_stream_analyze_duration = max_analyze_duration; max_subtitle_analyze_duration = max_analyze_duration; if (!max_analyze_duration) { max_stream_analyze_duration = max_analyze_duration= 5*AV_TIME_BASE; max_subtitle_analyze_duration = 30*AV_TIME_BASE; if (!strcmp(ic->iformat->name, "flv")) max_stream_analyze_duration = 90*AV_TIME_BASE; if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts")) max_stream_analyze_duration = 7*AV_TIME_BASE; } if (ic->pb) av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d nb_streams:%d\n", avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, ic->nb_streams); for (i = 0; i < ic->nb_streams; i++) { const AVCodec *codec; AVDictionary *thread_opt = NULL; st = ic->streams[i]; avctx = st->internal->avctx; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { /*if (!st->time_base.num) st->time_base = */ if (!avctx->time_base.num) avctx->time_base = st->time_base; } /* check if the caller has overridden the codec id */ #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS if (st->codec->codec_id != st->internal->orig_codec_id) { st->codecpar->codec_id= st->codec->codec_id; st->codecpar->codec_type = st->codec->codec_type; st->internal->orig_codec_id = st->codec->codec_id; } FF_ENABLE_DEPRECATION_WARNINGS #endif // only for the split stuff if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE) && st->request_probe <= 0) { st->parser = av_parser_init(st->codecpar->codec_id); if (st->parser) { if (st->need_parsing == AVSTREAM_PARSE_HEADERS) { st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; } else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW) { st->parser->flags |= PARSER_FLAG_USE_CODEC_TS; } } else if (st->need_parsing) { av_log(ic, AV_LOG_VERBOSE, "parser not found for codec " "%s, packets or times may be invalid.\n", avcodec_get_name(st->codecpar->codec_id)); } } if (st->codecpar->codec_id != st->internal->orig_codec_id) st->internal->orig_codec_id = st->codecpar->codec_id; ret = avcodec_parameters_to_context(avctx, st->codecpar); if (ret < 0) goto find_stream_info_err; if (st->request_probe <= 0) st->internal->avctx_inited = 1; codec = find_probe_decoder(ic, st, st->codecpar->codec_id); /* Force thread count to 1 since the H.264 decoder will not extract * SPS and PPS to extradata during multi-threaded decoding. */ av_dict_set(options ? &options[i] : &thread_opt, "threads", "1", 0); if (ic->codec_whitelist) av_dict_set(options ? &options[i] : &thread_opt, "codec_whitelist", ic->codec_whitelist, 0); /* Ensure that subtitle_header is properly set. */ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && codec && !avctx->codec) { if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0) av_log(ic, AV_LOG_WARNING, "Failed to open codec in %s\n",__FUNCTION__); } // Try to just open decoders, in case this is enough to get parameters. if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) { if (codec && !avctx->codec) if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0) av_log(ic, AV_LOG_WARNING, "Failed to open codec in %s\n",__FUNCTION__); } if (!options) av_dict_free(&thread_opt); } for (i = 0; i < ic->nb_streams; i++) { #if FF_API_R_FRAME_RATE ic->streams[i]->info->last_dts = AV_NOPTS_VALUE; #endif ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE; ic->streams[i]->info->fps_last_dts= AV_NOPTS_VALUE; } read_size = 0; for (;;) { int analyzed_all_streams; if (ff_check_interrupt(&ic->interrupt_callback)) { ret = AVERROR_EXIT; av_log(ic, AV_LOG_DEBUG, "interrupted\n"); break; } /* check if one codec still needs to be handled */ for (i = 0; i < ic->nb_streams; i++) { int fps_analyze_framecount = 20; st = ic->streams[i]; if (!has_codec_parameters(st, NULL)) break; if (ic->metadata) { AVDictionaryEntry *t = av_dict_get(ic->metadata, "skip-calc-frame-rate", NULL, AV_DICT_MATCH_CASE); if (t) { int fps_flag = (int) strtol(t->value, NULL, 10); if (!st->r_frame_rate.num && st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0 && fps_flag > 0) { int avg_fps = st->avg_frame_rate.num / st->avg_frame_rate.den; if (avg_fps > 0 && avg_fps <= 120) { st->r_frame_rate.num = st->avg_frame_rate.num; st->r_frame_rate.den = st->avg_frame_rate.den; } } } } /* If the timebase is coarse (like the usual millisecond precision * of mkv), we need to analyze more frames to reliably arrive at * the correct fps. */ if (av_q2d(st->time_base) > 0.0005) fps_analyze_framecount *= 2; if (!tb_unreliable(st->internal->avctx)) fps_analyze_framecount = 0; if (ic->fps_probe_size >= 0) fps_analyze_framecount = ic->fps_probe_size; if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) fps_analyze_framecount = 0; /* variable fps and no guess at the real fps */ if (!(st->r_frame_rate.num && st->avg_frame_rate.num) && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ? st->info->codec_info_duration_fields/2 : st->info->duration_count; if (count < fps_analyze_framecount) break; } if (!st->internal->avctx->extradata && (!st->internal->extract_extradata.inited || st->internal->extract_extradata.bsf) && extract_extradata_check(st)) break; if (st->first_dts == AV_NOPTS_VALUE && !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) && st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) && (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) break; } analyzed_all_streams = 0; if (!missing_streams || !*missing_streams) if (i == ic->nb_streams) { analyzed_all_streams = 1; /* NOTE: If the format has no header, then we need to read some * packets to get most of the streams, so we cannot stop here. */ if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) { /* If we found the info for all the codecs, we can stop. */ ret = count; av_log(ic, AV_LOG_DEBUG, "All info found\n"); flush_codecs = 0; break; } } /* We did not get all the codec info, but we read too much data. */ if (read_size >= probesize) { ret = count; av_log(ic, AV_LOG_DEBUG, "Probe buffer size limit of %"PRId64" bytes reached\n", probesize); for (i = 0; i < ic->nb_streams; i++) if (!ic->streams[i]->r_frame_rate.num && ic->streams[i]->info->duration_count <= 1 && ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && strcmp(ic->iformat->name, "image2")) av_log(ic, AV_LOG_WARNING, "Stream #%d: not enough frames to estimate rate; " "consider increasing probesize\n", i); break; } /* NOTE: A new stream can be added there if no header in file * (AVFMTCTX_NOHEADER). */ ret = read_frame_internal(ic, &pkt1); if (ret == AVERROR(EAGAIN)) continue; if (ret < 0) { /* EOF or error*/ eof_reached = 1; break; } pkt = &pkt1; if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) { ret = add_to_pktbuf(&ic->internal->packet_buffer, pkt, &ic->internal->packet_buffer_end, 0); if (ret < 0) goto find_stream_info_err; } st = ic->streams[pkt->stream_index]; if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) read_size += pkt->size; avctx = st->internal->avctx; if (!st->internal->avctx_inited) { ret = avcodec_parameters_to_context(avctx, st->codecpar); if (ret < 0) goto find_stream_info_err; st->internal->avctx_inited = 1; } if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) { /* check for non-increasing dts */ if (st->info->fps_last_dts != AV_NOPTS_VALUE && st->info->fps_last_dts >= pkt->dts) { av_log(ic, AV_LOG_DEBUG, "Non-increasing DTS in stream %d: packet %d with DTS " "%"PRId64", packet %d with DTS %"PRId64"\n", st->index, st->info->fps_last_dts_idx, st->info->fps_last_dts, st->codec_info_nb_frames, pkt->dts); st->info->fps_first_dts = st->info->fps_last_dts= AV_NOPTS_VALUE; } /* Check for a discontinuity in dts. If the difference in dts * is more than 1000 times the average packet duration in the * sequence, we treat it as a discontinuity. */ if (st->info->fps_last_dts != AV_NOPTS_VALUE && st->info->fps_last_dts_idx > st->info->fps_first_dts_idx && (pkt->dts - st->info->fps_last_dts) / 1000 > (st->info->fps_last_dts- st->info->fps_first_dts) / (st->info->fps_last_dts_idx - st->info->fps_first_dts_idx)) { av_log(ic, AV_LOG_WARNING, "DTS discontinuity in stream %d: packet %d with DTS " "%"PRId64", packet %d with DTS %"PRId64"\n", st->index, st->info->fps_last_dts_idx, st->info->fps_last_dts, st->codec_info_nb_frames, pkt->dts); st->info->fps_first_dts = st->info->fps_last_dts= AV_NOPTS_VALUE; } /* update stored dts values */ if (st->info->fps_first_dts == AV_NOPTS_VALUE) { st->info->fps_first_dts= pkt->dts; st->info->fps_first_dts_idx = st->codec_info_nb_frames; } st->info->fps_last_dts= pkt->dts; st->info->fps_last_dts_idx = st->codec_info_nb_frames; } if (st->codec_info_nb_frames>1) { int64_t t = 0; int64_t limit; if (st->time_base.den > 0) t = av_rescale_q(st->info->codec_info_duration, st->time_base, AV_TIME_BASE_Q); if (st->avg_frame_rate.num > 0) t = FFMAX(t, av_rescale_q(st->codec_info_nb_frames, av_inv_q(st->avg_frame_rate), AV_TIME_BASE_Q)); if (t == 0 && st->codec_info_nb_frames>30 && st->info->fps_first_dts != AV_NOPTS_VALUE && st->info->fps_last_dts!= AV_NOPTS_VALUE) t = FFMAX(t, av_rescale_q(st->info->fps_last_dts - st->info->fps_first_dts, st->time_base, AV_TIME_BASE_Q)); if (analyzed_all_streams)limit = max_analyze_duration; else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) limit = max_subtitle_analyze_duration; elselimit = max_stream_analyze_duration; if (t >= limit) { av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %"PRId64" reached at %"PRId64" microseconds st:%d\n", limit, t, pkt->stream_index); if (ic->flags & AVFMT_FLAG_NOBUFFER) av_packet_unref(pkt); break; } if (pkt->duration) { if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && pkt->pts != AV_NOPTS_VALUE && pkt->pts >= st->start_time) { st->info->codec_info_duration = FFMIN(pkt->pts - st->start_time, st->info->codec_info_duration + pkt->duration); } else st->info->codec_info_duration += pkt->duration; st->info->codec_info_duration_fields += st->parser && st->need_parsing && avctx->ticks_per_frame ==2 ? st->parser->repeat_pict + 1 : 2; } } #if FF_API_R_FRAME_RATE if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ff_rfps_add_frame(ic, st, pkt->dts); #endif if (!st->internal->avctx->extradata) { ret = extract_extradata(st, pkt); if (ret < 0) goto find_stream_info_err; } /* If still no information, we try to open the codec and to * decompress the frame. We try to avoid that in most cases as * it takes longer and uses more memory. For MPEG-4, we need to * decompress for QuickTime. * * If AV_CODEC_CAP_CHANNEL_CONF is set this will force decoding of at * least one frame of codec data, this makes sure the codec initializes * the channel configuration and does not only trust the values from * the container. */ try_decode_frame(ic, st, pkt, (options && i < orig_nb_streams) ? &options[i] : NULL); if (ic->flags & AVFMT_FLAG_NOBUFFER) av_packet_unref(pkt); st->codec_info_nb_frames++; count++; } if (eof_reached) { int stream_index; for (stream_index = 0; stream_index < ic->nb_streams; stream_index++) { st = ic->streams[stream_index]; avctx = st->internal->avctx; if (!has_codec_parameters(st, NULL)) { const AVCodec *codec = find_probe_decoder(ic, st, st->codecpar->codec_id); if (codec && !avctx->codec) { AVDictionary *opts = NULL; if (ic->codec_whitelist) av_dict_set(&opts, "codec_whitelist", ic->codec_whitelist, 0); if (avcodec_open2(avctx, codec, (options && stream_index < orig_nb_streams) ? &options[stream_index] : &opts) < 0) av_log(ic, AV_LOG_WARNING, "Failed to open codec in %s\n",__FUNCTION__); av_dict_free(&opts); } } // EOF already reached while reading the stream above. // So continue with reoordering DTS with whatever delay we have. if (ic->internal->packet_buffer && !has_decode_delay_been_guessed(st)) { update_dts_from_pts(ic, stream_index, ic->internal->packet_buffer); } } } if (flush_codecs) { AVPacket empty_pkt = { 0 }; int err = 0; av_init_packet(&empty_pkt); for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; /* flush the decoders */ if (st->info->found_decoder == 1) { do { err = try_decode_frame(ic, st, &empty_pkt, (options && i < orig_nb_streams) ? &options[i] : NULL); } while (err > 0 && !has_codec_parameters(st, NULL)); if (err < 0) { av_log(ic, AV_LOG_INFO, "decoding for stream %d failed\n", st->index); } } } } // close codecs which were opened in try_decode_frame() for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; avcodec_close(st->internal->avctx); } ff_rfps_calculate(ic); for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; avctx = st->internal->avctx; if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample) { uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt); if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == avctx->pix_fmt) avctx->codec_tag= tag; } /* estimate average framerate if not set by demuxer */ if (st->info->codec_info_duration_fields && !st->avg_frame_rate.num && st->info->codec_info_duration) { int best_fps= 0; double best_error = 0.01; AVRational codec_frame_rate = avctx->framerate; if (st->info->codec_info_duration>= INT64_MAX / st->time_base.num / 2|| st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den || st->info->codec_info_duration< 0) continue; av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, st->info->codec_info_duration_fields * (int64_t) st->time_base.den, st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000); /* Round guessed framerate to a "standard" framerate if it's * within 1% of the original estimate. */ for (j = 0; j < MAX_STD_TIMEBASES; j++) { AVRational std_fps = { get_std_framerate(j), 12 * 1001 }; double error= fabs(av_q2d(st->avg_frame_rate) / av_q2d(std_fps) - 1); if (error < best_error) { best_error = error; best_fps= std_fps.num; } if (ic->internal->prefer_codec_framerate && codec_frame_rate.num > 0 && codec_frame_rate.den > 0) { error= fabs(av_q2d(codec_frame_rate) / av_q2d(std_fps) - 1); if (error < best_error) { best_error = error; best_fps= std_fps.num; } } } if (best_fps) av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, best_fps, 12 * 1001, INT_MAX); } if (!st->r_frame_rate.num) { if (avctx->time_base.den * (int64_t) st->time_base.num <= avctx->time_base.num * avctx->ticks_per_frame * (int64_t) st->time_base.den) { av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, avctx->time_base.den, (int64_t)avctx->time_base.num * avctx->ticks_per_frame, INT_MAX); } else { st->r_frame_rate.num = st->time_base.den; st->r_frame_rate.den = st->time_base.num; } } if (st->display_aspect_ratio.num && st->display_aspect_ratio.den) { AVRational hw_ratio = { avctx->height, avctx->width }; st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio, hw_ratio); } } else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { if (!avctx->bits_per_coded_sample) avctx->bits_per_coded_sample = av_get_bits_per_sample(avctx->codec_id); // set stream disposition based on audio service type switch (avctx->audio_service_type) { case AV_AUDIO_SERVICE_TYPE_EFFECTS: st->disposition = AV_DISPOSITION_CLEAN_EFFECTS; break; case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED: st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED; break; case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED: st->disposition = AV_DISPOSITION_HEARING_IMPAIRED; break; case AV_AUDIO_SERVICE_TYPE_COMMENTARY: st->disposition = AV_DISPOSITION_COMMENT; break; case AV_AUDIO_SERVICE_TYPE_KARAOKE: st->disposition = AV_DISPOSITION_KARAOKE; break; } } } if (probesize) estimate_timings(ic, old_offset); av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN); if (ret >= 0 && ic->nb_streams) /* We could not have all the codec parameters before EOF. */ ret = -1; for (i = 0; i < ic->nb_streams; i++) { const char *errmsg; st = ic->streams[i]; /* if no packet was ever seen, update context now for has_codec_parameters */ if (!st->internal->avctx_inited) { if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->format == AV_SAMPLE_FMT_NONE) st->codecpar->format = st->internal->avctx->sample_fmt; ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar); if (ret < 0) goto find_stream_info_err; } if (!has_codec_parameters(st, &errmsg)) { char buf[256]; avcodec_string(buf, sizeof(buf), st->internal->avctx, 0); av_log(ic, AV_LOG_WARNING, "Could not find codec parameters for stream %d (%s): %s\n" "Consider increasing the value for the 'analyzeduration' and 'probesize' options\n", i, buf, errmsg); } else { ret = 0; } } compute_chapters_end(ic); /* update the stream parameters from the internal codec contexts */ for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; if (st->internal->avctx_inited) { int orig_w = st->codecpar->width; int orig_h = st->codecpar->height; ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx); if (ret < 0) goto find_stream_info_err; // The decoder might reduce the video size by the lowres factor. if (av_codec_get_lowres(st->internal->avctx) && orig_w) { st->codecpar->width = orig_w; st->codecpar->height = orig_h; } } #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS ret = avcodec_parameters_to_context(st->codec, st->codecpar); if (ret < 0) goto find_stream_info_err; // The old API (AVStream.codec) "requires" the resolution to be adjusted // by the lowres factor. if (av_codec_get_lowres(st->internal->avctx) && st->internal->avctx->width) { av_codec_set_lowres(st->codec, av_codec_get_lowres(st->internal->avctx)); st->codec->width = st->internal->avctx->width; st->codec->height = st->internal->avctx->height; } if (st->codec->codec_tag != MKTAG('t','m','c','d')) { st->codec->time_base = st->internal->avctx->time_base; st->codec->ticks_per_frame = st->internal->avctx->ticks_per_frame; } st->codec->framerate = st->avg_frame_rate; if (st->internal->avctx->subtitle_header) { st->codec->subtitle_header = av_malloc(st->internal->avctx->subtitle_header_size); if (!st->codec->subtitle_header) goto find_stream_info_err; st->codec->subtitle_header_size = st->internal->avctx->subtitle_header_size; memcpy(st->codec->subtitle_header, st->internal->avctx->subtitle_header, st->codec->subtitle_header_size); } // Fields unavailable in AVCodecParameters st->codec->coded_width = st->internal->avctx->coded_width; st->codec->coded_height = st->internal->avctx->coded_height; st->codec->properties = st->internal->avctx->properties; FF_ENABLE_DEPRECATION_WARNINGS #endif st->internal->avctx_inited = 0; } find_stream_info_err: for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; if (st->info) av_freep(&st->info->duration_error); av_freep(&ic->streams[i]->info); av_bsf_free(&ic->streams[i]->internal->extract_extradata.bsf); av_packet_free(&ic->streams[i]->internal->extract_extradata.pkt); } if (ic->pb) av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n", avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count); return ret; }
返回值大於等於 0 為正常執行,整個執行過程大概分為幾部:
-
聲明瞭
AVStream
結構體來儲存媒體流的資訊 -
通過
codec = find_probe_decoder(ic, st, st->codecpar->codec_id);
來查詢解碼器 -
通過
avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt)
來開啟解碼器 -
通過
read_frame_internal(ic, &pkt1)
讀取一幀 AVPacket 資料(A new stream can be added there if no header in file) -
通過
try_decode_frame(ic, st, pkt, (options && i < orig_nb_streams) ? &options[i] : NULL);
來解碼(If still no information, we try to open the codec and to decompress the frame. We try to avoid that in most cases asit takes longer and uses more memory. For MPEG-4, we need to decompress for QuickTime.)
find_probe_decoder
static const AVCodec *find_probe_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id) { const AVCodec *codec; #if CONFIG_H264_DECODER /* Other parts of the code assume this decoder to be used for h264, * so force it if possible. */ if (codec_id == AV_CODEC_ID_H264) return avcodec_find_decoder_by_name("h264"); #endif codec = find_decoder(s, st, codec_id); if (!codec) return NULL; if (codec->capabilities & AV_CODEC_CAP_AVOID_PROBING) { const AVCodec *probe_codec = NULL; while (probe_codec = av_codec_next(probe_codec)) { if (probe_codec->id == codec_id && av_codec_is_decoder(probe_codec) && !(probe_codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_EXPERIMENTAL))) { return probe_codec; } } } return codec; }
內部呼叫的是find_decoder()
:
static const AVCodec *find_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id) { #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS if (st->codec->codec) return st->codec->codec; FF_ENABLE_DEPRECATION_WARNINGS #endif switch (st->codecpar->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->video_codec)return s->video_codec; break; case AVMEDIA_TYPE_AUDIO: if (s->audio_codec)return s->audio_codec; break; case AVMEDIA_TYPE_SUBTITLE: if (s->subtitle_codec) return s->subtitle_codec; break; } return avcodec_find_decoder(codec_id); }
如果 AVStream 包含了解碼器,那麼返回對應的 AVCodec,如果沒有,則進到avcodec_find_decoder()
當中
avcodec_find_decoder
AVCodec *avcodec_find_decoder(enum AVCodecID id) { return find_encdec(id, 0); } static AVCodec *find_encdec(enum AVCodecID id, int encoder) { AVCodec *p, *experimental = NULL; p = first_avcodec; id= remap_deprecated_codec_id(id); while (p) { if ((encoder ? av_codec_is_encoder(p) : av_codec_is_decoder(p)) && p->id == id) { if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) { experimental = p; } else return p; } p = p->next; } return experimental; }
avcodec_find_decoder()
方法利用 AVCodecID 查詢 AVCodec,並將找到的 AVCodec 返回,其呼叫的是find_encdec()
方法,其中first_avcodec
便是註冊的時候的那個連結串列的頭,迴圈遍歷並比較輸入的 ID 和每一個編碼器的 ID,直到找到 ID 取值相等的編碼器;remap_deprecated_codec_id()
用於將一些過時的編碼器 ID 對映到新的編碼器 ID
avcodec_open2
avcodec_open2()
主要作用是初始化一個視音訊編解碼器的 AVCodecContext
read_frame_internal
av_read_frame()
內部實際上就是呼叫的read_frame_internal()
try_decode_frame
/* returns 1 or 0 if or if not decoded data was returned, or a negative error */ static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt, AVDictionary **options) { AVCodecContext *avctx = st->internal->avctx; const AVCodec *codec; int got_picture = 1, ret = 0; AVFrame *frame = av_frame_alloc(); AVSubtitle subtitle; AVPacket pkt = *avpkt; int do_skip_frame = 0; enum AVDiscard skip_frame; if (!frame) return AVERROR(ENOMEM); if (!avcodec_is_open(avctx) && st->info->found_decoder <= 0 && (st->codecpar->codec_id != -st->info->found_decoder || !st->codecpar->codec_id)) { AVDictionary *thread_opt = NULL; codec = find_probe_decoder(s, st, st->codecpar->codec_id); if (!codec) { st->info->found_decoder = -st->codecpar->codec_id; ret= -1; goto fail; } /* Force thread count to 1 since the H.264 decoder will not extract * SPS and PPS to extradata during multi-threaded decoding. */ av_dict_set(options ? options : &thread_opt, "threads", "1", 0); if (s->codec_whitelist) av_dict_set(options ? options : &thread_opt, "codec_whitelist", s->codec_whitelist, 0); ret = avcodec_open2(avctx, codec, options ? options : &thread_opt); if (!options) av_dict_free(&thread_opt); if (ret < 0) { st->info->found_decoder = -avctx->codec_id; goto fail; } st->info->found_decoder = 1; } else if (!st->info->found_decoder) st->info->found_decoder = 1; if (st->info->found_decoder < 0) { ret = -1; goto fail; } if (avpriv_codec_get_cap_skip_frame_fill_param(avctx->codec)) { do_skip_frame = 1; skip_frame = avctx->skip_frame; avctx->skip_frame = AVDISCARD_ALL; } while ((pkt.size > 0 || (!pkt.data && got_picture)) && ret >= 0 && (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) || (!st->codec_info_nb_frames && (avctx->codec->capabilities & AV_CODEC_CAP_CHANNEL_CONF)))) { got_picture = 0; if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) { ret = avcodec_send_packet(avctx, &pkt); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) break; if (ret >= 0) pkt.size = 0; ret = avcodec_receive_frame(avctx, frame); if (ret >= 0) got_picture = 1; if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) ret = 0; } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { ret = avcodec_decode_subtitle2(avctx, &subtitle, &got_picture, &pkt); if (ret >= 0) pkt.size = 0; } if (ret >= 0) { if (got_picture) st->nb_decoded_frames++; ret= got_picture; } } if (!pkt.data && !got_picture) ret = -1; fail: if (do_skip_frame) { avctx->skip_frame = skip_frame; } av_frame_free(&frame); return ret; }
-
判斷視音訊流的解碼器是否已經開啟,如果沒有開啟的話,先開啟相應的解碼器
find_probe_decoder
-
根據視音訊流型別的不同,呼叫不同的解碼函式進行解碼
-
如果是音視訊,呼叫
avcodec_send_packet()
avcodec_decode_video2() avcodec_decode_audio4()
-
字幕流呼叫
avcodec_decode_subtitle2()
-
如果是音視訊,呼叫
- 解碼的迴圈會一直持續下去直到滿足了 while() 的所有條件。
estimate_timings
static void estimate_timings(AVFormatContext *ic, int64_t old_offset) { int64_t file_size; /* get the file size, if possible */ if (ic->iformat->flags & AVFMT_NOFILE) { file_size = 0; } else { file_size = avio_size(ic->pb); file_size = FFMAX(0, file_size); } if ((!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts")) && file_size && (ic->pb->seekable & AVIO_SEEKABLE_NORMAL)) { /* get accurate estimate from the PTSes */ estimate_timings_from_pts(ic, old_offset); ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS; } else if (has_duration(ic)) { /* at least one component has timings - we use them for all * the components */ fill_all_stream_timings(ic); ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM; } else { /* less precise: use bitrate info */ estimate_timings_from_bit_rate(ic); ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE; } update_stream_timings(ic); { int i; AVStream av_unused *st; for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; av_log(ic, AV_LOG_TRACE, "stream %d: start_time: %0.3f duration: %0.3f\n", i, (double) st->start_time * av_q2d(st->time_base), (double) st->duration* av_q2d(st->time_base)); } av_log(ic, AV_LOG_TRACE, "format: start_time: %0.3f duration: %0.3f bitrate=%"PRId64" kb/s\n", (double) ic->start_time / AV_TIME_BASE, (double) ic->duration/ AV_TIME_BASE, (int64_t)ic->bit_rate / 1000); } }
estimate_timings()
位於avformat_find_stream_info()
最後面,用於估算 AVFormatContext 以及 AVStream 的時長 duration,有 3 種估算方法:
estimate_timings_from_pts() fill_all_stream_timings() estimate_timings_from_bit_rate()