(转)FFmpeg源代码简单分析:avformat_find_stream_info()

时间:2022-09-20 10:42:51
目录(?)[+]

=====================================================

FFmpeg的库函数源代码分析文章列表:

【架构图】

FFmpeg源代码结构图 - 解码

FFmpeg源代码结构图 - 编码

【通用】

FFmpeg 源代码简单分析:av_register_all()

FFmpeg 源代码简单分析:avcodec_register_all()

FFmpeg 源代码简单分析:内存的分配和释放(av_malloc()、av_free()等)

FFmpeg 源代码简单分析:常见结构体的初始化和销毁(AVFormatContext,AVFrame等)

FFmpeg 源代码简单分析:avio_open2()

FFmpeg 源代码简单分析:av_find_decoder()和av_find_encoder()

FFmpeg 源代码简单分析:avcodec_open2()

FFmpeg 源代码简单分析:avcodec_close()

【解码】

图解FFMPEG打开媒体的函数avformat_open_input

FFmpeg 源代码简单分析:avformat_open_input()

FFmpeg 源代码简单分析:avformat_find_stream_info()

FFmpeg 源代码简单分析:av_read_frame()

FFmpeg 源代码简单分析:avcodec_decode_video2()

FFmpeg 源代码简单分析:avformat_close_input()

【编码】

FFmpeg 源代码简单分析:avformat_alloc_output_context2()

FFmpeg 源代码简单分析:avformat_write_header()

FFmpeg 源代码简单分析:avcodec_encode_video()

FFmpeg 源代码简单分析:av_write_frame()

FFmpeg 源代码简单分析:av_write_trailer()

【其它】

FFmpeg源代码简单分析:日志输出系统(av_log()等)

FFmpeg源代码简单分析:结构体成员管理系统-AVClass

FFmpeg源代码简单分析:结构体成员管理系统-AVOption

FFmpeg源代码简单分析:libswscale的sws_getContext()

FFmpeg源代码简单分析:libswscale的sws_scale()

FFmpeg源代码简单分析:libavdevice的avdevice_register_all()

FFmpeg源代码简单分析:libavdevice的gdigrab

【脚本】

FFmpeg源代码简单分析:makefile

FFmpeg源代码简单分析:configure

【H.264】

FFmpeg的H.264解码器源代码简单分析:概述

=====================================================

本文简单分析FFmpeg中一个常用的函数:avformat_find_stream_info()。该函数可以读取一部分视音频数据并且获得一些相关的信息。avformat_find_stream_info()的声明位于libavformat\avformat.h,如下所示。

  1. /**
  2. * Read packets of a media file to get stream information. This
  3. * is useful for file formats with no headers such as MPEG. This
  4. * function also computes the real framerate in case of MPEG-2 repeat
  5. * frame mode.
  6. * The logical file position is not changed by this function;
  7. * examined packets may be buffered for later processing.
  8. *
  9. * @param ic media file handle
  10. * @param options  If non-NULL, an ic.nb_streams long array of pointers to
  11. *                 dictionaries, where i-th member contains options for
  12. *                 codec corresponding to i-th stream.
  13. *                 On return each dictionary will be filled with options that were not found.
  14. * @return >=0 if OK, AVERROR_xxx on error
  15. *
  16. * @note this function isn't guaranteed to open all the codecs, so
  17. *       options being non-empty at return is a perfectly normal behavior.
  18. *
  19. * @todo Let the user decide somehow what information is needed so that
  20. *       we do not waste time getting stuff the user does not need.
  21. */
  22. int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

简单解释一下它的参数的含义:

ic:输入的AVFormatContext。
options:额外的选项,目前没有深入研究过。

函数正常执行后返回值大于等于0。
该函数最典型的例子可以参考:最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)

PS:由于该函数比较复杂,所以只看了一部分代码,以后有时间再进一步分析。

函数调用关系图

函数的调用关系如下图所示。

(转)FFmpeg源代码简单分析:avformat_find_stream_info()
 

avformat_find_stream_info()

