FFmpeg基础:SDL播放音频流数据

时间:2025-03-09 09:32:29
#include <iostream> #include <> extern "C" { #include <libavcodec/> #include <libavdevice/> #include <libavfilter/> #include <libavformat/> #include <libavformat/> #include <libavutil/> #include <libpostproc/> #include <libswresample/> #include <libswscale/> #include <libavutil/> #include <libavutil/> #include <libavformat/> } #include "" #include "SDL_thread.h" #include "SDL_syswm.h" #include "SDL_render.h" #include "SDL_audio.h" typedef struct _AudioPacket { AVPacketList* first, *last; int nb_packets, size; SDL_mutex* mutex; SDL_cond* cond; } AudioPacket; #define SDL_AUDIO_BUFFER_SIZE 1024 #define MAX_AUDIO_FRAME_SIZE 192000 static AVFrame wanted_frame; static AudioPacket audioq; static SDL_AudioSpec wantedSpec = { 0 }, audioSpec = { 0 }; struct SwrContext* swrCtx = NULL; //从数据队列里面取数据 int getAudioPacket(AudioPacket* q, AVPacket* pkt, int block) { AVPacketList* pktl; int ret; SDL_LockMutex(q->mutex); while (1) { pktl = q->first; if (pktl) { q->first = pktl->next; if (!q->first) q->last = NULL; q->nb_packets--; q->size -= pktl->pkt.size; *pkt = pktl->pkt; av_free(pktl); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } //从音频流中解析数据包 int audio_decode_frame(AVCodecContext* aCodecCtx, uint8_t* audio_buf, int buf_size) { static AVPacket pkt; static uint8_t* audio_pkt_data = NULL; static int audio_pkt_size = 0; static AVFrame frame; int len1; int data_size = 0; SwrContext* swr_ctx = NULL; while (1) { //取到数据之后解析数据 while (audio_pkt_size > 0) { int got_frame = 0; avcodec_send_packet(aCodecCtx, &pkt); avcodec_receive_frame(aCodecCtx, &frame); len1 = frame.pkt_size; if (len1 < 0) { audio_pkt_size = 0; break; } //拷贝音频数据 audio_pkt_data += len1; audio_pkt_size -= len1; data_size = 0; if (got_frame) { int linesize = 1; data_size = av_samples_get_buffer_size(&linesize, aCodecCtx->channels, frame.nb_samples, aCodecCtx->sample_fmt, 1); assert(data_size <= buf_size); memcpy(audio_buf, frame.data[0], data_size); } //获取通道信息 if (frame.channels > 0 && frame.channel_layout == 0) frame.channel_layout = av_get_default_channel_layout(frame.channels); else if (frame.channels == 0 && frame.channel_layout > 0) frame.channels = av_get_channel_layout_nb_channels(frame.channel_layout); if (swr_ctx) { swr_free(&swr_ctx); swr_ctx = NULL; } //对音频格式进行转换,重采样 swr_ctx = swr_alloc_set_opts(NULL, wanted_frame.channel_layout, (AVSampleFormat)wanted_frame.format, wanted_frame.sample_rate, frame.channel_layout, (AVSampleFormat)frame.format, frame.sample_rate, 0, NULL); if (!swr_ctx || swr_init(swr_ctx) < 0) { printf("swr_init failed\n"); } int dst_nb_samples = (int)av_rescale_rnd(swr_get_delay(swr_ctx, frame.sample_rate) + frame.nb_samples, wanted_frame.sample_rate, wanted_frame.format, AV_ROUND_INF); int len2 = swr_convert(swr_ctx, &audio_buf, dst_nb_samples, (const uint8_t**)frame.data, frame.nb_samples); if (len2 < 0) { printf("swr_convert failed\n"); } av_packet_unref(&pkt); if (swr_ctx) { swr_free(&swr_ctx); swr_ctx = NULL; } //返回数据长度 return wanted_frame.channels * len2 * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16); } //从队列里面取数据 if (getAudioPacket(&audioq, &pkt, 1) < 0) return -1; audio_pkt_data = pkt.data; audio_pkt_size = pkt.size; } } //音频数据包的回调函数 void audio_callback(void* userdata, Uint8* stream, int len) { AVCodecContext* aCodecCtx = (AVCodecContext*)userdata; int len1, audio_size; static uint8_t audio_buff[192000 * 3 / 2]; static unsigned int audio_buf_size = 0; static unsigned int audio_buf_index = 0; SDL_memset(stream, 0, len); while (len > 0) { if (audio_buf_index >= audio_buf_size) { audio_size = audio_decode_frame(aCodecCtx, audio_buff, sizeof(audio_buff)); if (audio_size < 0) { audio_buf_size = 1024; memset(audio_buff, 0, audio_buf_size); } else audio_buf_size = audio_size; audio_buf_index = 0; } //播放取到的音频数据 len1 = audio_buf_size - audio_buf_index; if (len1 > len) len1 = len; SDL_MixAudio(stream, audio_buff + audio_buf_index, len, SDL_MIX_MAXVOLUME); len -= len1; stream += len1; audio_buf_index += len1; } } //初始化音频数据包 void init_audio_packet(AudioPacket* q) { q->last = NULL; q->first = NULL; q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } //将数据包加入到队列中 int put_audio_packet(AVPacket* packet) { AVPacketList* pktl; AVPacket* newPkt; newPkt = (AVPacket*)av_mallocz_array(1, sizeof(AVPacket)); if (av_packet_ref(newPkt, packet) < 0) return -1; pktl = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if (!pktl) return -1; pktl->pkt = *newPkt; pktl->next = NULL; SDL_LockMutex(audioq.mutex); if (!audioq.last) audioq.first = pktl; else audioq.last->next = pktl; audioq.last = pktl; audioq.nb_packets++; audioq.size += newPkt->size; SDL_CondSignal(audioq.cond); SDL_UnlockMutex(audioq.mutex); return 0; } int main(int argc, char *argv[]) { int ret = -1; int audioStream; //媒体文件上下文 AVFormatContext *pFormatCtx = NULL; //音频解码器上下文 AVCodecContext *pCodecAudioCtx = NULL; //音频解码器 AVCodec *pAudioCodec = NULL; AVFrame *pFrame = NULL; AVPacket packet; SDL_Event event; if (argc < 2) { printf("Usage: command <file>\n"); return ret; } //初始化SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) printf("There is something wrong with your SDL Libs. Couldn't run"); //打开音频驱动 #ifdef _WIN32 SDL_AudioInit("directsound"); #endif //打开媒体文件,获取流信息 if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) { printf("Failed to open multi-media file\n"); goto __FAIL; } if (avformat_find_stream_info(pFormatCtx, NULL)<0) { printf("could not find stream info\n"); goto __FAIL; } //查找音频流 audioStream = -1; for (int i = 0; i<(int)pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0) { audioStream = i; } } if (audioStream == -1) { goto __FAIL; } //获取音频解码器 pAudioCodec = avcodec_find_decoder(pFormatCtx->streams[audioStream]->codecpar->codec_id); if (pAudioCodec == NULL) { goto __FAIL; } //创建解码器上下文并拷贝参数 pCodecAudioCtx = avcodec_alloc_context3(pAudioCodec); if (pCodecAudioCtx == NULL) { goto __FAIL; } ret = avcodec_parameters_to_context(pCodecAudioCtx, pFormatCtx->streams[audioStream]->codecpar); if (ret < 0) { goto __FAIL; } //打开解码器 ret = avcodec_open2(pCodecAudioCtx, pAudioCodec, NULL); if (ret < 0) { goto __FAIL; } //设置音频参数转换的上下文 swrCtx = swr_alloc(); if (swrCtx == NULL) { goto __FAIL; } //设置通道数,采样率,采样格式的输入输出格式 av_opt_set_channel_layout(swrCtx, "in_channel_layout", pCodecAudioCtx->channel_layout, 0); av_opt_set_channel_layout(swrCtx, "out_channel_layout", pCodecAudioCtx->channel_layout, 0); av_opt_set_int(swrCtx, "in_sample_rate", pCodecAudioCtx->sample_rate, 0); av_opt_set_int(swrCtx, "out_sample_rate", pCodecAudioCtx->sample_rate, 0); av_opt_set_sample_fmt(swrCtx, "in_sample_fmt", pCodecAudioCtx->sample_fmt, 0); av_opt_set_sample_fmt(swrCtx, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); int res = swr_init(swrCtx); if (res != 0) { goto __FAIL; } //打开音响设备 memset(&wantedSpec, 0, sizeof(wantedSpec)); wantedSpec.channels = pCodecAudioCtx->channels; wantedSpec.freq = pCodecAudioCtx->sample_rate; wantedSpec.format = AUDIO_S16SYS; wantedSpec.silence = 0; wantedSpec.samples = SDL_AUDIO_BUFFER_SIZE; wantedSpec.userdata = pCodecAudioCtx; //音频流的上下文 wantedSpec.callback = audio_callback; //设置数据包的回调函数 if (SDL_OpenAudio(&wantedSpec, &audioSpec) < 0) { printf("Failed to open audio"); goto __FAIL; } wanted_frame.format = AV_SAMPLE_FMT_S16; wanted_frame.sample_rate = audioSpec.freq; wanted_frame.channel_layout = av_get_default_channel_layout(audioSpec.channels); wanted_frame.channels = audioSpec.channels; //初始化数据包 init_audio_packet(&audioq); SDL_PauseAudio(0); pFrame = av_frame_alloc(); //读取数据包 while (av_read_frame(pFormatCtx, &packet) >= 0) { if (packet.stream_index == audioStream) { put_audio_packet(&packet); } else { SDL_Delay(1000 / 30); } SDL_PollEvent(&event); av_packet_unref(&packet); } __QUIT: ret = 0; __FAIL: //后处理清理数据 if (pFrame) { av_frame_free(&pFrame); } if (pCodecAudioCtx) { avcodec_close(pCodecAudioCtx); } if (pFormatCtx) { avformat_close_input(&pFormatCtx); } SDL_Quit(); return ret; }