今天学习解析媒体文件。

写了一个用例,解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件。

照惯例,先学习,再代码。

学习

av_register_all

[cpp] view plaincopy
  1. /**
  2. * 初始化 libavformat,并且注册所有的合并器、解析器和协议。
  3. * 如果你不调用这个方法,你可以明确地选择你想要程序支持的格式。
  4. * 参照 av_register_input_format()
  5. * 参照 av_register_output_format()
  6. */
  7. void av_register_all(void);

avformat_open_input

[cpp] view plaincopy
  1. /**
  2. * 打开一个输入流并且读取它的头部数据,这个编解码器不会被打开。
  3. * 这个流必须使用avformat_close_input()关闭。
  4. *
  5. * @参数 ps   指向一个用户提供的AVFormatContext(使用avformat_alloc_context分配的空间)。
  6. *          这个参数可以指向NULL,这样,该参数将会在方法内部被分配控件,并且写入ps。
  7. *
  8. *          注意,如果这个参数是用户提供的,方法调用失败后,AVFormatContext会被释放。
  9. *
  10. * @参数 url 被打开媒体流的url。对于媒体文件,它是媒体文件路径。
  11. * @参数 fmt 如果不为空,这个参数规定一个特定的输入格式。
  12. *           否则,输入格式将被自动检测。
  13. * @参数 options ...
  14. *
  15. * @成功返回0,失败返回负数。
  16. *
  17. * @注意,如果你想使用特定的IO,在分配格式上下文空间之前,设置它的pb字段
  18. */
  19. int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);

avformat_find_stream_info

[cpp] view plaincopy
  1. /* 读取媒体文件的包来获取流信息。这个方法对于没有头部的文件格式也是有用的,例如MPEG。
  2. * 对于MPEG-2这种帧模型重复的类型,这个方法也会计算真实帧率。
  3. *
  4. * 该逻辑文件地址不被这个方法更改。
  5. * 检查的数据包可能会被缓冲以用于以后处理。
  6. *
  7. * @参数 ic 媒体文件句柄(格式上下文)
  8. * @参数 options ...
  9. * @成功返回值大于0
  10. * @注意:这个方法不保证打开所有的编解码器,所以非空的options参数将会返回一个完全合理的行为。
  11. *
  12. * 为了让用户决定什么样的信息是他们需要的,我们不会浪费时间去获取用户不需要的东西。
  13. */
  14. nt avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

av_find_best_stream

[cpp] view plaincopy
  1. /* 在媒体文件中寻找最合适的流结构体(AVStream)。
  2. * 如果参数decoder_ret为非空,av_find_best_stream将会流的编解码寻找默认解码器。
  3. * 对于那些不能找到解码器的流来说,它会被忽略。
  4. * @param ic 媒体文件句柄(格式上下文)
  5. * @param type 流类型,视频(AVMEDIA_TYPE_VIDEO),音频(AVMEDIA_TYPE_AUDIO)
  6. * @param wanted_stream_nb AVStream在AVFormatContext中streams的索引,调试证明最好填-1。
  7. * @param related_stream -1
  8. * @param decoder_ret null
  9. * @param flags 0
  10. * @return 成功返回值非负。
  11. */
  12. nt av_find_best_stream(AVFormatContext *ic,
  13. enum AVMediaType type,
  14. int wanted_stream_nb,
  15. int related_stream,
  16. AVCodec **decoder_ret,
  17. int flags);

avcodec_find_decoder

[cpp] view plaincopy
  1. /* 通过匹配的编解码器ID,找到一个注册的解码器.
  2. */
  3. AVCodec *avcodec_find_decoder(enum AVCodecID id);

avcodec_alloc_context3

[cpp] view plaincopy
  1. /* 分配一个AVCodecContext,并且设置它的字段为默认值。
  2. * 这个结构体应该使用avcodec_free_context()释放
  3. */
  4. VCodecContext *avcodec_alloc_context3(const AVCodec *codec);

avcodec_parameters_to_context

[cpp] view plaincopy
  1. /*根据AVCodecParameters的值填充AVCodecContext。
  2. *AVCodecParameters与AVCodecContext相对应的将会被替换,不对应不替换。
  3. */
  4. int avcodec_parameters_to_context(AVCodecContext *codec,
  5. const AVCodecParameters *par);

avcodec_open2

[cpp] view plaincopy
  1. /* 使用提供的AVCodec来初始化AVCodecContext结构体。在使用这个功能之前,AVCodecContext必须通过
  2. * avcodec_alloc_context3()分配空间。
  3. * 这些方法 avcodec_find_decoder_by_name(),avcodec_find_encoder_by_name()... 提供一个简单的方法来检索到一个编解码器。
  4. *   这个方法不是线程安全的
  5. */
  6. nt avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

