SDL播放音频和视频一样,需要先解码,再播放;但音频的播放不同于视频,他需要调用扬声器,播放音频不像视频一样给一帧数据播放一帧,音频是扬声器将缓冲区的数据播放完之后再向程序拿取数据。
SDL播放音频流程:
1)初始化SDL:SDL_Init()
2)设置音频空间参数,并定义填充缓存区的回调函数:设置SDL_AudioSpec参数
3)打开音频设备:SDL_OpenAudio()
4)播放:SDL_PauseAudio(0) //0表示播放,1表示暂停
5)关闭音频设备:SDL_CloseAudio()
6)退出SDL:SDL_Quit()
SDL播放音频流demo
#define SDL_MAIN_HANDLED //使用SDL必须定义的宏
#include
#include
using namespace std;
extern "C"
{
#include
#include
#include
#include //音频重采样保证解码后数据和播放设置的参数一样
#include
}
const char* infile = "1.mp4";
static Uint8* pAudio_chunk; //音频数据缓存区
static Uint32 audio_len; //剩余长度
static Uint8* pAudio_pos; //当前位置
//填充音频设备缓冲区的回调函数
void fill_audio_buffer(void* userdata, Uint8* stream, int len)
{
SDL_memset(stream, 0, len);
// 判断是否有读到数据
if (audio_len == 0)
return;
len = (len > audio_len ? audio_len : len);
SDL_MixAudio(stream, pAudio_pos, len, SDL_MIX_MAXVOLUME); //对音乐数据进行混音
pAudio_pos += len;
audio_len -= len;
}
int main(int argv, char** argc)
{
// const char* infile = argc[1];
AVFormatContext* afc = avformat_alloc_context(); //初始化上下文结构体AVFormatContext
if (avformat_open_input(&afc, infile, NULL, NULL) != 0)
{
cout << "open video error " << endl;
return -1;
}
if (avformat_find_stream_info(afc, NULL) < 0) //获取流信息,更新上下文结构AVFormatContext
{
cout << "find stream error" << endl;
return -1;
}
int audioflag = -1;
AVCodec* vdecodec, * adecodec;
AVCodecContext* acc = NULL;
for (int i = 0; i < afc->nb_streams; ++i)
{
if (afc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) //音频
{
audioflag = i;
adecodec = avcodec_find_decoder(afc->streams[audioflag]->codecpar->codec_id);
acc = avcodec_alloc_context3(adecodec);
avcodec_parameters_to_context(acc, afc->streams[i]->codecpar);
if (avcodec_open2(acc, adecodec, NULL) < 0)
{
cout << "open audio decodec error" << endl;
return -1;
}
}
}
if (audioflag == -1)
{
cout << "audio stream not find" << endl;
return -1;
}
int out_framesize = 1024;//nb_samples: AAC-1024 MP3-1152
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO; //音频输出格式布局
int out_nb_samples = out_framesize;
AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_FLTP;
int out_sample_rate = acc->sample_rate;//采样率
int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
//cout << "out_channels = " << out_channels << endl;
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
uint8_t** audio_data_buffer = NULL;//存储转换后的数据,再编码AAC
//SDL
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
//播放参数设置
SDL_AudioSpec audioSpec;
audioSpec.freq = out_sample_rate; //DSP频率 相当于采样率
audioSpec.format = AUDIO_S16SYS; //格式
audioSpec.channels = out_channels; //通道数
audioSpec.silence = 0; //静音值,一般为0
audioSpec.samples = out_framesize;
audioSpec.callback = fill_audio_buffer; //回调
audioSpec.userdata = acc; //用户数据
int a = SDL_OpenAudio(&audioSpec, nullptr); //打开音频设备,第二个参数为播放音频的设备,NULL表示默认设备
if (a < 0)
{
printf("Can not open audio! %d", a);
return -1;
}
SDL_PauseAudio(0);
//in_channel_layout = av_get_default_channel_layout(acc->channels);
SwrContext *au_convert_ctx = swr_alloc();
au_convert_ctx = swr_alloc_set_opts(au_convert_ctx,
out_channel_layout,//输出
out_sample_fmt, //编码前你希望的格式
out_sample_rate,//输出
av_get_default_channel_layout(acc->channels), //in_channel_layout, //输入
acc->sample_fmt,//PCM源文件的采样格式
acc->sample_rate, //输入
0, NULL);
swr_init(au_convert_ctx);
int data_count = 0;
AVFrame* aframe = av_frame_alloc();
AVPacket pkt;//= (AVPacket*)av_malloc(sizeof(AVPacket));
av_init_packet(&pkt);
while (av_read_frame(afc, &pkt) >= 0)
{
if (pkt.stream_index == audioflag) //音频解码
{
int ret = 0;
if (ret = avcodec_send_packet(acc, &pkt) != 0)
{
cout << "send audio packet to decodec error" << endl;
return -1;
}
while (ret >= 0)
{
ret = avcodec_receive_frame(acc, aframe);
if (ret == 0)
{
int audio_buffersize = aframe->linesize[0];//av_samples_get_buffer_size(NULL, acc->channels, acc->frame_size, acc->sample_fmt, 1); //获取音频数据大小
cout << "channels = " << acc->channels << "rate = " << acc->sample_rate << " nb_samples =" << aframe->nb_samples << " pcm size = " << audio_buffersize << " ="<linesize[0] << endl; //输出音频的通道数和采样率
data_count += audio_buffersize;
av_samples_alloc_array_and_samples(&audio_data_buffer,
NULL, out_channels,
192000*2,//aframe->nb_samples,
out_sample_fmt, 1);
int convert_size = swr_convert(au_convert_ctx,
audio_data_buffer,
192000,
(const uint8_t**)aframe->data,
aframe->nb_samples);
pAudio_chunk = *audio_data_buffer;
audio_len = audio_buffersize;
pAudio_pos = pAudio_chunk;
while (audio_len > 0)
SDL_Delay(1);
}
else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
cout << "receive audio frame error, need again" << endl;
break; //接收到的数据无效 需要重新读入
}
else
{
cout << "avcodec_receive_frame audio error code:" << ret << endl;
return -3; //解码错误,可选择直接退出程序
}
}
}
}
//再次解码输出解码器中的数据,否则会丢失部分数据
int ret = 0;
if (ret = avcodec_send_packet(acc, NULL) != 0)
{
cout << "send audio packet to decodec error" << endl;
return -1;
}
while (ret >= 0)
{
ret = avcodec_receive_frame(acc, aframe);
if (ret == 0)
{
int audio_buffersize = av_samples_get_buffer_size(NULL, acc->channels, acc->frame_size, acc->sample_fmt, 1); //获取音频数据大小
cout << "decodec channels = " << acc->channels << "rate = " << acc->sample_rate << " nb_samples =" << aframe->nb_samples << "pcm size = " << audio_buffersize << endl;
data_count += audio_buffersize;
pAudio_chunk = (Uint8*)aframe->data;
audio_len = audio_buffersize;
pAudio_pos = pAudio_chunk;
while (audio_len > 0)
SDL_Delay(1);
}
else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
cout << "receive audio frame error, need again" << endl;
break; //接收到的数据无效 需要重新读入
}
else
{
cout << "avcodec_receive_frame audio error code:" << ret << endl;
return -3; //解码错误,可选择直接退出程序
}
}
//退出SDL
SDL_Quit();
//释放资源
av_packet_unref(&pkt);
av_frame_free(&aframe);
avcodec_free_context(&acc);
avformat_close_input(&afc);
return 0;
}