• SDL2 播放音频(MP4)


    1.简介

    这里引入FFmpeg库,获取音频流数据,然后通过FFmpeg将视频流解码成pcm原始数据,再将pcm数据送入到SDL库中实现音频播放。

    2.FFmpeg的操作流程

    • 注册API:av_register_all()
    • 构建输入AVFormatContext上下文:avformat_open_input()
    • 查找音视频流信息:avformat_find_stream_info()
    • 查找解码器:avcodec_find_decoder()
    • 打开解码器:avcodec_open2()
    • 然后通过while循环,不停的读取数据:av_read_frame()
    • 帧解码:avcodec_send_packet()和avcodec_receive_frame()
    • 重采样:swr_convert()

    3.SDL音频播放流程

    SDL播放音频的流程如下:

    • 初始化音频子系统:SDL_Init()。
    • 设置音频参数:SDL_AudioSpec。
    • 设置回调函数:SDL_AudioCallback。
    • 打开音频设备:SDL_OpenAudio()。
    • 打开pcm文件,读取数据。
    • 开始播放:SDL_PauseAudio()。

    4.示例

    1. #include
    2. #include
    3. #include
    4. extern "C"
    5. {
    6. #include "libavcodec/avcodec.h"
    7. #include "libavformat/avformat.h"
    8. #include "libswscale/swscale.h"
    9. #include "libavutil/imgutils.h"
    10. #include "libswresample/swresample.h"
    11. };
    12. static Uint8 *audio_chunk;
    13. static Uint32 audio_len;
    14. static Uint8 *audio_pos;
    15. void fill_audio(void *udata, Uint8 *stream, int len)
    16. {
    17. //SDL 2.0
    18. SDL_memset(stream, 0, len);
    19. if (audio_len == 0) /* Only play if we have data left */
    20. return;
    21. len = (len > audio_len ? audio_len : len); /* Mix as much data as possible */
    22. SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME/2);
    23. audio_pos += len;
    24. audio_len -= len;
    25. }
    26. AVFrame *recv(AVCodecContext *codecCtx)
    27. {
    28. if (!codecCtx)
    29. {
    30. return NULL;
    31. }
    32. AVFrame *frame = av_frame_alloc();
    33. int ret = avcodec_receive_frame(codecCtx, frame);
    34. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    35. {
    36. av_frame_free(&frame);
    37. return NULL;
    38. }
    39. else if (ret < 0)
    40. {
    41. av_frame_free(&frame);
    42. return NULL;
    43. }
    44. return frame;
    45. }
    46. #undef main
    47. int main(int argc, char* argv[])
    48. {
    49. av_register_all();
    50. ///ffmpeg
    51. avformat_network_init();
    52. AVFormatContext* pFormatCtx = NULL;
    53. const char* inputUrl = "./2.mp4";
    54. ///打开输入的流
    55. int ret = avformat_open_input(&pFormatCtx, inputUrl, NULL, NULL);
    56. if (ret != 0)
    57. {
    58. printf("Couldn't open input stream.\n");
    59. return -1;
    60. }
    61. //查找流信息
    62. if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    63. {
    64. printf("Couldn't find stream information.\n");
    65. return -1;
    66. }
    67. //找到音频流索引
    68. int audio_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    69. AVStream* st = pFormatCtx->streams[audio_index];
    70. AVCodec* codec = nullptr;
    71. //找到解码器
    72. codec = avcodec_find_decoder(st->codecpar->codec_id);
    73. if (!codec)
    74. {
    75. fprintf(stderr, "Codec not found\n");
    76. return -1;
    77. }
    78. //申请AVCodecContext
    79. AVCodecContext* pCodecCtx = avcodec_alloc_context3(codec);
    80. if (!pCodecCtx)
    81. {
    82. return -1;
    83. }
    84. avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audio_index]->codecpar);
    85. //打开解码器
    86. if ((ret = avcodec_open2(pCodecCtx, codec, NULL) < 0))
    87. {
    88. return -1;
    89. }
    90. AVPacket* pkt = av_packet_alloc();
    91. //------------SDL----------------
    92. //Output Info-----------------------------
    93. printf("---------------- File Information ---------------\n");
    94. av_dump_format(pFormatCtx, 0, inputUrl, 0);
    95. printf("-------------------------------------------------\n");
    96. SwrContext *swrContext = swr_alloc();
    97. if (!swrContext)
    98. {
    99. return -1;
    100. }
    101. swrContext = swr_alloc_set_opts(NULL, //ctx
    102. AV_CH_LAYOUT_STEREO, //输出channel布局
    103. AV_SAMPLE_FMT_S16, //输出的采样格式
    104. 44100, //采样率
    105. av_get_default_channel_layout(pCodecCtx->channels), //输入channel布局
    106. pCodecCtx->sample_fmt, //输入的采样格式
    107. pCodecCtx->sample_rate, //输入的采样率
    108. 0, NULL);
    109. // 初始化重采样上下文
    110. if (swr_init(swrContext) < 0)
    111. {
    112. swr_free(&swrContext);
    113. return -1;
    114. }
    115. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
    116. {
    117. printf("Could not initialize SDL - %s\n", SDL_GetError());
    118. return -1;
    119. }
    120. //SDL_AudioSpec
    121. SDL_AudioSpec wanted_spec;
    122. wanted_spec.freq = 44100;
    123. wanted_spec.format = AUDIO_S16SYS;
    124. wanted_spec.channels = 2;
    125. wanted_spec.silence = 0;
    126. wanted_spec.samples = 1024;
    127. wanted_spec.callback = fill_audio;
    128. wanted_spec.userdata = pCodecCtx;
    129. if (SDL_OpenAudio(&wanted_spec, NULL) < 0)
    130. {
    131. printf("can't open audio.\n");
    132. return -1;
    133. }
    134. //Play
    135. SDL_PauseAudio(0);
    136. // 分配输出音频数据
    137. uint8_t *out_buffer = nullptr;
    138. while (av_read_frame(pFormatCtx, pkt) >= 0)
    139. {
    140. if (pkt->stream_index == audio_index)
    141. {
    142. //一次send 多次recv
    143. int ret = avcodec_send_packet(pCodecCtx, pkt);
    144. if (ret < 0)
    145. continue;
    146. //释放资源
    147. av_packet_unref(pkt);
    148. while (1)
    149. {
    150. AVFrame *frame = recv(pCodecCtx);
    151. if (!frame)
    152. break;
    153. //输入的样本数
    154. int in_nb_samples = frame->nb_samples;//1024
    155. int out_linesize;
    156. int dst_nb_samples = av_rescale_rnd(in_nb_samples, 44100, frame->sample_rate, AV_ROUND_UP);
    157. //输出的样本数
    158. int out_buffer_size = av_samples_get_buffer_size(NULL, 2, dst_nb_samples, AV_SAMPLE_FMT_S16, 0);
    159. if(!out_buffer)
    160. out_buffer = (uint8_t *)av_malloc(out_buffer_size);
    161. //返回每个通道输出的样本数,错误时为负值
    162. int sampleCount = swr_convert(swrContext, &out_buffer, dst_nb_samples,
    163. (const uint8_t**)frame->data, in_nb_samples);
    164. if (sampleCount <= 0)
    165. {
    166. av_frame_free(&frame);
    167. break;
    168. }
    169. int outSize = sampleCount * 2 * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
    170. while (audio_len > 0)//Wait until finish
    171. SDL_Delay(1);
    172. //Set audio buffer (PCM data)
    173. audio_chunk = (Uint8 *)out_buffer;
    174. //Audio buffer length
    175. audio_len = outSize;
    176. audio_pos = audio_chunk;
    177. av_frame_free(&frame);
    178. }
    179. }
    180. else
    181. {
    182. //释放资源
    183. av_packet_unref(pkt);
    184. }
    185. }
    186. //--------------
    187. av_free(out_buffer);
    188. av_packet_free(&pkt);
    189. swr_close(swrContext);
    190. swr_free(&swrContext);
    191. avcodec_close(pCodecCtx);
    192. avcodec_free_context(&pCodecCtx);
    193. avformat_close_input(&pFormatCtx);
    194. SDL_CloseAudio();
    195. SDL_Quit();
    196. }

     5.相关推荐

    [总结]FFMPEG视音频编解码零基础学习方法_零基础ffmpeg 雷霄骅-CSDN博客 

    FFmpeg 音频解码(秒懂)-CSDN博客

    SDL2 播放音频数据(PCM)-CSDN博客

    SDL2 消息循环和事件响应-CSDN博客 

    SDL2 播放视频文件(MP4)-CSDN博客 

  • 相关阅读:
    C++中istream_iterator和ostream_iterator的源码分析
    如何成为一名高级数字 IC 设计工程师(1-7)Verilog 编码语法篇:常数
    项目启动 | 宏昌电器牵手盘古信息,数字化制造引领企业高质量发展
    java项目-第142期ssm美食推荐系统-ssm毕业设计_计算机毕业设计
    C++的string你可能会用,但是你模拟实现过了吗?带你实现以下string的重要接口!
    鸿蒙搭配前端开发:应用端与WEB端交互
    JavaEE进阶(5/27)Spring Boot
    计算机操作系统学习(一)引言
    Rn开发社区推荐组件
    高质量实现单文件导入、导出功能(使用EasyExcel )
  • 原文地址:https://blog.csdn.net/wzz953200463/article/details/134430093