1背景
上一篇提到 目前全是杂音,下次再找哪里出的问题。这一篇开始找分析问题。
2步骤
2.1 播放设备检查---无效
一开始以为是参数的问题,修改了下面的各种参数,以及更换设备名称都没效果
// 2. 设置期望的音频参数
SDL_AudioSpec desiredSpec;
SDL_zero(desiredSpec);
// 使用音频帧中的参数(这些应该在解码时设置)
desiredSpec.freq = m_sampleRate; // 采样率 (例如44100)
desiredSpec.channels = m_channels; // 声道数 (例如2)
desiredSpec.format = m_format; // 样本格式 (例如AUDIO_S16)
desiredSpec.samples = 1024; // 音频缓冲区大小(建议2048或4096)
desiredSpec.callback = audioCallback; // 回调函数
desiredSpec.userdata = this; // 传递实例指针
// 3. 打开音频设备
m_audioDeviceID = SDL_OpenAudioDevice(
nullptr, // 使用默认设备
0, // 0=输出设备,1=输入设备
&desiredSpec,
&m_obtainedSpec,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE |
SDL_AUDIO_ALLOW_CHANNELS_CHANGE
);
if (m_audioDeviceID == 0) {
qCritical() << "Failed to open audio device:" << SDL_GetError();
return false;
}
// 4. 检查实际获得的音频参数
if (desiredSpec.format != m_obtainedSpec.format) {
qWarning() << "Using different audio format:"
<< "Desired:" << SDL_GetPixelFormatName(desiredSpec.format)
<< "Obtained:" << SDL_GetPixelFormatName(m_obtainedSpec.format);
}
if (desiredSpec.channels != m_obtainedSpec.channels) {
qWarning() << "Using different channel count:"
<< "Desired:" << (int)desiredSpec.channels
<< "Obtained:" << (int)m_obtainedSpec.channels;
}
if (desiredSpec.freq != m_obtainedSpec.freq) {
qWarning() << "Using different sample rate:"
<< "Desired:" << desiredSpec.freq
<< "Obtained:" << m_obtainedSpec.freq;
}
// 5. 保存实际获得的参数
m_sampleRate = m_obtainedSpec.freq;
m_channels = m_obtainedSpec.channels;
m_format = m_obtainedSpec.format;
m_outSampleFmt = convertSDLToFFmpegFormat(m_format);
// 6. 开始播放
SDL_PauseAudioDevice(m_audioDeviceID, 0);
qDebug() << "SDL2 audio initialized:"
<< "Format:" << m_format
<< "Sample rate:" << m_sampleRate
<< "Channels:" << m_channels
<< "Buffer size:" << m_obtainedSpec.samples;
2.2 重采样检查---无效
上面的播放设备没问题,就开始检查重采样
void SDL2AudioWnd::processAudioFrame(AVFrame *inputFrame)
{
AVChannelLayout in_ch_layout;
av_channel_layout_copy(&in_ch_layout, &inputFrame->ch_layout);
// 2. 设置输出声道布局(立体声)
AVChannelLayout out_ch_layout;
av_channel_layout_default(&out_ch_layout, m_channels); // 2表示立体声
// 2. 检查是否需要重采样
bool needsResample =
(inputFrame->sample_rate != this->m_sampleRate) ||
!av_channel_layout_compare(&in_ch_layout, &out_ch_layout) ||
(inputFrame->format != m_outSampleFmt);
// 3. 创建或更新重采样上下文(如果需要)
if (!m_swrCtx && needsResample) {
m_swrCtx = swr_alloc();
// 设置重采样参数(兼容FFmpeg 5.0+)
av_opt_set_chlayout(m_swrCtx, "in_chlayout", &in_ch_layout, 0);
av_opt_set_chlayout(m_swrCtx, "out_chlayout", &out_ch_layout, 0);
av_opt_set_int(m_swrCtx, "in_sample_rate", inputFrame->sample_rate, 0);
av_opt_set_int(m_swrCtx, "out_sample_rate", m_sampleRate, 0);
av_opt_set_sample_fmt(m_swrCtx, "in_sample_fmt", static_cast<AVSampleFormat>(inputFrame->format), 0);
av_opt_set_sample_fmt(m_swrCtx, "out_sample_fmt",m_outSampleFmt, 0);
int ret=swr_init(m_swrCtx);
// 初始化重采样器
if (ret < 0) {
qWarning() << "Failed to initialize resampler";
swr_free(&m_swrCtx);
return;
}
}
// 4. 创建输出帧
AVFrame* outputFrame = av_frame_alloc();
outputFrame->sample_rate = this->m_sampleRate;
outputFrame->format = m_outSampleFmt;
// 设置输出声道布局
av_channel_layout_copy(&outputFrame->ch_layout, &out_ch_layout);
// 5. 计算输出样本数
int64_t delay = swr_get_delay(m_swrCtx, inputFrame->sample_rate);
int64_t out_samples = av_rescale_rnd(
delay + inputFrame->nb_samples,
m_sampleRate,
inputFrame->sample_rate,
AV_ROUND_UP
);
outputFrame->nb_samples = (int)out_samples;
// 6. 分配输出帧缓冲区
int ret = av_frame_get_buffer(outputFrame, 0);
if (ret < 0) {
qWarning() << "Failed to allocate output frame buffer";
av_frame_free(&outputFrame);
return;
}
// 7. 执行重采样
ret = swr_convert_frame(m_swrCtx, outputFrame, inputFrame);
if (ret < 0) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(ret, errbuf, sizeof(errbuf));
qWarning() << "Resampling failed:" << errbuf;
av_frame_free(&outputFrame);
return;
}
addAudioFrame(outputFrame);
}
这里重点修改了重采样参数,打断点,依旧没有任何效果。检查了视频帧的格式,输出的格式。
2.3 音频帧存储问题---无效
上面的没解决之后,尝试ffmpeg解析方面去检查问题
while (!this->quit) {
av_init_packet(packet);
int ret=av_read_frame(this->fmt_ctx,this->packet);
if(ret!=0){
if(!this->quit){
}
av_packet_unref(this->packet);
return;
}
if (packet->stream_index == audio_stream_index) {
ret=avcodec_send_packet(this->audio_codec_ctx,this->packet);
if(ret!=0){
av_packet_unref(this->packet);
return;
}
ret= avcodec_receive_frame(this->audio_codec_ctx,this->frame);
if(ret!=0){
}else{
this->audio_push_frame(this->frame);
break;
}
}
}
这里打了断点,检查了上面的参数设置,取消refcounted_frames都没任何效果
if(audio_codec_ctx->codec_type==AVMEDIA_TYPE_VIDEO||
audio_codec_ctx->codec_type==AVMEDIA_TYPE_AUDIO){
av_dict_set(&audio_codec_opts,"refcounted_frames","1",0);
}
2.4 检查播放---最终原因
- 在
addAudioFrame中,我们使用frame->buf[0]来获取数据。但是,AVFrame的数据存储在data指针数组中,而不是buf。buf是用于引用计数的,如果没有启用引用计数,那么buf可能为空,而数据在data中。我们应该使用frame->data[0]。 - 我们只考虑了平面格式(planar)吗?在
addAudioFrame中,我们假设音频是交错的(interleaved),因为我们只取frame->data[0]。实际上,如果格式是平面的,那么每个声道的数据在data[0],data[1]等。而重采样输出如果是交错的,那么所有数据都在data[0]中。