avformat_find_stream_info()的定义位于libavformat\utils.c。它的代码比较长,如下所示。

  1. int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
  2. {
  3. int i, count, ret = 0, j;
  4. int64_t read_size;
  5. AVStream *st;
  6. AVPacket pkt1, *pkt;
  7. int64_t old_offset  = avio_tell(ic->pb);
  8. // new streams might appear, no options for those
  9. int orig_nb_streams = ic->nb_streams;
  10. int flush_codecs;
  11. int64_t max_analyze_duration = ic->max_analyze_duration2;
  12. int64_t probesize = ic->probesize2;
  13. if (!max_analyze_duration)
  14. max_analyze_duration = ic->max_analyze_duration;
  15. if (ic->probesize)
  16. probesize = ic->probesize;
  17. flush_codecs = probesize > 0;
  18. av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);
  19. if (!max_analyze_duration) {
  20. if (!strcmp(ic->iformat->name, "flv") && !(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
  21. max_analyze_duration = 10*AV_TIME_BASE;
  22. } else
  23. max_analyze_duration = 5*AV_TIME_BASE;
  24. }
  25. if (ic->pb)
  26. av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d\n",
  27. avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count);
  28. for (i = 0; i < ic->nb_streams; i++) {
  29. const AVCodec *codec;
  30. AVDictionary *thread_opt = NULL;
  31. st = ic->streams[i];
  32. if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
  33. st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
  34. /*            if (!st->time_base.num)
  35. st->time_base = */
  36. if (!st->codec->time_base.num)
  37. st->codec->time_base = st->time_base;
  38. }
  39. // only for the split stuff
  40. if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE)) {
  41. st->parser = av_parser_init(st->codec->codec_id);
  42. if (st->parser) {
  43. if (st->need_parsing == AVSTREAM_PARSE_HEADERS) {
  44. st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
  45. } else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW) {
  46. st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
  47. }
  48. } else if (st->need_parsing) {
  49. av_log(ic, AV_LOG_VERBOSE, "parser not found for codec "
  50. "%s, packets or times may be invalid.\n",
  51. avcodec_get_name(st->codec->codec_id));
  52. }
  53. }
  54. codec = find_decoder(ic, st, st->codec->codec_id);
  55. /* Force thread count to 1 since the H.264 decoder will not extract
  56. * SPS and PPS to extradata during multi-threaded decoding. */
  57. av_dict_set(options ? &options[i] : &thread_opt, "threads", "1", 0);
  58. if (ic->codec_whitelist)
  59. av_dict_set(options ? &options[i] : &thread_opt, "codec_whitelist", ic->codec_whitelist, 0);
  60. /* Ensure that subtitle_header is properly set. */
  61. if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE
  62. && codec && !st->codec->codec) {
  63. if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0)
  64. av_log(ic, AV_LOG_WARNING,
  65. "Failed to open codec in av_find_stream_info\n");
  66. }
  67. // Try to just open decoders, in case this is enough to get parameters.
  68. if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) {
  69. if (codec && !st->codec->codec)
  70. if (avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt) < 0)
  71. av_log(ic, AV_LOG_WARNING,
  72. "Failed to open codec in av_find_stream_info\n");
  73. }
  74. if (!options)
  75. av_dict_free(&thread_opt);
  76. }
  77. for (i = 0; i < ic->nb_streams; i++) {
  78. #if FF_API_R_FRAME_RATE
  79. ic->streams[i]->info->last_dts = AV_NOPTS_VALUE;
  80. #endif
  81. ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE;
  82. ic->streams[i]->info->fps_last_dts  = AV_NOPTS_VALUE;
  83. }
  84. count     = 0;
  85. read_size = 0;
  86. for (;;) {
  87. if (ff_check_interrupt(&ic->interrupt_callback)) {
  88. ret = AVERROR_EXIT;
  89. av_log(ic, AV_LOG_DEBUG, "interrupted\n");
  90. break;
  91. }
  92. /* check if one codec still needs to be handled */
  93. for (i = 0; i < ic->nb_streams; i++) {
  94. int fps_analyze_framecount = 20;
  95. st = ic->streams[i];
  96. if (!has_codec_parameters(st, NULL))
  97. break;
  98. /* If the timebase is coarse (like the usual millisecond precision
  99. * of mkv), we need to analyze more frames to reliably arrive at
  100. * the correct fps. */
  101. if (av_q2d(st->time_base) > 0.0005)
  102. fps_analyze_framecount *= 2;
  103. if (!tb_unreliable(st->codec))
  104. fps_analyze_framecount = 0;
  105. if (ic->fps_probe_size >= 0)
  106. fps_analyze_framecount = ic->fps_probe_size;
  107. if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
  108. fps_analyze_framecount = 0;
  109. /* variable fps and no guess at the real fps */
  110. if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
  111. st->info->duration_count < fps_analyze_framecount &&
  112. st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  113. break;
  114. if (st->parser && st->parser->parser->split &&
  115. !st->codec->extradata)
  116. break;
  117. if (st->first_dts == AV_NOPTS_VALUE &&
  118. !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
  119. st->codec_info_nb_frames < ic->max_ts_probe &&
  120. (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
  121. st->codec->codec_type == AVMEDIA_TYPE_AUDIO))
  122. break;
  123. }
  124. if (i == ic->nb_streams) {
  125. /* NOTE: If the format has no header, then we need to read some
  126. * packets to get most of the streams, so we cannot stop here. */
  127. if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
  128. /* If we found the info for all the codecs, we can stop. */
  129. ret = count;
  130. av_log(ic, AV_LOG_DEBUG, "All info found\n");
  131. flush_codecs = 0;
  132. break;
  133. }
  134. }
  135. /* We did not get all the codec info, but we read too much data. */
  136. if (read_size >= probesize) {
  137. ret = count;
  138. av_log(ic, AV_LOG_DEBUG,
  139. "Probe buffer size limit of %"PRId64" bytes reached\n", probesize);
  140. for (i = 0; i < ic->nb_streams; i++)
  141. if (!ic->streams[i]->r_frame_rate.num &&
  142. ic->streams[i]->info->duration_count <= 1 &&
  143. ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
  144. strcmp(ic->iformat->name, "image2"))
  145. av_log(ic, AV_LOG_WARNING,
  146. "Stream #%d: not enough frames to estimate rate; "
  147. "consider increasing probesize\n", i);
  148. break;
  149. }
  150. /* NOTE: A new stream can be added there if no header in file
  151. * (AVFMTCTX_NOHEADER). */
  152. ret = read_frame_internal(ic, &pkt1);
  153. if (ret == AVERROR(EAGAIN))
  154. continue;
  155. if (ret < 0) {
  156. /* EOF or error*/
  157. break;
  158. }
  159. if (ic->flags & AVFMT_FLAG_NOBUFFER)
  160. free_packet_buffer(&ic->packet_buffer, &ic->packet_buffer_end);
  161. {
  162. pkt = add_to_pktbuf(&ic->packet_buffer, &pkt1,
  163. &ic->packet_buffer_end);
  164. if (!pkt) {
  165. ret = AVERROR(ENOMEM);
  166. goto find_stream_info_err;
  167. }
  168. if ((ret = av_dup_packet(pkt)) < 0)
  169. goto find_stream_info_err;
  170. }
  171. st = ic->streams[pkt->stream_index];
  172. if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
  173. read_size += pkt->size;
  174. if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {
  175. /* check for non-increasing dts */
  176. if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
  177. st->info->fps_last_dts >= pkt->dts) {
  178. av_log(ic, AV_LOG_DEBUG,
  179. "Non-increasing DTS in stream %d: packet %d with DTS "
  180. "%"PRId64", packet %d with DTS %"PRId64"\n",
  181. st->index, st->info->fps_last_dts_idx,
  182. st->info->fps_last_dts, st->codec_info_nb_frames,
  183. pkt->dts);
  184. st->info->fps_first_dts =
  185. st->info->fps_last_dts  = AV_NOPTS_VALUE;
  186. }
  187. /* Check for a discontinuity in dts. If the difference in dts
  188. * is more than 1000 times the average packet duration in the
  189. * sequence, we treat it as a discontinuity. */
  190. if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
  191. st->info->fps_last_dts_idx > st->info->fps_first_dts_idx &&
  192. (pkt->dts - st->info->fps_last_dts) / 1000 >
  193. (st->info->fps_last_dts     - st->info->fps_first_dts) /
  194. (st->info->fps_last_dts_idx - st->info->fps_first_dts_idx)) {
  195. av_log(ic, AV_LOG_WARNING,
  196. "DTS discontinuity in stream %d: packet %d with DTS "
  197. "%"PRId64", packet %d with DTS %"PRId64"\n",
  198. st->index, st->info->fps_last_dts_idx,
  199. st->info->fps_last_dts, st->codec_info_nb_frames,
  200. pkt->dts);
  201. st->info->fps_first_dts =
  202. st->info->fps_last_dts  = AV_NOPTS_VALUE;
  203. }
  204. /* update stored dts values */
  205. if (st->info->fps_first_dts == AV_NOPTS_VALUE) {
  206. st->info->fps_first_dts     = pkt->dts;
  207. st->info->fps_first_dts_idx = st->codec_info_nb_frames;
  208. }
  209. st->info->fps_last_dts     = pkt->dts;
  210. st->info->fps_last_dts_idx = st->codec_info_nb_frames;
  211. }
  212. if (st->codec_info_nb_frames>1) {
  213. int64_t t = 0;
  214. if (st->time_base.den > 0)
  215. t = av_rescale_q(st->info->codec_info_duration, st->time_base, AV_TIME_BASE_Q);
  216. if (st->avg_frame_rate.num > 0)
  217. t = FFMAX(t, av_rescale_q(st->codec_info_nb_frames, av_inv_q(st->avg_frame_rate), AV_TIME_BASE_Q));
  218. if (   t == 0
  219. && st->codec_info_nb_frames>30
  220. && st->info->fps_first_dts != AV_NOPTS_VALUE
  221. && st->info->fps_last_dts  != AV_NOPTS_VALUE)
  222. t = FFMAX(t, av_rescale_q(st->info->fps_last_dts - st->info->fps_first_dts, st->time_base, AV_TIME_BASE_Q));
  223. if (t >= max_analyze_duration) {
  224. av_log(ic, AV_LOG_VERBOSE, "max_analyze_duration %"PRId64" reached at %"PRId64" microseconds\n",
  225. max_analyze_duration,
  226. t);
  227. if (ic->flags & AVFMT_FLAG_NOBUFFER)
  228. av_packet_unref(pkt);
  229. break;
  230. }
  231. if (pkt->duration) {
  232. st->info->codec_info_duration        += pkt->duration;
  233. st->info->codec_info_duration_fields += st->parser && st->need_parsing && st->codec->ticks_per_frame ==2 ? st->parser->repeat_pict + 1 : 2;
  234. }
  235. }
  236. #if FF_API_R_FRAME_RATE
  237. if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  238. ff_rfps_add_frame(ic, st, pkt->dts);
  239. #endif
  240. if (st->parser && st->parser->parser->split && !st->codec->extradata) {
  241. int i = st->parser->parser->split(st->codec, pkt->data, pkt->size);
  242. if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) {
  243. if (ff_alloc_extradata(st->codec, i))
  244. return AVERROR(ENOMEM);
  245. memcpy(st->codec->extradata, pkt->data,
  246. st->codec->extradata_size);
  247. }
  248. }
  249. /* If still no information, we try to open the codec and to
  250. * decompress the frame. We try to avoid that in most cases as
  251. * it takes longer and uses more memory. For MPEG-4, we need to
  252. * decompress for QuickTime.
  253. *
  254. * If CODEC_CAP_CHANNEL_CONF is set this will force decoding of at
  255. * least one frame of codec data, this makes sure the codec initializes
  256. * the channel configuration and does not only trust the values from
  257. * the container. */
  258. try_decode_frame(ic, st, pkt,
  259. (options && i < orig_nb_streams) ? &options[i] : NULL);
  260. if (ic->flags & AVFMT_FLAG_NOBUFFER)
  261. av_packet_unref(pkt);
  262. st->codec_info_nb_frames++;
  263. count++;
  264. }
  265. if (flush_codecs) {
  266. AVPacket empty_pkt = { 0 };
  267. int err = 0;
  268. av_init_packet(&empty_pkt);
  269. for (i = 0; i < ic->nb_streams; i++) {
  270. st = ic->streams[i];
  271. /* flush the decoders */
  272. if (st->info->found_decoder == 1) {
  273. do {
  274. err = try_decode_frame(ic, st, &empty_pkt,
  275. (options && i < orig_nb_streams)
  276. ? &options[i] : NULL);
  277. } while (err > 0 && !has_codec_parameters(st, NULL));
  278. if (err < 0) {
  279. av_log(ic, AV_LOG_INFO,
  280. "decoding for stream %d failed\n", st->index);
  281. }
  282. }
  283. }
  284. }
  285. // close codecs which were opened in try_decode_frame()
  286. for (i = 0; i < ic->nb_streams; i++) {
  287. st = ic->streams[i];
  288. avcodec_close(st->codec);
  289. }
  290. ff_rfps_calculate(ic);
  291. for (i = 0; i < ic->nb_streams; i++) {
  292. st = ic->streams[i];
  293. if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
  294. if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_coded_sample) {
  295. uint32_t tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
  296. if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == st->codec->pix_fmt)
  297. st->codec->codec_tag= tag;
  298. }
  299. /* estimate average framerate if not set by demuxer */
  300. if (st->info->codec_info_duration_fields &&
  301. !st->avg_frame_rate.num &&
  302. st->info->codec_info_duration) {
  303. int best_fps      = 0;
  304. double best_error = 0.01;
  305. if (st->info->codec_info_duration        >= INT64_MAX / st->time_base.num / 2||
  306. st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
  307. st->info->codec_info_duration        < 0)
  308. continue;
  309. av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
  310. st->info->codec_info_duration_fields * (int64_t) st->time_base.den,
  311. st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);
  312. /* Round guessed framerate to a "standard" framerate if it's
  313. * within 1% of the original estimate. */
  314. for (j = 0; j < MAX_STD_TIMEBASES; j++) {
  315. AVRational std_fps = { get_std_framerate(j), 12 * 1001 };
  316. double error       = fabs(av_q2d(st->avg_frame_rate) /
  317. av_q2d(std_fps) - 1);
  318. if (error < best_error) {
  319. best_error = error;
  320. best_fps   = std_fps.num;
  321. }
  322. }
  323. if (best_fps)
  324. av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
  325. best_fps, 12 * 1001, INT_MAX);
  326. }
  327. if (!st->r_frame_rate.num) {
  328. if (    st->codec->time_base.den * (int64_t) st->time_base.num
  329. <= st->codec->time_base.num * st->codec->ticks_per_frame * (int64_t) st->time_base.den) {
  330. st->r_frame_rate.num = st->codec->time_base.den;
  331. st->r_frame_rate.den = st->codec->time_base.num * st->codec->ticks_per_frame;
  332. } else {
  333. st->r_frame_rate.num = st->time_base.den;
  334. st->r_frame_rate.den = st->time_base.num;
  335. }
  336. }
  337. } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
  338. if (!st->codec->bits_per_coded_sample)
  339. st->codec->bits_per_coded_sample =
  340. av_get_bits_per_sample(st->codec->codec_id);
  341. // set stream disposition based on audio service type
  342. switch (st->codec->audio_service_type) {
  343. case AV_AUDIO_SERVICE_TYPE_EFFECTS:
  344. st->disposition = AV_DISPOSITION_CLEAN_EFFECTS;
  345. break;
  346. case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
  347. st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED;
  348. break;
  349. case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
  350. st->disposition = AV_DISPOSITION_HEARING_IMPAIRED;
  351. break;
  352. case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
  353. st->disposition = AV_DISPOSITION_COMMENT;
  354. break;
  355. case AV_AUDIO_SERVICE_TYPE_KARAOKE:
  356. st->disposition = AV_DISPOSITION_KARAOKE;
  357. break;
  358. }
  359. }
  360. }
  361. if (probesize)
  362. estimate_timings(ic, old_offset);
  363. av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN);
  364. if (ret >= 0 && ic->nb_streams)
  365. /* We could not have all the codec parameters before EOF. */
  366. ret = -1;
  367. for (i = 0; i < ic->nb_streams; i++) {
  368. const char *errmsg;
  369. st = ic->streams[i];
  370. if (!has_codec_parameters(st, &errmsg)) {
  371. char buf[256];
  372. avcodec_string(buf, sizeof(buf), st->codec, 0);
  373. av_log(ic, AV_LOG_WARNING,
  374. "Could not find codec parameters for stream %d (%s): %s\n"
  375. "Consider increasing the value for the 'analyzeduration' and 'probesize' options\n",
  376. i, buf, errmsg);
  377. } else {
  378. ret = 0;
  379. }
  380. }
  381. compute_chapters_end(ic);
  382. find_stream_info_err:
  383. for (i = 0; i < ic->nb_streams; i++) {
  384. st = ic->streams[i];
  385. if (ic->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
  386. ic->streams[i]->codec->thread_count = 0;
  387. if (st->info)
  388. av_freep(&st->info->duration_error);
  389. av_freep(&ic->streams[i]->info);
  390. }
  391. if (ic->pb)
  392. av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n",
  393. avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count);
  394. return ret;
  395. }

