QT + FFMPEG实现基本播放器(四):音频播放实现
对于音频这里采用两种播放方式,一种使用QT的QAudioOutput,另一种使用SDL进行音频播放。在使用QAudioOutput的时候需要在pro文件中添加 QT += multimedia
对于音频来说需要设置其相关的如声道,采样率,位数等参数:
bool AudioThread::Start()
{
Stop();
m_IsRuning = true;
#ifndef _USE_SDL_
//使用QAudioOutput进行播放
QAudioFormat fmt; //Qt音频的格式
fmt.setSampleRate(this->m_sampleRate); //采样率
fmt.setSampleSize(this->m_sampleSize); //位数
fmt.setChannelCount(this->m_channel); //声道
fmt.setCodec("audio/pcm"); //音频的格式
fmt.setByteOrder(QAudioFormat::LittleEndian); //次序
fmt.setSampleType(QAudioFormat::SignedInt); //样本的类别
m_AudioOutput = new QAudioOutput(fmt);
io = m_AudioOutput->start();
#else
//使用SDL进行播放
SDL_AudioSpec wanted_spec;
wanted_spec.freq = this->m_sampleRate; //采样率
wanted_spec.format = AUDIO_S16SYS; //音频数据格式
wanted_spec.channels = this->m_channel ; //声道
wanted_spec.silence = 0; //静音的值
wanted_spec.samples = 1024; //音频缓存区大小
wanted_spec.callback = fill_audio; //音频处理回调函数
if (SDL_OpenAudio(&wanted_spec, NULL)<0)
{
qDebug("can't open audio %s .\n", SDL_GetError());
return false;
}
//开启音频播放
SDL_PauseAudio(0);
#endif
this->start();
return true;
}
//SDL回调函数
void AudioThread::fill_audio(void *udata,Uint8 *stream,int len)
{
//SDL 2.0
SDL_memset(stream, 0, len);
if(audio_len==0)
return;
len = (len>audio_len?audio_len:len);
SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
//QAudioOutput音频数据写入函数
bool AudioThread::Write(const char *data, int datasize)
{
if (!data || datasize <= 0)
return false;
if (io)
{
io->write(data, datasize);
return true;
}
}
从avFrame中获取到的音频格式有可能与我们所设置的SDL或QAudioOutput的格式不同,因此需要将其进行重采样操作:
int AudioThread::AudioResample(AVCodecContext *ctx,char *out,AVFrame *avFrame)
{
if (!ctx || !avFrame || !out)
{
return 0;
}
if (m_aCtx == NULL)
{
m_aCtx = swr_alloc();
swr_alloc_set_opts(m_aCtx, ctx->channel_layout,
AV_SAMPLE_FMT_S16,
ctx->sample_rate,
ctx->channels,
ctx->sample_fmt,
ctx->sample_rate,
0, 0);
qDebug()<<"sample_rate "<< ctx->sample_rate;
//初始化样本转换
swr_init(m_aCtx);
}
uint8_t *data[1];
data[0] = (uint8_t *)out;
int len = swr_convert(m_aCtx, data, 10000, (const uint8_t **)avFrame->data, avFrame->nb_samples);
if (len <= 0)
{
return 0;
}
int outsize = av_samples_get_buffer_size(NULL, ctx->channels, avFrame->nb_samples, AV_SAMPLE_FMT_S16, 0);
return outsize;
}
在AudioThread的run函数中,循环读取g_MedieInfo.m_AudioPacketQueue中的音频数据,然后将其重采样之后播放到声卡:
void AudioThread::run(void)
{
char AudioDataOut[10000] = {0};
int data_count = 0;
qDebug()<<"Start Audio Thread";
while(m_IsRuning)
{
if( g_MedieInfo.m_AudioPacketQueue.empty())
{
msleep(1);
continue;
}
#ifndef _USE_SDL_
if (GetFree() < 10000)
{
msleep(1);
continue;
}
#endif
AVPacket avPacket;
g_MedieInfo.m_AudioPacketQueue.wait_and_pop(avPacket);
int ret = avcodec_send_packet(g_MedieInfo.avFormatContext->streams[g_MedieInfo.audioStreamIndex]->codec, &avPacket);
if (ret != 0)
{
qDebug()<<__func__<<__LINE__<<"send packet error";
continue;
}
ret = avcodec_receive_frame(g_MedieInfo.avFormatContext->streams[g_MedieInfo.audioStreamIndex]->codec,audioFrame);
if (ret < 0)
{
if (ret == AVERROR(EAGAIN))
{
continue;
}
if (ret == AVERROR_EOF)
{
qDebug()<<__func__<<__LINE__<<"receive frame error";
}
}
int len = AudioResample(g_MedieInfo.avFormatContext->streams[g_MedieInfo.audioStreamIndex]->codec,AudioDataOut,audioFrame);
#ifndef _USE_SDL_
Write(out, len);
#else
data_count += len;
//Set audio buffer (PCM data)
audio_chunk = (Uint8 *) AudioDataOut;
//Audio buffer length
audio_len = len;
audio_pos = audio_chunk;
while(audio_len>0)//Wait until finish
SDL_Delay(1);
#endif
av_packet_unref(&avPacket);
av_freep(&avPacket);
}
if (m_aCtx)
{
swr_free(&m_aCtx);
m_aCtx = NULL;
}
SDL_Quit();
qDebug()<<"Stop Audio Thread";
}