av_image_alloc

[cpp] view plaincopy
  1. /* 通过参数w,h,pix_fmt,align来分配一个图像的空间,参数pointers和linesizes将会被赋值。
  2. * pointers必须通过av_freep(&pointers[0])释放。
  3. * @param 调整缓冲区大小对齐的值
  4. */
  5. int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
  6. int w, int h, enum AVPixelFormat pix_fmt, int align);

av_read_frame

[cpp] view plaincopy
  1. /* 返回流的下一帧到AVPacket中。
  2. * 这个方法返回存储在文件中的数据,它并不确定这些有效帧有解码器。
  3. * 每次调用这个方法,它将分割文件中的数据到AVPacket里。它不会遗漏
  4. * 有效帧之间的数据,来给解码器最大的信息来解码。
  5. *
  6. * 如果返回的AVPacket有效,在不使用时,必须用av_packet_unref释放。
  7. * 对于视频,AVPacket包含一帧。对于音频,如果每一帧大小固定已知,那么
  8. * AVPacket包含多帧;如果每一帧大小可变,那么AVPacket包含一帧。
  9. *
  10. * 这几个值(pkt->pts, pkt->dts and pkt->duration)总是会被赋值。
  11. */
  12. int av_read_frame(AVFormatContext *s, AVPacket *pkt);

struct AVPacket

[cpp] view plaincopy
  1. /* 这个结构体存储压缩数据。它通过解析器输出,作为解码器的输入,或者通过编码器输出, 然后传给合成器。
  2. * 对于视频,它代表一个压缩帧,对于音频,它可能代表多个压缩帧,解码器允许输出一个空的packet,
  3. * 没有压缩数据,仅仅包含次要数据
  4. */
  5. typedef struct AVPacket

avcodec_send_packet

[cpp] view plaincopy
  1. /* 将数据包发送给解码器。(可以想象解码内部有个存放数据的容器)
  2. */
  3. int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

avcodec_receive_frame

[cpp] view plaincopy
  1. /* 返回解码过后的一个帧,与avcodec_send_packet配套使用。
  2. * avcodec_send_packet传入的AVPacket可能包含多帧,所以avcodec_receive_frame可以调用
  3. * 多次,直到失败。
  4. */
  5. int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

源码