由于avformat_find_stream_info()代码比较长,难以全部分析,在这里只能简单记录一下它的要点。该函数主要用于给每个媒体流(音频/视频)的AVStream结构体赋值。我们大致浏览一下这个函数的代码,会发现它其实已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作。换句话说,该函数实际上已经“走通”的解码的整个流程。下面看一下除了成员变量赋值之外,该函数的几个关键流程。

1.查找解码器:find_decoder()
2.打开解码器:avcodec_open2()
3.读取完整的一帧压缩编码的数据:read_frame_internal()
注:av_read_frame()内部实际上就是调用的read_frame_internal()。
4.解码一些压缩编码数据:try_decode_frame()

下面选择上述流程中几个关键函数的代码简单看一下。

find_decoder()

find_decoder()用于找到合适的解码器,它的定义如下所示。

  1. static const AVCodec *find_decoder(AVFormatContext *s, AVStream *st, enum AVCodecID codec_id)
  2. {
  3. if (st->codec->codec)
  4. return st->codec->codec;
  5. switch (st->codec->codec_type) {
  6. case AVMEDIA_TYPE_VIDEO:
  7. if (s->video_codec)    return s->video_codec;
  8. break;
  9. case AVMEDIA_TYPE_AUDIO:
  10. if (s->audio_codec)    return s->audio_codec;
  11. break;
  12. case AVMEDIA_TYPE_SUBTITLE:
  13. if (s->subtitle_codec) return s->subtitle_codec;
  14. break;
  15. }
  16. return avcodec_find_decoder(codec_id);
  17. }

