• Qt+FFmpeg+opengl从零制作视频播放器-3.解封装


    解封装:如下图所示,就是将FLV、MKV、MP4等文件解封装为视频H.264或H.265压缩数据,音频MP3或AAC的压缩数据,下图为常用的基本操作。

     ffmpeg使用解封装的基本流程如下:

    在使用FFmpeg API之前,需要先注册API,然后才能使用API。当然,新版本ffmpeg库不需要再调用下面的方法。

    av_register_all()

    初始化网络配置

    avformat_network_init

    设置一些参数,如下图所示,设置了最大延迟、传输协议等参数。

    1. //参数设置
    2. AVDictionary *opts = NULL;
    3. //设置rtsp流已tcp协议打开
    4. av_dict_set(&opts, "rtsp_transport", "tcp", 0);
    5. av_dict_set(&opts, "max_delay", "500", 0);
    6. av_dict_set(&opts, "buffer_size", "1024000", 0);
    7. av_dict_set(&opts, "probsize", "4096", 0);
    8. av_dict_set(&opts, "fps", "25", 0);

    构建AVFormatContext,声明输入的封装结构体,通过输入文件或者流地址作为封装结构的句柄。

    1.     AVFormatContext* inputFmtCtx = nullptr;
    2.     const char* inputUrl = "test.mp4";
    3.  
    4.     ///打开输入的流,获取数据 begin
    5.     int ret = avformat_open_input(&inputFmtCtx, inputUrl, NULL, NULL);

    查找音视频流信息,通过下面的接口与AVFormatContext中建立输入文件对应的流信息。

    1.     //查找;
    2.     if (avformat_find_stream_info(inputFmtCtx, NULL) < 0)
    3.     {
    4.         printf("Couldn't find stream information.\n");
    5.         return false;
    6.     }


    读取音视频流,采用av_read_frame来读取数据包,读出来的数据存储在AVPacket中,确定其为音频、视频、字幕数据,最后解码,或者存储。

    1.     AVPacket* pkt = NULL;
    2.     pkt = av_packet_alloc();
    3.  
    4.     while (av_read_frame(inputFmtCtx, pkt) >= 0)
    5.     {
    6.         //获取数据包
    7.  
    8.         //.....解码或者存储
    9.  
    10.         //重置
    11.         av_packet_unref(pkt);
    12.     }

    上述代码所示,通过循环调用av_read_frame()读取到pkt包,然后可以进行解码或者存储数据,如果读取的数据结束,则退出循环,开始指向结束操作。

    每次使用完AVPacket包后,需要重置,否则会内存泄漏。

    1. //重置
    2.     av_packet_unref(pkt);

    执行结束后关闭输入文件,释放资源。

    1.     //关闭
    2.     avformat_close_input(&inputFmtCtx);
    3. //释放
    4. avformat_free_context(inputFmtCtx);
    5.  
    6.     //释放资源
    7.     av_packet_free(&pkt);

     新建空白解决方案,如下图所示。

    解决方案,右键-添加-新建项目。 

    新建Qt控制台程序,3_demuxDemo 

    然后,配置ffmpeg的编译环境:看目录4。

    ffmpeg环境配置 

    解封装源码示例:

    1. #include
    2. extern "C" {
    3. #include "libavutil/avstring.h"
    4. #include "libavutil/channel_layout.h"
    5. #include "libavutil/eval.h"
    6. #include "libavutil/mathematics.h"
    7. #include "libavutil/pixdesc.h"
    8. #include "libavutil/imgutils.h"
    9. #include "libavutil/dict.h"
    10. #include "libavutil/fifo.h"
    11. #include "libavutil/parseutils.h"
    12. #include "libavutil/samplefmt.h"
    13. #include "libavutil/time.h"
    14. #include "libavutil/bprint.h"
    15. #include "libavutil/opt.h"
    16. #include "libavformat/avformat.h"
    17. #include "libavcodec/avcodec.h"
    18. #include "libavfilter/avfilter.h"
    19. #include "libavdevice/avdevice.h"
    20. #include "libswscale/swscale.h"
    21. #include "libavutil/opt.h"
    22. #include "libavutil/imgutils.h"
    23. #include "libavcodec/avfft.h"
    24. #include "libswresample/swresample.h"
    25. }
    26. #include
    27. using namespace std;
    28. static double r2d(AVRational r)
    29. {
    30. return r.den == 0 ? 0 : (double)r.num / (double)r.den;
    31. }
    32. int main(int argc, char *argv[])
    33. {
    34. QCoreApplication a(argc, argv);
    35. //av_register_all();
    36. avformat_network_init();
    37. AVDictionary* options = NULL;
    38. av_dict_set(&options, "buffer_size", "1024000", 0);
    39. av_dict_set(&options, "max_delay", "500000", 0);
    40. av_dict_set(&options, "stimeout", "2000000", 0);
    41. av_dict_set(&options, "rtsp_transport", "tcp", 0);
    42. AVFormatContext* inputFmtCtx = nullptr;
    43. const char* inputUrl = "F:/1920x1080.mp4";
    44. ///打开输入的流,获取数据 begin
    45. int ret = avformat_open_input(&inputFmtCtx, inputUrl, NULL, NULL);
    46. if (ret != 0)
    47. {
    48. printf("Couldn't open input stream.\n");
    49. return -1;
    50. }
    51. int vIndex = -1;
    52. int aIndex = -1;
    53. //查找;
    54. if (avformat_find_stream_info(inputFmtCtx, NULL) < 0)
    55. {
    56. printf("Couldn't find stream information.\n");
    57. return false;
    58. }
    59. for (int i = 0; i < inputFmtCtx->nb_streams/*视音频流的个数*/; i++)
    60. {
    61. //查找视频
    62. if (inputFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
    63. {
    64. vIndex = i;
    65. }
    66. else if (inputFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
    67. {
    68. aIndex = i;
    69. }
    70. }
    71. //===================video=================
    72. //视频宽
    73. int width = inputFmtCtx->streams[vIndex]->codecpar->width;
    74. //视频高
    75. int height = inputFmtCtx->streams[vIndex]->codecpar->height;
    76. //视频总时长s
    77. int64_t m_totalTime = static_cast<double>(inputFmtCtx->duration) / AV_TIME_BASE;
    78. //获取帧率;
    79. int fps = r2d(inputFmtCtx->streams[vIndex]->avg_frame_rate);
    80. if (fps == 0)
    81. {
    82. fps = 25;
    83. }
    84. int iHour, iMinute, iSecond, iTotalSeconds;//HH:MM:SS
    85. //打印结构体信息
    86. puts("AVFormatContext信息:");
    87. puts("---------------------------------------------");
    88. iTotalSeconds = (int)inputFmtCtx->duration/*微秒*/ / 1000000;
    89. iHour = iTotalSeconds / 3600;//小时
    90. iMinute = iTotalSeconds % 3600 / 60;//分钟
    91. iSecond = iTotalSeconds % 60;//秒
    92. printf("持续时间:%02d:%02d:%02d\n", iHour, iMinute, iSecond);
    93. printf("平均混合码率:%d kb/s\n", inputFmtCtx->bit_rate / 1000);
    94. printf("视音频个数:%d\n", inputFmtCtx->nb_streams);
    95. puts("---------------------------------------------");
    96. puts("AVInputFormat信息:");
    97. puts("---------------------------------------------");
    98. printf("封装格式名称:%s\n", inputFmtCtx->iformat->name);
    99. printf("封装格式长名称:%s\n", inputFmtCtx->iformat->long_name);
    100. printf("封装格式扩展名:%s\n", inputFmtCtx->iformat->extensions);
    101. printf("封装格式ID:%d\n", inputFmtCtx->iformat->raw_codec_id);
    102. puts("---------------------------------------------");
    103. puts("AVStream信息:");
    104. puts("---------------------------------------------");
    105. printf("视频流标识符:%d\n", inputFmtCtx->streams[vIndex]->index);
    106. printf("音频流标识符:%d\n", inputFmtCtx->streams[aIndex]->index);
    107. printf("视频流长度:%d微秒\n", inputFmtCtx->streams[vIndex]->duration);
    108. printf("音频流长度:%d微秒\n", inputFmtCtx->streams[aIndex]->duration);
    109. puts("---------------------------------------------");
    110. printf("视频时长:%d微秒\n", inputFmtCtx->streams[vIndex]->duration);
    111. printf("音频时长:%d微秒\n", inputFmtCtx->streams[aIndex]->duration);
    112. printf("视频宽:%d\n", inputFmtCtx->streams[vIndex]->codecpar->width);
    113. printf("视频高:%d\n", inputFmtCtx->streams[vIndex]->codecpar->height);
    114. printf("音频采样率:%d\n", inputFmtCtx->streams[aIndex]->codecpar->sample_rate);
    115. printf("音频信道数目:%d\n", inputFmtCtx->streams[aIndex]->codecpar->channels);
    116. puts("AVFormatContext元数据:");
    117. puts("---------------------------------------------");
    118. AVDictionaryEntry *dict = NULL;
    119. while (dict = av_dict_get(inputFmtCtx->metadata, "", dict, AV_DICT_IGNORE_SUFFIX))
    120. {
    121. printf("[%s] = %s\n", dict->key, dict->value);
    122. }
    123. puts("---------------------------------------------");
    124. puts("AVStream视频元数据:");
    125. puts("---------------------------------------------");
    126. dict = NULL;
    127. while (dict = av_dict_get(inputFmtCtx->streams[vIndex]->metadata, "", dict, AV_DICT_IGNORE_SUFFIX))
    128. {
    129. printf("[%s] = %s\n", dict->key, dict->value);
    130. }
    131. puts("---------------------------------------------");
    132. puts("AVStream音频元数据:");
    133. puts("---------------------------------------------");
    134. dict = NULL;
    135. while (dict = av_dict_get(inputFmtCtx->streams[aIndex]->metadata, "", dict, AV_DICT_IGNORE_SUFFIX))
    136. {
    137. printf("[%s] = %s\n", dict->key, dict->value);
    138. }
    139. puts("---------------------------------------------");
    140. av_dump_format(inputFmtCtx, -1, inputUrl, 0);
    141. printf("\n\n编译信息:\n%s\n\n", avcodec_configuration());
    142. AVPacket* pkt = NULL;
    143. pkt = av_packet_alloc();
    144. while (av_read_frame(inputFmtCtx, pkt) >= 0)
    145. {
    146. //获取数据包
    147. //.....解码或者存储
    148. //重置
    149. av_packet_unref(pkt);
    150. }
    151. //关闭
    152. avformat_close_input(&inputFmtCtx);
    153. //释放
    154. avformat_free_context(inputFmtCtx);
    155. //释放资源
    156. av_packet_free(&pkt);
    157. return a.exec();
    158. }

    运行截图:

    完整工程:

    https://download.csdn.net/download/wzz953200463/88959152icon-default.png?t=N7T8https://download.csdn.net/download/wzz953200463/88959152

  • 相关阅读:
    azkaban二次开发
    Neo4j 5的自治图数据库集群
    Express.js实现注册和登录
    UE4 扩展详细信息面板
    使用Java统计gitlab代码行数
    不讲故事的设计模式-责任链模式
    机器学习课后习题 --- 逻辑回归
    面试题:ElasticSearch是什么?应用场景是什么?
    高考相关系列
    场效应管器件
  • 原文地址:https://blog.csdn.net/wzz953200463/article/details/136461834