音频数据格式:
基本格式内容:声道数channels、采样率sample_rate(采样设备每秒抽取样本的次数)
采样数据类型(量化精度)sample_fmt
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
带planar的数据是按各个声道分组排列的,以双声道为例,左声道数据存储在data[0],右声道数据存储在data[1],每个声道所占用的字节数为linesize[0]和linesize[1],播放之前必须先转换为非planar形式。
不带P(packed)的音频数据在存储时,是按照LRLRLR…的格式交替存储在data[0]中,linesize[0]表示总的数据量。
channel_layout
声道分布FFmpeg\libavutil\channel_layout.h中有定义,常用的有以下几种:
#define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER)//单通道
#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT) //双声道
#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER) //三声道
音频帧的数据量计算:
一帧音频的数据量=channel数 * nb_samples样本数 * 每个样本占用的字节数
av_samples_get_buffer_size(0, avFrame->channels, len, AV_SAMPLE_FMT_S16, 1);
音频播放时间计算:
采样率44100Hz表示每秒44100个sample,正常一帧为1024个sample,每帧播放时间为1024*1000(ms)/44100=23.2ms
设置QAudioOutput的音频格式QAudioFormat
setSampleRate(int); // 设置采样率
setChannelCount(int); // 设置声道数
setSampleSize(int); // 设置采样数据大小(bit位数)
setCodec(QString); // 设置解码器类型,裸流就设置为 "audio/pcm"
setSampleType(QAudioFormat::SampleType); // 设置采样数据格式
//其中SampleType中的枚举类型表示未知、有符号整型、无符号整型、浮点型。
enum SampleType { Unknown, SignedInt, UnSignedInt, Float };
//获得采样数据类型的字节数,乘以8就可以得到SampleSize的参数。
av_get_bytes_per_sample(audioCodecContext->sample_fmt);
FFmpeg中的音频格式转换
刚解码出的数据由于排列方式的原因不能直接播放,需要根据音频解码上下文设置并初始化转换上下文:
SwrContext*swrContext = swr_alloc_set_opts(
nullptr,
AV_CH_LAYOUT_STEREO,
AV_SAMPLE_FMT_S16,
44100,
audioCodecContext->channel_layout,
audioCodecContext->sample_fmt,
audioCodecContext->sample_rate,
0, nullptr);
if(swr_init(swrContext) < 0){
avformat_close_input(&avFormatContext);
avcodec_free_context(&videoCodecContext);
avcodec_free_context(&audioCodecContext);}
重采样:解码得到一帧音频后,先转换后计算所需内存大小,然后分配内存进行格式转换:
//输出样本数
int64_t out_nb_samples = av_rescale_rnd(
swr_get_delay(getswrcontext, avFrame->sample_rate) + avFrame->nb_samples,
44100, avFrame->sample_rate, AV_ROUND_UP);
//输出数据所需空间
int size = av_samples_get_buffer_size(0, avFrame->channels, static_cast<int>(out_nb_samples), AV_SAMPLE_FMT_S16, 1);
audiobuffer=new uint8_t[size];
//重采样样本数
int len = swr_convert(
getswrcontext,&audiobuffer,
static_cast<int>(out_nb_samples),
reinterpret_cast<const uint8_t **>(&avFrame->data),
static_cast<int>(avFrame->nb_samples));
if (len <0){
delete [] audiobuffer;
return;
}
//根据重采样样本数计算所需空间
int size1 = av_samples_get_buffer_size(0, avFrame->channels, len, AV_SAMPLE_FMT_S16, 1);
emit sendaudiomsg(audiobuffer,size1);
设置QAudioFormat,初始化QAudioOutput,并打开音频设备:
QAudioFormat outformat;
outformat.setSampleRate(44100);
outformat.setChannelCount(2);
outformat.setSampleSize(16);
outformat.setCodec("audio/pcm");
outformat.setSampleType(QAudioFormat::SignedInt);
outformat.setByteOrder(QAudioFormat::LittleEndian);
QAudioOutput *qOutput;
QAudioDeviceInfo outDeviceInfo(QAudioDeviceInfo::defaultOutputDevice());
if(outDeviceInfo.isNull()){
return;
}
if (!outDeviceInfo.isFormatSupported(outformat)){
outformat = outDeviceInfo.nearestFormat(outformat);
}
if (qOutput == nullptr){
qOutput = new QAudioOutput(outformat);
}
QIODevice *outputDevice = qOutput->start();
if (outputDevice == nullptr){
return;
}
//通过QIODevice::write()方法,写入音频数据进行播放
if(outputDevice){
outputDevice->write(reinterpret_cast<const char *>(audiomsg), size);}
//播放停止后停止释放QAudioOutput
qOutput->stop();
delete qOutput;
//QAudioOutput状态QAudio::State 判断是否放完