这里引入FFmpeg库,获取音频流数据,然后通过FFmpeg将视频流解码成pcm原始数据,再将pcm数据送入到SDL库中实现音频播放。
SDL播放音频的流程如下:
- #include
- #include
- #include
-
- extern "C"
- {
- #include "libavcodec/avcodec.h"
- #include "libavformat/avformat.h"
- #include "libswscale/swscale.h"
- #include "libavutil/imgutils.h"
- #include "libswresample/swresample.h"
- };
-
- static Uint8 *audio_chunk;
- static Uint32 audio_len;
- static Uint8 *audio_pos;
-
- void fill_audio(void *udata, Uint8 *stream, int len)
- {
- //SDL 2.0
- SDL_memset(stream, 0, len);
- if (audio_len == 0) /* Only play if we have data left */
- return;
- len = (len > audio_len ? audio_len : len); /* Mix as much data as possible */
-
- SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME/2);
- audio_pos += len;
- audio_len -= len;
- }
-
- AVFrame *recv(AVCodecContext *codecCtx)
- {
- if (!codecCtx)
- {
- return NULL;
- }
-
- AVFrame *frame = av_frame_alloc();
- int ret = avcodec_receive_frame(codecCtx, frame);
-
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- {
- av_frame_free(&frame);
- return NULL;
- }
- else if (ret < 0)
- {
- av_frame_free(&frame);
- return NULL;
- }
-
- return frame;
- }
-
- #undef main
- int main(int argc, char* argv[])
- {
- av_register_all();
-
- ///ffmpeg
- avformat_network_init();
-
- AVFormatContext* pFormatCtx = NULL;
- const char* inputUrl = "./2.mp4";
-
- ///打开输入的流
- int ret = avformat_open_input(&pFormatCtx, inputUrl, NULL, NULL);
- if (ret != 0)
- {
- printf("Couldn't open input stream.\n");
- return -1;
- }
-
- //查找流信息
- if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
- {
- printf("Couldn't find stream information.\n");
- return -1;
- }
-
- //找到音频流索引
- int audio_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
- AVStream* st = pFormatCtx->streams[audio_index];
- AVCodec* codec = nullptr;
- //找到解码器
- codec = avcodec_find_decoder(st->codecpar->codec_id);
- if (!codec)
- {
- fprintf(stderr, "Codec not found\n");
- return -1;
- }
-
- //申请AVCodecContext
- AVCodecContext* pCodecCtx = avcodec_alloc_context3(codec);
- if (!pCodecCtx)
- {
- return -1;
- }
-
- avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audio_index]->codecpar);
-
- //打开解码器
- if ((ret = avcodec_open2(pCodecCtx, codec, NULL) < 0))
- {
- return -1;
- }
-
- AVPacket* pkt = av_packet_alloc();
-
- //------------SDL----------------
- //Output Info-----------------------------
- printf("---------------- File Information ---------------\n");
- av_dump_format(pFormatCtx, 0, inputUrl, 0);
- printf("-------------------------------------------------\n");
-
- SwrContext *swrContext = swr_alloc();
- if (!swrContext)
- {
- return -1;
- }
-
- swrContext = swr_alloc_set_opts(NULL, //ctx
- AV_CH_LAYOUT_STEREO, //输出channel布局
- AV_SAMPLE_FMT_S16, //输出的采样格式
- 44100, //采样率
- av_get_default_channel_layout(pCodecCtx->channels), //输入channel布局
- pCodecCtx->sample_fmt, //输入的采样格式
- pCodecCtx->sample_rate, //输入的采样率
- 0, NULL);
-
- // 初始化重采样上下文
- if (swr_init(swrContext) < 0)
- {
- swr_free(&swrContext);
- return -1;
- }
-
-
- if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
- {
- printf("Could not initialize SDL - %s\n", SDL_GetError());
- return -1;
- }
-
- //SDL_AudioSpec
- SDL_AudioSpec wanted_spec;
- wanted_spec.freq = 44100;
- wanted_spec.format = AUDIO_S16SYS;
- wanted_spec.channels = 2;
- wanted_spec.silence = 0;
- wanted_spec.samples = 1024;
- wanted_spec.callback = fill_audio;
- wanted_spec.userdata = pCodecCtx;
-
-
- if (SDL_OpenAudio(&wanted_spec, NULL) < 0)
- {
- printf("can't open audio.\n");
- return -1;
- }
-
- //Play
- SDL_PauseAudio(0);
-
- // 分配输出音频数据
- uint8_t *out_buffer = nullptr;
- while (av_read_frame(pFormatCtx, pkt) >= 0)
- {
- if (pkt->stream_index == audio_index)
- {
- //一次send 多次recv
- int ret = avcodec_send_packet(pCodecCtx, pkt);
- if (ret < 0)
- continue;
-
- //释放资源
- av_packet_unref(pkt);
-
- while (1)
- {
- AVFrame *frame = recv(pCodecCtx);
- if (!frame)
- break;
-
- //输入的样本数
- int in_nb_samples = frame->nb_samples;//1024
-
- int out_linesize;
- int dst_nb_samples = av_rescale_rnd(in_nb_samples, 44100, frame->sample_rate, AV_ROUND_UP);
-
- //输出的样本数
- int out_buffer_size = av_samples_get_buffer_size(NULL, 2, dst_nb_samples, AV_SAMPLE_FMT_S16, 0);
-
- if(!out_buffer)
- out_buffer = (uint8_t *)av_malloc(out_buffer_size);
-
- //返回每个通道输出的样本数,错误时为负值
- int sampleCount = swr_convert(swrContext, &out_buffer, dst_nb_samples,
- (const uint8_t**)frame->data, in_nb_samples);
-
- if (sampleCount <= 0)
- {
- av_frame_free(&frame);
- break;
- }
-
- int outSize = sampleCount * 2 * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
-
- while (audio_len > 0)//Wait until finish
- SDL_Delay(1);
-
- //Set audio buffer (PCM data)
- audio_chunk = (Uint8 *)out_buffer;
- //Audio buffer length
- audio_len = outSize;
- audio_pos = audio_chunk;
-
- av_frame_free(&frame);
- }
- }
- else
- {
- //释放资源
- av_packet_unref(pkt);
- }
- }
-
- //--------------
- av_free(out_buffer);
- av_packet_free(&pkt);
- swr_close(swrContext);
- swr_free(&swrContext);
- avcodec_close(pCodecCtx);
- avcodec_free_context(&pCodecCtx);
- avformat_close_input(&pFormatCtx);
-
- SDL_CloseAudio();
- SDL_Quit();
- }