FFmpeg解析之avformat_find_stream_info函数

avformat_find_stream_info 的主要作用就是:解析媒体文件并获取相关的流信息
整体的逻辑如下图所示:

/*** Read packets of a media file to get stream information. This* is useful for file formats with no headers such as MPEG. This* function also computes the real framerate in case of MPEG-2 repeat* frame mode.* The logical file position is not changed by this function;* examined packets may be buffered for later processing.** @param ic media file handle* @param options  If non-NULL, an ic.nb_streams long array of pointers to*                 dictionaries, where i-th member contains options for*                 codec corresponding to i-th stream.*                 On return each dictionary will be filled with options that were not found.* @return >=0 if OK, AVERROR_xxx on error** @note this function isn't guaranteed to open all the codecs, so*       options being non-empty at return is a perfectly normal behavior.** @todo Let the user decide somehow what information is needed so that*       we do not waste time getting stuff the user does not need.*//*** 从注释可知道,此方法通过读取若干 packet 包来获取流信息,这对于像 MPEG 这种没有 header 的格式比较有用。此函数也计算了像 MPEG-2 这种支持 repeat mode 的真实帧率* 这个函数不会修改逻辑文件位置,所读取到的 packet 会缓存起来供后面使用。*/
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;int64_t old_offset  = avio_tell(ic->pb);// new streams might appear, no options for thoseint 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) {//默认5*AV_TIME_BASE,针对flv和mpeg默认90*AV_TIME_BASE和7*AV_TIME_BASEmax_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);/*** -----【第一次循环遍历流】-------* 主要就是:初始化 avctx 的 time_base 等参数,初始化解析器,将编解码器的参数信息拷贝到编解码器上下文中,* 查找解码器、打开解码器*/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;//设置avctx->time_base}/* check if the caller has overridden the codec id */
#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGSif (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 stuffif (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE) && st->internal->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->internal->request_probe <= 0)st->internal->avctx_inited = 1;codec = find_probe_decoder(ic, st, st->codecpar->codec_id);//根据解码器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->internal->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_RATEic->streams[i]->internal->info->last_dts = AV_NOPTS_VALUE;
#endific->streams[i]->internal->info->fps_first_dts = AV_NOPTS_VALUE;ic->streams[i]->internal->info->fps_last_dts  = AV_NOPTS_VALUE;}//  对 stream 中的数据进行 probe,读取一定的数据到内存中read_size = 0;for (;;) {//-------------------------【第三个循环】------------------const AVPacket *pkt;int analyzed_all_streams;//  ff_check_interrupt 检测是否有用户层进行中断请求,if (ff_check_interrupt(&ic->interrupt_callback)) {ret = AVERROR_EXIT;av_log(ic, AV_LOG_DEBUG, "interrupted\n");break;}/* 再次对所有的 streams 进行一次遍历,检查 stream 中 codec 信息是否完整,* 如果还有没解析出的,那么 break 跳出当前 stream 的遍历(此时还没有跳出 for(;;) 的大循环)* 在确认了 has_codec 之后,根据各种可能出现的情况设置好 fps_analyze_framecount;之后,还有一系列判断是否应该 break 遍历。*//* check if one codec still needs to be handled */for (i = 0; i < ic->nb_streams; i++) {//-----------【第三个循环的第一个嵌套循环】---------------int fps_analyze_framecount = 20;int count;st = ic->streams[i];/*** has_codec_parameters:此函数检查 codec 信息是否完整*/if (!has_codec_parameters(st, NULL))break;/* 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. *///如果时基比较粗略(例如 mkv 通常的毫秒精度),我们需要分析更多帧才能可靠地获得正确的 fps,fps_analyze_framecount * 2if (av_q2d(st->time_base) > 0.0005)fps_analyze_framecount *= 2;if (!tb_unreliable(st->internal->avctx))//时间基不正常或不是 mpeg4/mpeg2/HEVC/AVC 影片,不需要分析,fps_analyze_framecount = 0fps_analyze_framecount = 0;if (ic->fps_probe_size >= 0)//设置了 fps_probe_size,则按 fps_probe_sizefps_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 */// 可变 fps 并且无法猜测真实 fpscount = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?st->internal->info->codec_info_duration_fields/2 :st->internal->info->duration_count;//未找到帧率的视频帧,并且 count < fps_analyze_framecount,则退出循环去估算视频帧          if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {if (count < fps_analyze_framecount)break;}// Look at the first 3 frames if there is evidence of frame delay// but the decoder delay is not set.//查看前 3 帧是否有帧延迟现象但未设置解码器延迟if (st->internal->info->frame_delay_evidence && count < 2 && st->internal->avctx->has_b_frames == 0)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;/* 如果循环能正常结束,说明流信息的探测完毕,此时 i == ic->nb_streams;* 如果中间 break 了,说明某个流的信息还没有完全得到,此时 i < ic->nb_streams, 需继续探测*/if (!missing_streams || !*missing_streams)if (i == ic->nb_streams) {// 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. */// 如果是像 MPEG 这种没有头的封装格式,需要解析更多的 packets; 否则 breakif (!(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. *///虽然流信息还没完全探测出来,但是 read_size >= probesize, 已读取到的大小超过了 probesize,则退出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]->internal->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). */// 读取 packet ret = read_frame_internal(ic, &pkt1);if (ret == AVERROR(EAGAIN))continue;if (ret < 0) {/* EOF or error*/eof_reached = 1;break;}if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) {ret = avpriv_packet_list_put(&ic->internal->packet_buffer,&ic->internal->packet_buffer_end,&pkt1, NULL, 0);if (ret < 0)goto unref_then_goto_end;pkt = &ic->internal->packet_buffer_end->pkt;} else {pkt = &pkt1;}st = ic->streams[pkt->stream_index];//更新 read_size, read_size 用于下一轮判断是否已经超过 probesizeif (!(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 unref_then_goto_end;st->internal->avctx_inited = 1;}if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {/* check for non-increasing dts */if (st->internal->info->fps_last_dts != AV_NOPTS_VALUE &&st->internal->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->internal->info->fps_last_dts_idx,st->internal->info->fps_last_dts, st->codec_info_nb_frames,pkt->dts);st->internal->info->fps_first_dts =st->internal->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->internal->info->fps_last_dts != AV_NOPTS_VALUE &&st->internal->info->fps_last_dts_idx > st->internal->info->fps_first_dts_idx &&(pkt->dts - (uint64_t)st->internal->info->fps_last_dts) / 1000 >(st->internal->info->fps_last_dts     - (uint64_t)st->internal->info->fps_first_dts) /(st->internal->info->fps_last_dts_idx - st->internal->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->internal->info->fps_last_dts_idx,st->internal->info->fps_last_dts, st->codec_info_nb_frames,pkt->dts);st->internal->info->fps_first_dts =st->internal->info->fps_last_dts  = AV_NOPTS_VALUE;}/* update stored dts values */if (st->internal->info->fps_first_dts == AV_NOPTS_VALUE) {st->internal->info->fps_first_dts     = pkt->dts;st->internal->info->fps_first_dts_idx = st->codec_info_nb_frames;}st->internal->info->fps_last_dts     = pkt->dts;st->internal->info->fps_last_dts_idx = st->codec_info_nb_frames;}if (st->codec_info_nb_frames>1) {int64_t t = 0;int64_t limit;//下面计算已经读取到的时间长度//codec_info_duration:已经读取到的 packet 的总时长if (st->time_base.den > 0)t = av_rescale_q(st->internal->info->codec_info_duration, st->time_base, AV_TIME_BASE_Q);//t = 已经读取到的帧数/帧率    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));//根据 fps_last_dts - fps_first_dts 来计算if (   t == 0&& st->codec_info_nb_frames>30&& st->internal->info->fps_first_dts != AV_NOPTS_VALUE&& st->internal->info->fps_last_dts  != AV_NOPTS_VALUE)t = FFMAX(t, av_rescale_q(st->internal->info->fps_last_dts - st->internal->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;else                                                     limit = max_stream_analyze_duration;// 当前读取到的 packet 总时长 >= limit,则 breakif (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(&pkt1);break;}// 更新已经读取到的 packet 的总时长if (pkt->duration) {if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && pkt->pts != AV_NOPTS_VALUE && st->start_time != AV_NOPTS_VALUE && pkt->pts >= st->start_time) {st->internal->info->codec_info_duration = FFMIN(pkt->pts - st->start_time, st->internal->info->codec_info_duration + pkt->duration);} elsest->internal->info->codec_info_duration += pkt->duration;st->internal->info->codec_info_duration_fields += st->parser && st->need_parsing && avctx->ticks_per_frame ==2 ? st->parser->repeat_pict + 1 : 2;}}if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
#if FF_API_R_FRAME_RATEff_rfps_add_frame(ic, st, pkt->dts);
#endifif (pkt->dts != pkt->pts && pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE)st->internal->info->frame_delay_evidence = 1;}if (!st->internal->avctx->extradata) {ret = extract_extradata(st, pkt);if (ret < 0)goto unref_then_goto_end;}/* 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(&pkt1);//已经探测的帧数 + 1,count 总数 + 1st->codec_info_nb_frames++;count++;}// 是否已经到达文件尾部if (eof_reached) {int stream_index;// 再遍历一次流,检查编码器参数,如果参数完整会再次调用 avcodec_open2for (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);}}}/* * 刷新解码器* 有一些帧可能在缓存区,需要 flush*/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->internal->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);}}}}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->internal->info->codec_info_duration_fields &&!st->avg_frame_rate.num &&st->internal->info->codec_info_duration) {int best_fps      = 0;double best_error = 0.01;AVRational codec_frame_rate = avctx->framerate;if (st->internal->info->codec_info_duration        >= INT64_MAX / st->time_base.num / 2||st->internal->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||st->internal->info->codec_info_duration        < 0)continue;av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,st->internal->info->codec_info_duration_fields * (int64_t) st->time_base.den,st->internal->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 * (uint64_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->internal->display_aspect_ratio.num && st->internal->display_aspect_ratio.den) {AVRational hw_ratio = { avctx->height, avctx->width };st->sample_aspect_ratio = av_mul_q(st->internal->display_aspect_ratio,hw_ratio);}} else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {// 对于音频流,基于音频流服务类型初始化 dispositionif (!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 typeswitch (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' (%"PRId64") and 'probesize' (%"PRId64") options\n",i, buf, errmsg, ic->max_analyze_duration, ic->probesize);} else {ret = 0;}}ret = compute_chapters_end(ic);if (ret < 0)goto find_stream_info_err;// 更新 stream 各个结构的数据/* 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;ret = add_coded_side_data(st, st->internal->avctx);if (ret < 0)goto find_stream_info_err;
#if FF_API_LOWRES// The decoder might reduce the video size by the lowres factor.if (st->internal->avctx->lowres && orig_w) {st->codecpar->width = orig_w;st->codecpar->height = orig_h;}
#endif}#if FF_API_LAVF_AVCTX
FF_DISABLE_DEPRECATION_WARNINGSret = avcodec_parameters_to_context(st->codec, st->codecpar);if (ret < 0)goto find_stream_info_err;#if FF_API_LOWRES// The old API (AVStream.codec) "requires" the resolution to be adjusted// by the lowres factor.if (st->internal->avctx->lowres && st->internal->avctx->width) {st->codec->lowres = st->internal->avctx->lowres;st->codec->width = st->internal->avctx->width;st->codec->height = st->internal->avctx->height;}
#endifif (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 AVCodecParametersst->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
#endifst->internal->avctx_inited = 0;}find_stream_info_err:for (i = 0; i < ic->nb_streams; i++) {st = ic->streams[i];if (st->internal->info)av_freep(&st->internal->info->duration_error);avcodec_close(ic->streams[i]->internal->avctx);av_freep(&ic->streams[i]->internal->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;unref_then_goto_end:av_packet_unref(&pkt1);goto find_stream_info_err;
}
// 检查当前的音视频流信息是否完整
static int has_codec_parameters(AVStream *st, const char **errmsg_ptr)
{AVCodecContext *avctx = st->internal->avctx;#define FAIL(errmsg) do {                                         \if (errmsg_ptr)                                           \*errmsg_ptr = errmsg;                                 \return 0;                                                 \} while (0)if (   avctx->codec_id == AV_CODEC_ID_NONE&& avctx->codec_type != AVMEDIA_TYPE_DATA)FAIL("unknown codec");switch (avctx->codec_type) {case AVMEDIA_TYPE_AUDIO://音频的需要检测frame_size、sample_fmt、sample_rate、channels等if (!avctx->frame_size && determinable_frame_size(avctx))FAIL("unspecified frame size");if (st->internal->info->found_decoder >= 0 &&avctx->sample_fmt == AV_SAMPLE_FMT_NONE)FAIL("unspecified sample format");if (!avctx->sample_rate)FAIL("unspecified sample rate");if (!avctx->channels)FAIL("unspecified number of channels");if (st->internal->info->found_decoder >= 0 && !st->internal->nb_decoded_frames && avctx->codec_id == AV_CODEC_ID_DTS)FAIL("no decodable DTS frames");break;case AVMEDIA_TYPE_VIDEO://视频的需要检测width、pix_fmt、sample_aspect_ratio等if (!avctx->width)FAIL("unspecified size");if (st->internal->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE)FAIL("unspecified pixel format");if (st->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40)if (!st->sample_aspect_ratio.num && !st->codecpar->sample_aspect_ratio.num && !st->codec_info_nb_frames)FAIL("no frame in rv30/40 and no sar");break;case AVMEDIA_TYPE_SUBTITLE:if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width)FAIL("unspecified size");break;case AVMEDIA_TYPE_DATA:if (avctx->codec_id == AV_CODEC_ID_NONE) return 1;}return 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/700293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

聊聊JVM运行时数据区的堆内存

聊聊JVM运行时数据区的堆内存 内存模型变迁&#xff1a; Java堆在JVM启动时创建内存区域去实现对象、数组与运行时常量的内存分配&#xff0c;它是虚拟机管理最大的&#xff0c;也是垃圾回收的主要内存区域 。 内存模型变迁&#xff1a; 为什么要有年轻区和老年区&#xff1f;…

【算法与数据结构】链表、哈希表、栈和队列、二叉树(笔记二)

文章目录 四、链表理论五、哈希表理论五、栈和队列理论5.1 单调栈 六、二叉树理论6.1 树的定义6.2 二叉树的存储方式6.3 二叉树的遍历方式6.4 高度和深度 最近博主学习了算法与数据结构的一些视频&#xff0c;在这个文章做一些笔记和心得&#xff0c;本篇文章就写了一些基础算法…

基于AI将普通RGB图像转换为苹果Vision Pro支持的空间照片

将 RGB 图像转换为空间图片 一、引言 随着AR和VR技术的普及,空间照片格式(.HEIC)逐渐受到关注。这种格式允许用户在AR/VR设备上体验到更为真实的立体空间效果。为了让更多的普通图片也能享受这种技术,我们开发了这款可以将普通RGB图像转换为苹果Vision Pro支持的.HEIC格式的…

STM32F103学习笔记(七) PWR电源管理(原理篇)

目录 1. PWR电源管理简介 2. STM32F103的PWR模块概述 2.1 PWR模块的基本工作原理 2.2 电源管理的功能和特点 3. PWR模块的常见应用场景 4. 常见问题与解决方案 1. PWR电源管理简介 PWR&#xff08;Power&#xff09;模块是STM32F103系列微控制器中的一个重要组成部分&…

实习日志18

1.刚弄好数据库就破大防了 1.1.图片显示&#xff0c;PDF不显示 我的图片是base64编码显示&#xff0c;pdf是用url显示 首先想到url出问题了 感觉可能是之前的那个问题&#xff0c;到服务器上url变回去了 活字格V9获取图片失败bug&#xff0c;报错404&#xff0c;了解存储路…

使用Streamlit构建纯LLM Chatbot WebUI傻瓜教程

文章目录 使用Streamlit构建纯LLM Chatbot WebUI傻瓜教程开发环境hello Streatelit显示DataFrame数据显示地图WebUI左右布局设置st.sidebar左侧布局st.columns右侧布局 大语言模型LLM Chatbot WebUI设置Chatbot页面布局showdataframe()显示dataframeshowLineChart()显示折线图s…

进程间的通信-- 管道

一 进程通信原理 我们知道进程间相互独立&#xff0c;具有独立性。那么我们要实现两个进程之间的通信就需要&#xff0c;让这两个进程看到同一个文件。然后一个进程对文件写入&#xff0c;一个进程对文件内容进行读取&#xff0c;这就是现实了进程间的通信。 二 进程通信的几种…

【C++私房菜】面向对象中的多态

文章目录 一、多态二、对象的静态类型和动态类型三、虚函数和纯虚函数1、虚函数2、虚析构函数3、抽象基类和纯虚函数4、多态的原理 四、重载、覆盖(重写)、隐藏(重定义)的对比 一、多态 OOP的核心思想是多态性(polymorphism)。多态性这个词源自希腊语&#xff0c;其含义是“多…

【黑马程序员】1、TypeScript介绍_黑马程序员前端TypeScript教程,TypeScript零基础入门到实战全套教程

课程地址&#xff1a;【黑马程序员前端TypeScript教程&#xff0c;TypeScript零基础入门到实战全套教程】 https://www.bilibili.com/video/BV14Z4y1u7pi/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 1、TypeScript介绍 1.1 TypeScript是什…

信号通信与消息队列实现的通信:2024/2/23

作业1&#xff1a;将信号和消息队列的课堂代码敲一遍 1.1 信号 1.1.1 信号默认、捕获、忽略处理(普通信号) 代码&#xff1a; #include <myhead.h> void handler(int signo) {if(signoSIGINT){printf("用户键入 ctrlc\n");} } int main(int argc, const ch…

Windows Server 2019 IIS HTTPS证书部署流程详解

一、下载SSL证书 1、下载IIS 类型的证书 以阿里云证书为例&#xff1a; 2、解压已下载的SSL证书压缩包 二、导入SSL证书 1、在服务器上使用WinR组合键&#xff0c;打开运行对话框&#xff0c;输入mmc&#xff0c;单击确定 打开控制台操作界面&#xff0c;如下&#xff1a; …

可视化 RAG 数据 — EDA for Retrieval-Augmented Generation

目录 一、说明 二、准备好 三、准备文件 四、拆分和创建数据集的嵌入 五、构建 LangChain 六、问一个问题 七、可视化 八、下一步是什么&#xff1f; 九、引用 一、说明 像 GPT-4 这样的大型语言模型 &#xff08;LLM&#xff09; 在文本理解和生成方面表现出令人印象深刻的能力…

介绍 CI / CD

目录 一、介绍 CI / CD 1、为什么要 CI / CD 方法简介 1、持续集成 2、持续交付 3、持续部署 2、GitLab CI / CD简介 3、GitLab CI / CD 的工作原理 4、基本CI / CD工作流程 5、首次设置 GitLab CI / CD 6、GitLab CI / CD功能集 一、介绍 CI / CD 在本文档中&#x…

Python中format()方法的基本使用,第一种用法 <模板字符串>.format(<参数列表>)。

第一种用法&#xff1a; <模板字符串>.format(<参数列表>) 解析&#xff1a; 其中&#xff1a; <模板字符串>是包含占位符或者叫槽&#xff08;用花括号 {} 表示&#xff09;的字符串&#xff0c;用来指定最终格式化后的字符串的样式和结构。<参数列表…

STM32 系统滴答时钟启动过程 SysTick_Config

STM32 系统滴答时钟启动过程 SysTick_Config 1. 系统滴答时钟1.1 简介1.2 配置1.3 启动和更新 1. 系统滴答时钟 1.1 简介 SysTick&#xff1a;系统滴答时钟&#xff0c;属于Cortex-M4内核中的一个外设&#xff0c;24bit向下递减计数。 Systick定时器常用来做延时&#xff0c;…

二次元风格个人主页HTML源码

源码介绍 直接上传服务器压缩包解压就完事了&#xff0c;修改index.html内代码即可&#xff0c;注释写的很全&#xff0c;替换图片在文件夹img&#xff0c;只有前端&#xff0c;没有后台&#xff0c;大佬如果需要&#xff0c;可以自行添加后台。本源码非常适合个人工作室主页。…

CMake管理CUDA并使用cuSOLVER等

一、出现问题 我在使用官方案例的时候&#xff0c;使用VS2022CMake管理编译的时候出现如下的错误&#xff1a; 官方CMakeLists.txt&#xff1a; cmake_minimum_required(VERSION 3.9)set(ROUTINE bicgstab)project("${ROUTINE}_example"DESCRIPTION "GPU-Acce…

在Linux服务器上部署一个单机项目

目录 一、jdk安装 二、tomcat安装 三、MySQL安装 四、部署项目 一、jdk安装 1. 上传jdk安装包 jdk-8u151-linux-x64.tar.gz 进入opt目录&#xff0c;将安装包拖进去 2. 解压安装包 这里需要解压到usr/local目录下&#xff0c;在这里我新建一个文件夹保存解压后的文件 [r…

使用 ES|QL 优化可观察性:简化 Kubernetes 和 OTel 的 SRE 操作和问题解决

作者&#xff1a;Bahubali Shetti 作为一名运营工程师&#xff08;SRE、IT 运营、DevOps&#xff09;&#xff0c;管理技术和数据蔓延是一项持续的挑战。 简单地管理大量高维和高基数数据是令人难以承受的。 作为单一平台&#xff0c;Elastic 帮助 SRE 将无限的遥测数据&#…

责任链模式与spring容器的搭配应用

背景 有个需求&#xff0c;原先只涉及到一种A情况设备的筛选&#xff0c;每次筛选会经过多个流程&#xff0c;比如先a功能&#xff0c;a功能通过再筛选b功能&#xff0c;然后再筛选c功能&#xff0c;以此类推。现在新增了另外一种B情况的筛选&#xff0c;B情况同样需要A情况的筛…