从代码中可以看出,如果指定的AVStream已经包含了解码器,则函数什么也不做直接返回。否则调用avcodec_find_decoder()获取解码器。avcodec_find_decoder()是一个FFmpeg的API函数,在这里不做详细分析。

read_frame_internal()

read_frame_internal()的功能是读取一帧压缩码流数据。FFmpeg的API函数av_read_frame()内部调用的就是read_frame_internal()。有关这方面的知识可以参考文章:

ffmpeg 源代码简单分析 : av_read_frame()
因此,可以认为read_frame_internal()和av_read_frame()的功能基本上是等同的。

try_decode_frame()

try_decode_frame()的功能可以从字面上的意思进行理解:“尝试解码一些帧”,它的定义如下所示。

  1. /* returns 1 or 0 if or if not decoded data was returned, or a negative error */
  2. static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt,
  3. AVDictionary **options)
  4. {
  5. const AVCodec *codec;
  6. int got_picture = 1, ret = 0;
  7. AVFrame *frame = av_frame_alloc();
  8. AVSubtitle subtitle;
  9. AVPacket pkt = *avpkt;
  10. if (!frame)
  11. return AVERROR(ENOMEM);
  12. if (!avcodec_is_open(st->codec) &&
  13. st->info->found_decoder <= 0 &&
  14. (st->codec->codec_id != -st->info->found_decoder || !st->codec->codec_id)) {
  15. AVDictionary *thread_opt = NULL;
  16. codec = find_decoder(s, st, st->codec->codec_id);
  17. if (!codec) {
  18. st->info->found_decoder = -st->codec->codec_id;
  19. ret                     = -1;
  20. goto fail;
  21. }
  22. /* Force thread count to 1 since the H.264 decoder will not extract
  23. * SPS and PPS to extradata during multi-threaded decoding. */
  24. av_dict_set(options ? options : &thread_opt, "threads", "1", 0);
  25. if (s->codec_whitelist)
  26. av_dict_set(options ? options : &thread_opt, "codec_whitelist", s->codec_whitelist, 0);
  27. ret = avcodec_open2(st->codec, codec, options ? options : &thread_opt);
  28. if (!options)
  29. av_dict_free(&thread_opt);
  30. if (ret < 0) {
  31. st->info->found_decoder = -st->codec->codec_id;
  32. goto fail;
  33. }
  34. st->info->found_decoder = 1;
  35. } else if (!st->info->found_decoder)
  36. st->info->found_decoder = 1;
  37. if (st->info->found_decoder < 0) {
  38. ret = -1;
  39. goto fail;
  40. }
  41. while ((pkt.size > 0 || (!pkt.data && got_picture)) &&
  42. ret >= 0 &&
  43. (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) ||
  44. (!st->codec_info_nb_frames &&
  45. st->codec->codec->capabilities & CODEC_CAP_CHANNEL_CONF))) {
  46. got_picture = 0;
  47. switch (st->codec->codec_type) {
  48. case AVMEDIA_TYPE_VIDEO:
  49. ret = avcodec_decode_video2(st->codec, frame,
  50. &got_picture, &pkt);
  51. break;
  52. case AVMEDIA_TYPE_AUDIO:
  53. ret = avcodec_decode_audio4(st->codec, frame, &got_picture, &pkt);
  54. break;
  55. case AVMEDIA_TYPE_SUBTITLE:
  56. ret = avcodec_decode_subtitle2(st->codec, &subtitle,
  57. &got_picture, &pkt);
  58. ret = pkt.size;
  59. break;
  60. default:
  61. break;
  62. }
  63. if (ret >= 0) {
  64. if (got_picture)
  65. st->nb_decoded_frames++;
  66. pkt.data += ret;
  67. pkt.size -= ret;
  68. ret       = got_picture;
  69. }
  70. }
  71. if (!pkt.data && !got_picture)
  72. ret = -1;
  73. fail:
  74. av_frame_free(&frame);
  75. return ret;
  76. }