解析MP4文件得到视频帧和音频帧,并分别保存到不同的文件。(代码许多地方返回值未校验)
[cpp] view plaincopy
  1. extern "C"
  2. {
  3. #include <libavutil/imgutils.h>
  4. #include <libavutil/samplefmt.h>
  5. #include <libavutil/timestamp.h>
  6. #include <libavformat/avformat.h>
  7. }
  8. int get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt)
  9. {
  10. int i;
  11. struct sample_fmt_entry {
  12. enum AVSampleFormat sample_fmt;
  13. const char *fmt_be, *fmt_le;
  14. }
  15. sample_fmt_entries[] = {
  16. { AV_SAMPLE_FMT_U8,  "u8",    "u8" },
  17. { AV_SAMPLE_FMT_S16, "s16be", "s16le" },
  18. { AV_SAMPLE_FMT_S32, "s32be", "s32le" },
  19. { AV_SAMPLE_FMT_FLT, "f32be", "f32le" },
  20. { AV_SAMPLE_FMT_DBL, "f64be", "f64le" },
  21. };
  22. *fmt = NULL;
  23. for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
  24. struct sample_fmt_entry *entry = &sample_fmt_entries[i];
  25. if (sample_fmt == entry->sample_fmt) {
  26. *fmt = AV_NE(entry->fmt_be, entry->fmt_le);
  27. return 0;
  28. }
  29. }
  30. fprintf(stderr,
  31. "sample format %s is not supported as output format\n",
  32. av_get_sample_fmt_name(sample_fmt));
  33. return -1;
  34. }
  35. void demuxing_decoding(const char* filename)
  36. {
  37. av_register_all();
  38. puts("注册...");
  39. AVFormatContext* formatContext = nullptr;
  40. if (0 != avformat_open_input(&formatContext, filename, NULL, NULL))
  41. {
  42. puts("avformat_open_input失败.");
  43. return;
  44. }
  45. puts("avformat_open_input...");
  46. if (avformat_find_stream_info(formatContext, NULL) < 0)
  47. {
  48. puts("avformat_find_stream_info失败.");
  49. return;
  50. }
  51. puts("avformat_find_stream_info...");
  52. //视频
  53. int video_stream_idx = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, 0, -1, nullptr, 0);
  54. if (video_stream_idx < 0)
  55. {
  56. puts("avformat_find_stream_info AVMEDIA_TYPE_VIDEO失败.");
  57. return;
  58. }
  59. AVStream* video_st = formatContext->streams[video_stream_idx];
  60. AVCodec *videCodec = avcodec_find_decoder(video_st->codecpar->codec_id);
  61. AVCodecContext* videoContext = avcodec_alloc_context3(videCodec);
  62. avcodec_parameters_to_context(videoContext, video_st->codecpar);
  63. avcodec_open2(videoContext, videCodec, nullptr);
  64. uint8_t *video_dst_data[4] = { NULL };
  65. int video_dst_linesize[4];
  66. int video_dst_bufsize = av_image_alloc(video_dst_data, video_dst_linesize, videoContext->width, videoContext->height, videoContext->pix_fmt, 1);
  67. //音频
  68. int audio_stream_idx = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
  69. AVStream* audio_st = formatContext->streams[audio_stream_idx];
  70. AVCodec* audioCodec = avcodec_find_decoder(audio_st->codecpar->codec_id);
  71. AVCodecContext* audioContext = avcodec_alloc_context3(audioCodec);
  72. avcodec_parameters_to_context(audioContext, audio_st->codecpar);
  73. avcodec_open2(audioContext, audioCodec, nullptr);
  74. av_dump_format(formatContext, 0, filename, 0);
  75. AVFrame* frame = av_frame_alloc();
  76. AVPacket pkt;
  77. av_init_packet(&pkt);
  78. pkt.data = NULL;
  79. pkt.size = 0;
  80. FILE *videofile = nullptr;
  81. char video_name[128] = { 0 };
  82. sprintf_s(video_name, 128, "output\\demuxing_video.%s", videCodec->name);
  83. fopen_s(&videofile, video_name, "wb");
  84. FILE *audiofile = nullptr;
  85. char audio_name[128] = { 0 };
  86. sprintf_s(audio_name, 128, "output\\demuxing_audio.%s", audioCodec->name);
  87. fopen_s(&audiofile, audio_name, "wb");
  88. while (av_read_frame(formatContext, &pkt) >= 0)
  89. {
  90. if (pkt.stream_index == video_stream_idx)
  91. {
  92. avcodec_send_packet(videoContext, &pkt);
  93. while (0 == avcodec_receive_frame(videoContext, frame))
  94. {
  95. av_image_copy(video_dst_data, video_dst_linesize, (const uint8_t **)frame->data, frame->linesize, videoContext->pix_fmt, videoContext->width, videoContext->height);
  96. fwrite(video_dst_data[0], 1, video_dst_bufsize, videofile);
  97. puts("写入视频");
  98. }
  99. }
  100. else if (pkt.stream_index == audio_stream_idx)
  101. {
  102. avcodec_send_packet(audioContext, &pkt);
  103. while (0 == avcodec_receive_frame(audioContext, frame))
  104. {
  105. size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)frame->format);
  106. fwrite(frame->extended_data[0], 1, unpadded_linesize, audiofile);
  107. puts("写入音频");
  108. }
  109. }
  110. av_packet_unref(&pkt);
  111. }
  112. printf("Play the output video file with the command:\n"
  113. "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
  114. av_get_pix_fmt_name(videoContext->pix_fmt), videoContext->width, videoContext->height,
  115. video_name);
  116. enum AVSampleFormat sfmt = audioContext->sample_fmt;
  117. int n_channels = audioContext->channels;
  118. const char *fmt;
  119. if (av_sample_fmt_is_planar(sfmt)) {
  120. const char *packed = av_get_sample_fmt_name(sfmt);
  121. printf("Warning: the sample format the decoder produced is planar "
  122. "(%s). This example will output the first channel only.\n",
  123. packed ? packed : "?");
  124. sfmt = av_get_packed_sample_fmt(sfmt);
  125. n_channels = 1;
  126. }
  127. if (get_format_from_sample_fmt(&fmt, sfmt) < 0)
  128. {
  129. }
  130. printf("Play the output audio file with the command:\n"
  131. "ffplay -f %s -ac %d -ar %d %s\n",
  132. fmt, n_channels, audioContext->sample_rate,
  133. audio_name);
  134. fclose(videofile);
  135. fclose(audiofile);
  136. avcodec_free_context(&videoContext);
  137. avcodec_free_context(&audioContext);
  138. avformat_close_input(&formatContext);
  139. av_frame_free(&frame);
  140. av_free(video_dst_data[0]);
  141. getchar();
  142. }

转自:http://blog.csdn.net/u013043408/article/details/52701776

