• [FFmpeg学习]windows环境sdl播放音频试验


    参考资料

    FFmpeg和SDL2播放mp4_sdl 播放mp4 声音-CSDN博客

    SimplePlayer/SimplePlayer.c at master · David1840/SimplePlayer · GitHub

    在前面的学习中,通过获得的AVFrame进行了播放画面,

    [FFmpeg学习]初级的SDL播放mp4测试-CSDN博客

    播放音频原理类似,也是获取AVFrame的信息,

    1. extern "C" {
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. }
    8. // Simplest FFmpeg Sync Player.cpp : 定义控制台应用程序的入口点。
    9. //
    10. #include
    11. #include
    12. #include "SDL.h"
    13. static Uint8* audio_chunk;
    14. static Uint32 audio_len;
    15. static Uint8* audio_pos;
    16. #define MAX_AUDIO_FRAME_SIZE 19200
    17. //音频设备需要更多数据的时候会调用该回调函数
    18. void read_audio_data(void* udata, Uint8* stream, int len) {
    19. fprintf(stderr, "stream addr:%p, audio_len:%d, len:%d\n",
    20. stream,
    21. audio_len,
    22. len);
    23. //首先使用SDL_memset()将stream中的数据设置为0
    24. SDL_memset(stream, 0, len);
    25. if (audio_len == 0)
    26. return;
    27. len = (len > audio_len ? audio_len : len);
    28. SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    29. audio_pos += len;
    30. audio_len -= len;
    31. }
    32. #undef main
    33. int main(int argc, char* argv[]) {
    34. const char* file = "test.mp4";
    35. AVFormatContext* pFormatCtx = NULL; //for opening multi-media file
    36. int i, audioStream = -1;
    37. AVCodecParameters* pCodecParameters = NULL; //codec context
    38. AVCodecContext* pCodecCtx = NULL;
    39. const AVCodec* pCodec = NULL; // the codecer
    40. AVFrame* pFrame = NULL;
    41. AVPacket* packet;
    42. uint8_t* out_buffer;
    43. int64_t in_channel_layout;
    44. struct SwrContext* au_convert_ctx;
    45. if (avformat_open_input(&pFormatCtx, file, NULL, NULL) != 0) {
    46. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open video file!");
    47. return -1; // Couldn't open file
    48. }
    49. audioStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    50. if (audioStream == -1) {
    51. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Din't find a video stream!");
    52. return -1;// Didn't find a video stream
    53. }
    54. // Get a pointer to the codec context for the video stream
    55. pCodecParameters = pFormatCtx->streams[audioStream]->codecpar;
    56. // Find the decoder for the video stream
    57. pCodec = avcodec_find_decoder(pCodecParameters->codec_id);
    58. if (pCodec == NULL) {
    59. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported codec!\n");
    60. return -1; // Codec not found
    61. }
    62. // Copy context
    63. pCodecCtx = avcodec_alloc_context3(pCodec);
    64. if (avcodec_parameters_to_context(pCodecCtx, pCodecParameters) != 0) {
    65. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't copy codec context");
    66. return -1;// Error copying codec context
    67. }
    68. // Open codec
    69. if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
    70. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open decoder!\n");
    71. return -1; // Could not open codec
    72. }
    73. packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    74. av_init_packet(packet);
    75. pFrame = av_frame_alloc();
    76. uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;//输出声道
    77. int out_nb_samples = 1024;
    78. enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输出格式S16
    79. int out_sample_rate = 44100;
    80. int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
    81. int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
    82. out_buffer = (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
    83. //Init
    84. if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
    85. printf("Could not initialize SDL - %s\n", SDL_GetError());
    86. return -1;
    87. }
    88. SDL_AudioSpec spec;
    89. spec.freq = out_sample_rate;
    90. spec.format = AUDIO_S16SYS;
    91. spec.channels = out_channels;
    92. spec.silence = 0;
    93. spec.samples = out_nb_samples;
    94. spec.callback = read_audio_data;
    95. spec.userdata = pCodecCtx;
    96. if (SDL_OpenAudio(&spec, NULL) < 0) {
    97. printf("can't open audio.\n");
    98. return -1;
    99. }
    100. in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
    101. printf("in_channel_layout --->%d\n", in_channel_layout);
    102. au_convert_ctx = swr_alloc();
    103. au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
    104. in_channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
    105. swr_init(au_convert_ctx);
    106. SDL_PauseAudio(0);
    107. while (av_read_frame(pFormatCtx, packet) >= 0) {
    108. if (packet->stream_index == audioStream) {
    109. avcodec_send_packet(pCodecCtx, packet);
    110. while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
    111. swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t**)pFrame->data,
    112. pFrame->nb_samples); // 转换音频
    113. }
    114. audio_chunk = (Uint8*)out_buffer;
    115. audio_len = out_buffer_size;
    116. audio_pos = audio_chunk;
    117. while (audio_len > 0) {
    118. SDL_Delay(1);//延迟播放
    119. }
    120. }
    121. av_packet_unref(packet);
    122. }
    123. swr_free(&au_convert_ctx);
    124. SDL_Quit();
    125. return 0;
    126. }

    在windows上,如果有方法废弃的错误,

    错误    C4996    'AVCodecContext::channels': 被声明为已否决    

    可以设置SDL检查为否,来解决

    代码流程里,需要注意一下audio_callback是怎么回调的,

    audio_callback函数是由SDL音频系统在需要更多音频数据以填充音频缓冲区时自动调用的。当SDL音频系统开始播放音频时,它会周期性地调用音频回调函数以获取新的音频数据。音频回调函数的调用频率取决于音频采样率和缓冲区大小。

    main函数中,我们初始化SDL音频系统并设置了音频回调函数audio_callback

    1. if (SDL_Init(SDL_INIT_AUDIO) < 0) {
    2. fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
    3. exit(1);
    4. }
    5. // 设置音频回调函数
    6. SDL_AudioSpec desired;
    7. desired.freq = audioCodecContext->sample_rate;
    8. desired.format = AUDIO_S16SYS; // 使用16位有符号小端字节序样本
    9. desired.channels = audioCodecContext->channels;
    10. desired.samples = 4096; // 音频缓冲区大小,可以根据需要调整
    11. desired.callback = audio_callback;
    12. desired.userdata = NULL;
    13. // 打开音频设备
    14. SDL_AudioDeviceID deviceId = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE);
    15. if (deviceId <= 0) {
    16. fprintf(stderr, "Failed to open audio device: %s\n", SDL_GetError());
    17. return -1;
    18. }
    19. // 开始播放音频
    20. SDL_PauseAudioDevice(deviceId, 0);

    当我们调用SDL_OpenAudioDevice函数时,SDL音频系统会注册音频回调函数audio_callback。当我们调用SDL_PauseAudioDevice(deviceId, 0)开始播放音频时,SDL音频系统会根据音频参数(如采样率、通道数和缓冲区大小)定期调用audio_callback函数。

    audio_callback函数中,我们需要根据音频缓冲区的需求提供音频数据。这通常涉及从文件或实时流中解码音频数据,并将其传递给SDL音频系统。在我们的示例中,我们从全局变量audio_buffer中读取音频数据,该变量由swr_convert函数填充。

    总之,audio_callback函数是由SDL音频系统在需要更多音频数据以填充音频缓冲区时自动触发的。您无需手动调用此函数,SDL音频系统会负责调用它。只需确保在回调函数中提供正确的音频数据即可。

  • 相关阅读:
    在线智慧礼佛供品程序开发
    Buuctf [MRCTF2020]Ez_bypass 1 WP解析
    力扣算法 Java 刷题笔记【十大排序算法】代码实现
    Maven依赖scope为system级别部署时Jar包缺少解决
    【强化学习】《动手学强化学习》马尔可夫决策过程
    奔走相告,一波福利来袭! “Python量化场景编程技术手册”出炉!
    51单片机:实现CSGO中C4下包功能(附功能实现视频和代码详解)
    Stream流中的常用方法(forEach,filter,map,count,limit,skip,concat)和Stream流的特点
    年轻人不用太过于努力
    上海华清071班
  • 原文地址:https://blog.csdn.net/aaajj/article/details/139713457