从try_decode_frame()的定义可以看出,该函数首先判断视音频流的解码器是否已经打开,如果没有打开的话,先打开相应的解码器。接下来根据视音频流类型的不同,调用不同的解码函数进行解码:视频流调用avcodec_decode_video2(),音频流调用avcodec_decode_audio4(),字幕流调用avcodec_decode_subtitle2()。解码的循环会一直持续下去直到满足了while()的所有条件。

while()语句的条件中有一个has_codec_parameters()函数,用于判断AVStream中的成员变量是否都已经设置完毕。该函数在avformat_find_stream_info()中的多个地方被使用过。下面简单看一下该函数。

has_codec_parameters()

has_codec_parameters()用于检查AVStream中的成员变量是否都已经设置完毕。函数的定义如下。

  1. static int has_codec_parameters(AVStream *st, const char **errmsg_ptr)
  2. {
  3. AVCodecContext *avctx = st->codec;
  4. #define FAIL(errmsg) do {                                         \
  5. if (errmsg_ptr)                                           \
  6. *errmsg_ptr = errmsg;                                 \
  7. return 0;                                                 \
  8. } while (0)
  9. if (   avctx->codec_id == AV_CODEC_ID_NONE
  10. && avctx->codec_type != AVMEDIA_TYPE_DATA)
  11. FAIL("unknown codec");
  12. switch (avctx->codec_type) {
  13. case AVMEDIA_TYPE_AUDIO:
  14. if (!avctx->frame_size && determinable_frame_size(avctx))
  15. FAIL("unspecified frame size");
  16. if (st->info->found_decoder >= 0 &&
  17. avctx->sample_fmt == AV_SAMPLE_FMT_NONE)
  18. FAIL("unspecified sample format");
  19. if (!avctx->sample_rate)
  20. FAIL("unspecified sample rate");
  21. if (!avctx->channels)
  22. FAIL("unspecified number of channels");
  23. if (st->info->found_decoder >= 0 && !st->nb_decoded_frames && avctx->codec_id == AV_CODEC_ID_DTS)
  24. FAIL("no decodable DTS frames");
  25. break;
  26. case AVMEDIA_TYPE_VIDEO:
  27. if (!avctx->width)
  28. FAIL("unspecified size");
  29. if (st->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE)
  30. FAIL("unspecified pixel format");
  31. if (st->codec->codec_id == AV_CODEC_ID_RV30 || st->codec->codec_id == AV_CODEC_ID_RV40)
  32. if (!st->sample_aspect_ratio.num && !st->codec->sample_aspect_ratio.num && !st->codec_info_nb_frames)
  33. FAIL("no frame in rv30/40 and no sar");
  34. break;
  35. case AVMEDIA_TYPE_SUBTITLE:
  36. if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width)
  37. FAIL("unspecified size");
  38. break;
  39. case AVMEDIA_TYPE_DATA:
  40. if (avctx->codec_id == AV_CODEC_ID_NONE) return 1;
  41. }
  42. return 1;
  43. }