ffmpeg之demux 解复用相关推荐

  1. 【四】【vlc-android】播放控制交互与demux解复用层、媒体数据流拉取层的具体数据传递和控制流程源码分析

    1.VLC中有很多demux/mux/encoder/decoder模块,因此需要先了解这些模块的加载原理,模块的加载原理基本一致,因此举例分析MP4解复用模块如何加载完成的,主要流程如下: // v ...

  2. FFmpeg从入门到牛掰(一):解复用(demux)讲解

    转载请注明出处:https://blog.csdn.net/impingo 项目地址:https://github.com/im-pingo/pingos 解复用讲解 概念 解复用操作 函数调用流程 ...

  3. 【FFmpeg】FFmpeg 相关术语简介 ( 容器 | 媒体流 | 数据帧 | 数据包 | 编解码器 | 复用 | 解复用 )

    文章目录 一.FFmpeg 简介 二.FFmpeg 相关术语 1.容器 2.媒体流 3.数据帧 4.数据包 5.编解码器 6.复用 7.解复用 博客资源 一.FFmpeg 简介 FFmpeg 是 &q ...

  4. 【FFmpeg】使用 FFmpeg 处理音视频格式转换流程 ( 解复用 | 解码 | 帧处理 | 编码 | 复用 )

    FFmpeg 系列文章目录 [FFmpeg]Windows 搭建 FFmpeg 命令行运行环境 [FFmpeg]FFmpeg 相关术语简介 [FFmpeg]FFmpeg 相关术语简介 二 [FFmpe ...

  5. 《Android FFmpeg 播放器开发梳理》第一章 播放器初始化与解复用流程

    <Android FFmpeg 播放器开发梳理>: 第零章 基础公共类的封装 播放器初始化与解复用流程 这一章,我们来讲解播放器解复用(从文件中读取数据包)的流程.在讲解播放器的读数据包流 ...

  6. ffmpeg解复用编解码 常用API大全给出详细中文解释

    int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags); 将你给出的条目设置进入你给到的 p ...

  7. FFmpeg入门详解之43:FFmpeg解封装的原理与实战

    FFMpeg 解封装 本例子实现的是将音视频分离,例如将封装格式为 FLV.MKV.MP4.AVI 等封装格式的文件,将音频.视频分离开来. 大致的解封装流程: 1.首先要对解复用器进行初始化. 2. ...

  8. FFmpeg入门详解之119:FFmpeg的SDK编程回顾总结并操练

    3.FFmpeg的SDK编程回顾总结并操练 参考课程:"FFmpeg4.3--系列5--SDK二次开发详解与实战" FFmpeg主要框架 FFmpeg骨架:"八大金刚&q ...

  9. FFmpeg入门详解之122:Qt5 FFmpeg本地摄像头采集预览实战

    6.Qt5+FFmpeg本地摄像头采集预览实战 源码工程:S26_Test2 FFmpeg命令行处理摄像头 ffmpeg -list_devices true -f dshow -i dummy 命令 ...

最新文章

  1. webpack打开项目命令_webpack前端模块打包工具基本使用的详细记录(一)
  2. [CPP]--Unicode 字符编码
  3. 微信小程序登录页php后台,微信小程序:微信登陆(ThinkPHP作后台)
  4. Android kotlin基础语法
  5. linux 版本_Linux动态库版本号作用机制
  6. H2O_Hyper_V-master网页端管理程序源码
  7. C++全局变量的声明和定义
  8. linux nginx vue_Vue-CLI 3.x 部署项目至生产服务器
  9. 51Nod-1101 换零钱【0/1背包+DP】
  10. 视频教程-思科路由器搭建终极实战-网络技术
  11. Android 手势事件工具类GestureDetector和VelocityTracker
  12. 服务器 磁盘阵列数据恢复案例之:RAID6三块磁盘离线数据恢复过程
  13. 网站优质内容细则及示例说明
  14. 技巧_MFC_标题栏背景
  15. AI的艺术创造力超越人类?绝不是痴人说梦
  16. Android统一推送联盟成立
  17. 日常pytho3练习脚本之--两个逗比聊天机器人
  18. 讲讲BW/4 HANA和BW on HANA的区别
  19. 有一个一维数组,内放10个学生成绩,写一个函数当主函数调用此函数后嫩求出平均分、最高分和最低分
  20. Kotlin 特色之 Sealed Class 和 Interface

热门文章

  1. 计算机一级等级考试——计算机基础及Office应用教程-兰晓宇-专题视频课程
  2. 向下扎根,向上生长,探寻华为云AI的“根”力量
  3. 新加坡西海岸公寓SeaHill 稀有公寓式联排别墅 外国人可买
  4. 周易名:传统周易结合现代人工智能起名字
  5. org.apache.axis2.AxisFault: java.lang.Error: Unresolved compilation problem:
  6. Linux常用命令和快捷键大全
  7. 【ae】文字3D:动画3D:3D摄像机
  8. map例子:魔法咒语问题
  9. 51CTO移动客户端可以在线下载安装啦 - 51CTO博客开发 - 51CTO技术博客
  10. 【Arduino 无刷电机控制教程】