estimate_timings()

estimate_timings()位于avformat_find_stream_info()最后面,用于估算AVFormatContext以及AVStream的时长duration。它的代码如下所示。

  1. static void estimate_timings(AVFormatContext *ic, int64_t old_offset)
  2. {
  3. int64_t file_size;
  4. /* get the file size, if possible */
  5. if (ic->iformat->flags & AVFMT_NOFILE) {
  6. file_size = 0;
  7. } else {
  8. file_size = avio_size(ic->pb);
  9. file_size = FFMAX(0, file_size);
  10. }
  11. if ((!strcmp(ic->iformat->name, "mpeg") ||
  12. !strcmp(ic->iformat->name, "mpegts")) &&
  13. file_size && ic->pb->seekable) {
  14. /* get accurate estimate from the PTSes */
  15. estimate_timings_from_pts(ic, old_offset);
  16. ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS;
  17. } else if (has_duration(ic)) {
  18. /* at least one component has timings - we use them for all
  19. * the components */
  20. fill_all_stream_timings(ic);
  21. ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM;
  22. } else {
  23. /* less precise: use bitrate info */
  24. estimate_timings_from_bit_rate(ic);
  25. ic->duration_estimation_method = AVFMT_DURATION_FROM_BITRATE;
  26. }
  27. update_stream_timings(ic);
  28. {
  29. int i;
  30. AVStream av_unused *st;
  31. for (i = 0; i < ic->nb_streams; i++) {
  32. st = ic->streams[i];
  33. av_dlog(ic, "%d: start_time: %0.3f duration: %0.3f\n", i,
  34. (double) st->start_time / AV_TIME_BASE,
  35. (double) st->duration   / AV_TIME_BASE);
  36. }
  37. av_dlog(ic,
  38. "stream: start_time: %0.3f duration: %0.3f bitrate=%d kb/s\n",
  39. (double) ic->start_time / AV_TIME_BASE,
  40. (double) ic->duration   / AV_TIME_BASE,
  41. ic->bit_rate / 1000);
  42. }
  43. }

从estimate_timings()的代码中可以看出,有3种估算方法:
(1)通过pts(显示时间戳)。该方法调用estimate_timings_from_pts()。它的基本思想就是读取视音频流中的结束位置AVPacket的PTS和起始位置AVPacket的PTS,两者相减得到时长信息。
(2)通过已知流的时长。该方法调用fill_all_stream_timings()。它的代码没有细看,但从函数的注释的意思来说,应该是当有些视音频流有时长信息的时候,直接赋值给其他视音频流。
(3)通过bitrate(码率)。该方法调用estimate_timings_from_bit_rate()。它的基本思想就是获得整个文件大小,以及整个文件的bitrate,两者相除之后得到时长信息。

estimate_timings_from_bit_rate()

在这里附上上述几种方法中最简单的函数estimate_timings_from_bit_rate()的代码。

  1. static void estimate_timings_from_bit_rate(AVFormatContext *ic)
  2. {
  3. int64_t filesize, duration;
  4. int i, show_warning = 0;
  5. AVStream *st;
  6. /* if bit_rate is already set, we believe it */
  7. if (ic->bit_rate <= 0) {
  8. int bit_rate = 0;
  9. for (i = 0; i < ic->nb_streams; i++) {
  10. st = ic->streams[i];
  11. if (st->codec->bit_rate > 0) {
  12. if (INT_MAX - st->codec->bit_rate < bit_rate) {
  13. bit_rate = 0;
  14. break;
  15. }
  16. bit_rate += st->codec->bit_rate;
  17. }
  18. }
  19. ic->bit_rate = bit_rate;
  20. }
  21. /* if duration is already set, we believe it */
  22. if (ic->duration == AV_NOPTS_VALUE &&
  23. ic->bit_rate != 0) {
  24. filesize = ic->pb ? avio_size(ic->pb) : 0;
  25. if (filesize > ic->data_offset) {
  26. filesize -= ic->data_offset;
  27. for (i = 0; i < ic->nb_streams; i++) {
  28. st      = ic->streams[i];
  29. if (   st->time_base.num <= INT64_MAX / ic->bit_rate
  30. && st->duration == AV_NOPTS_VALUE) {
  31. duration = av_rescale(8 * filesize, st->time_base.den,
  32. ic->bit_rate *
  33. (int64_t) st->time_base.num);
  34. st->duration = duration;
  35. show_warning = 1;
  36. }
  37. }
  38. }
  39. }
  40. if (show_warning)
  41. av_log(ic, AV_LOG_WARNING,
  42. "Estimating duration from bitrate, this may be inaccurate\n");
  43. }

从代码中可以看出,该函数做了两步工作:
(1)如果AVFormatContext中没有bit_rate信息,就把所有AVStream的bit_rate加起来作为AVFormatContext的bit_rate信息。
(2)使用文件大小filesize除以bitrate得到时长信息。具体的方法是:

AVStream->duration=(filesize*8/bit_rate)/time_base

PS:
1)filesize乘以8是因为需要把Byte转换为Bit
2)具体的实现函数是那个av_rescale()函数。x=av_rescale(a,b,c)的含义是x=a*b/c。
3)之所以要除以time_base,是因为AVStream中的duration的单位是time_base,注意这和AVFormatContext中的duration的单位(单位是AV_TIME_BASE,固定取值为1000000)是不一样的。

至此,avformat_find_stream_info()主要的函数就分析完了。

雷霄骅
leixiaohua1020@126.com
http://blog.csdn.net/leixiaohua1020

转自:http://blog.csdn.net/leixiaohua1020/article/details/44084321

(转)FFmpeg源代码简单分析:avformat_find_stream_info()的更多相关文章

  1. FFmpeg源代码简单分析:avformat&lowbar;find&lowbar;stream&lowbar;info&lpar;&rpar;

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  2. FFmpeg源代码简单分析:libavdevice的gdigrab

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  3. FFmpeg源代码简单分析:libavdevice的avdevice&lowbar;register&lowbar;all&lpar;&rpar;

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  4. FFmpeg源代码简单分析:configure

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  5. FFmpeg源代码简单分析:makefile

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  6. FFmpeg源代码简单分析:libswscale的sws&lowbar;scale&lpar;&rpar;

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  7. FFmpeg源代码简单分析:libswscale的sws&lowbar;getContext&lpar;&rpar;

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  8. FFmpeg源代码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  9. FFmpeg源代码简单分析:结构体成员管理系统-AVClass

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

随机推荐

  1. 搭建web框架手册(一)

    昨天听完永康对EASYUI的介绍后终于明白了优秀的UI框架就是第一生产力,过去自己一直沉浸在后端代码中,完全忽视了前端的生产力交互,总觉得界面漂亮就是生产力,其实大错特错,真正的具有高效生产力的界面其 ...

  2. java selenium &lpar;九&rpar; 常见web UI 元素操作 及API使用

    本篇介绍我们如何利用selenium 来操作各种页面元素 阅读目录  链接(link) <div> <p>链接 link</p> <a href=" ...

  3. vs2012 发布网站时,发布目录为空

    当我使用Release Any CPU时为空 使用Release X86就正常发布了 奇怪. 之后再切换回 Release Any CPU时正常发布. 在生成时可以尝试设置好生成配置,先生成,再发布.

  4. Build Android Webrtc Libjingle Library On Ubuntu

    Our team is developing an app to help people solve problem face to face. We choose webrtc protocol a ...

  5. 截取视图某一段另存为部分视图&lpar;Partial View&rpar;

    在做ASP.NET MVC后台管理程序时,根据程序需要,Isus.NET需要实现一个功能,就是动态截取视图某一段另存为部分视图Partial View. 思路为在视图中,使用jQury的程序截图以及P ...

  6. weblogic诊断案例-AdminServer平均1-2周崩溃

    OS2台:RH5.5 64位 WEBLOGIC VERSION:9.2.3 JDK:1.5 64位 weblogic一个管理服务,4个受管(2台服务器做集群,每台服务器2个受管).应用部署后通过一个月 ...

  7. B450配置

    电脑型号 联想 B450 笔记本电脑  (扫描时间:2015?5?7?操作系统 Windows 7 旗舰版 32位 SP1 ( DirectX 11 ) 处理器 英特尔 酷睿2 双核 T9800 @ ...

  8. delphi DCC32命令行方式编译delphi工程源码

    本文链接地址:http://blog.csdn.net/sushengmiyan/article/details/10284879 作者:苏生米沿 Borland出品的Delphi,在TIOBE公布的 ...

  9. Android项目--tabhost

    所有牵扯到自定义布局的layout中尽量用RelativeLayout 在通讯录中如果像小米手机的UI那就是viewpager,在这里,我们做成静态的.通过tabhost来做. 1.布局 a) 直接用 ...

  10. C&plus;&plus;中所有的变量和函数都必须有类型

    /* C++中所有的变量和函数都必须有类型 C语言中的默认类型在C++中是不合法的 函数f的返回值是什么类型,参数又是什么类型? 函数g可以接受多少个参数? */ //更换成.cpp就会报错 f(i) ...