1背景
上一篇处理了播放视频的,这一篇处理播放音频的。
2步骤
2.1 保存音频帧
添加 havframebuf.h
class HAVFrameBuf
{
public:
HAVFrameBuf();
int push(AVFrame* pFrame);
int pop(AVFrame* pFrame);
void clear();
int cache_num;
std::deque<AVFrame*> frames;
std::mutex mutex;
std::condition_variable buffer_not_full;
std::condition_variable buffer_not_empty;
};
以及havframebuf.cpp
HAVFrameBuf::HAVFrameBuf() {
cache_num=DEFAUTL_FRAME_CACHENUM;
}
int HAVFrameBuf::push(AVFrame *pFrame)
{
std::unique_lock<std::mutex> locker(this->mutex);
if(!pFrame || !pFrame->data[0] || pFrame->nb_samples <= 0)
return -10;
this->buffer_not_full.wait(locker,[this](){
return frames.size() < cache_num;
});
if(frames.size()>=(size_t)cache_num){
AVFrame* oldFrame =frames.front(); // 移动所有权
frames.pop_front(); // 先移除,避免析构冲突
av_frame_free(&oldFrame);
}
int ret=0;
AVFrame* newFrame = av_frame_alloc();
if (!newFrame) {
return -1; // 分配失败
}
// 复制帧内容(不复制数据,只增加引用计数)
if (av_frame_ref(newFrame, pFrame) < 0) {
av_frame_free(&newFrame);
return -2; // 复制失败
}
if (frames.empty()) { // 或者根据你的逻辑判断是否为空
ret = 1;
}
frames.push_back(newFrame);
locker.unlock();
this->buffer_not_empty.notify_one();
return ret;
}
int HAVFrameBuf::pop(AVFrame *pFrame)
{
std::unique_lock<std::mutex> locker(this->mutex);
if(frames.size()==0){
return -20;
}
this->buffer_not_empty.wait(locker, [this] {
return !frames.empty();
});
AVFrame* oldFrame =frames.front(); // 移动所有权
frames.pop_front(); // 先移除,避免析构冲突
if(frames.empty()){
return -30;
}
// 复制帧内容(不复制数据,只增加引用计数)
if (av_frame_ref(pFrame,oldFrame) < 0) {
av_frame_free(&oldFrame);
return -2; // 复制失败
}
locker.unlock();
this->buffer_not_full.notify_one();
return 0;
}
void HAVFrameBuf::clear()
{
std::unique_lock<std::mutex> locker(this->mutex);
frames.clear();
}
在hvideoplayer.h中添加音频帧存储
class HVideoPlayer
{
public:
HVideoPlayer();
public:
void set_media(HMedia& media);
virtual int start()=0;
virtual int stop()=0;
virtual int pause()=0;
virtual int resume()=0;
void clear_frame_cache(){
this->frame_buf.clear();
this->audio_frame_buf.clear();
}
int push_frame(HFrame* pFrame){
return frame_buf.push(pFrame);
}
int pop_frame(HFrame* pFrame){
return frame_buf.pop(pFrame);
}
int audio_push_frame(AVFrame* pFrame){
return this->audio_frame_buf.push(pFrame);
}
int audio_pop_frame(AVFrame* pFrame){
return this->audio_frame_buf.pop(pFrame);
}
public:
HMedia media;
int32_t width;
int32_t height;
int64_t duration;
int64_t start_time;
int eof;
int error;
int fps;
protected:
HFrameBuf frame_buf;//视频帧
HAVFrameBuf audio_frame_buf;//音频帧
};
添加HAudioWnd
class HAudioWnd : public QWidget
{
Q_OBJECT
public:
explicit HAudioWnd(QWidget *parent = nullptr);
signals:
public:
virtual void playAudio();
public:
AVFrame* last_frame;
};
hvideowidget.h 中添加声明
class HVideoWidget : public QFrame
{
Q_OBJECT
public:
explicit HVideoWidget(QWidget *parent = nullptr);
~HVideoWidget();
private:
Ui::HVideoWidget *ui;
HVideoWnd *videownd;
HAudioWnd *audiownd;
2.2 使用 QAudioOutput
添加AudioWnd
class AudioWnd : public HAudioWnd
{
Q_OBJECT
public:
explicit AudioWnd(QWidget *parent = nullptr);
signals:
public:
// HAudioWnd interface
public:
virtual void playAudio() override;
};
hvideowidget.cpp 中初始化
HVideoWidget::HVideoWidget(QWidget *parent)
: QFrame(parent)
, ui(new Ui::HVideoWidget)
{
ui->setupUi(this);
this->videownd=new GLWnd(this);
this->aspect_ratio.type=ASPECT_FULL;
this->audiownd=new AudioWnd(this);
this->timer=new QTimer(this);
timer->setTimerType(Qt::PreciseTimer);
connect(timer,SIGNAL(timeout()),this,SLOT(onTimerUpdate()));
}
在ffmpeg播放器19里分析了各种播放器,这里先尝试第一种。
尝试失败 multimedia 在6.7下面会有各种问题,放弃 doc.qt.io/archives/qt…
2.3 使用sdl播放音频
添加sdl2的include以及lib,参考 ffmpeg播放器10(window依赖库以及头文件)
添加以后的目录如下所示,include改为sdl2
hvideowidget.cpp 中初始化
HVideoWidget::HVideoWidget(QWidget *parent)
: QFrame(parent)
, ui(new Ui::HVideoWidget)
{
ui->setupUi(this);
this->videownd=new GLWnd(this);
this->aspect_ratio.type=ASPECT_FULL;
this->audiownd=new SDL2AudioWnd(this);
this->timer=new QTimer(this);
timer->setTimerType(Qt::PreciseTimer);
connect(timer,SIGNAL(timeout()),this,SLOT(onTimerUpdate()));
}
更新onTimerUpdate
void HVideoWidget::onTimerUpdate()
{
if(this->pImpl_player==NULL)return;
//提取一帧
if(this->pImpl_player->pop_frame(&this->videownd->last_frame)==0){
// update video frame
videownd->update();
if(this->pImpl_player->audio_pop_frame(this->audiownd->last_frame)==0){
// update audio frame
audiownd->playAudio();
}
}
}
2.4 音频解码器
hffplayer.h 添加音频
private:
AVFormatContext* fmt_ctx;
AVDictionary* fmt_opts;
AVCodecContext* codec_ctx;
AVDictionary* codec_opts;
AVCodecContext* audio_codec_ctx;
AVDictionary* audio_codec_opts;
hffplayer.cpp 初始化
HFFPlayer::HFFPlayer() {
fmt_opts=NULL;
fmt_ctx=NULL;
codec_ctx = NULL;
codec_opts=NULL;
audio_codec_ctx = NULL;
audio_codec_opts=NULL;
}
hffplayer.cpp 中的open
int HFFPlayer::open()
{
std::string ifile;
AVInputFormat* ifmt=NULL;
switch (media.type) {
case MEDIA_TYPE_FILE:
ifile=media.src;
break;
default:
break;
return -10;
}
int ret=0;
fmt_ctx=avformat_alloc_context();
if(fmt_ctx==NULL){
ret=-10;
return ret;
}
//打开媒体文件
av_dict_set(&fmt_opts,"buffer_size","2048000",0);
ret = avformat_open_input(&fmt_ctx, ifile.c_str(), ifmt, &fmt_opts);
if (ret != 0) {
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
return ret;
}
//查找是否有流
ret=avformat_find_stream_info(fmt_ctx,NULL);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
return ret;
}
//获取视频流音频流索引
video_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
audio_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
subtitle_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_SUBTITLE,-1,-1,NULL,0);
if(video_stream_index<0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-20;
return ret;
}
//根据索引获取视频流
AVStream* video_stream=fmt_ctx->streams[video_stream_index];
//根据索引获取音频流
AVStream* audio_stream=fmt_ctx->streams[audio_stream_index];
//获取视频源文件编码
AVCodecParameters* codec_param=video_stream->codecpar;
//获取音频文件编码
AVCodecParameters* audio_codec_param=audio_stream->codecpar;
//解码器
const AVCodec* codec=NULL;
codec=avcodec_find_decoder(codec_param->codec_id);
if(codec==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-30;
return ret;
}
const AVCodec* audio_codec=NULL;
audio_codec=avcodec_find_decoder(audio_codec_param->codec_id);
if(audio_codec==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-31;
return ret;
}
// 解码器上下文
codec_ctx=avcodec_alloc_context3(codec);
if(codec_ctx==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-40;
return ret;
}
audio_codec_ctx=avcodec_alloc_context3(audio_codec);
if(audio_codec_ctx==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
ret=-40;
return ret;
}
// 配置解码上下文的解码参数
ret=avcodec_parameters_to_context(codec_ctx,codec_param);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(codec_ctx){
avcodec_free_context(&codec_ctx);
codec_ctx=NULL;
}
return ret;
}
ret=avcodec_parameters_to_context(audio_codec_ctx,audio_codec_param);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(audio_codec_ctx){
avcodec_free_context(&audio_codec_ctx);
audio_codec_ctx=NULL;
}
return ret;
}
//配置引用计数
if(codec_ctx->codec_type==AVMEDIA_TYPE_VIDEO||
codec_ctx->codec_type==AVMEDIA_TYPE_AUDIO){
av_dict_set(&codec_opts,"refcounted_frames","1",0);
}
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);
}
//解码
ret=avcodec_open2(codec_ctx,codec,&codec_opts);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(codec_ctx){
avcodec_free_context(&codec_ctx);
codec_ctx=NULL;
}
return ret;
}
ret=avcodec_open2(audio_codec_ctx,audio_codec,&audio_codec_opts);
if(ret!=0){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(audio_codec_ctx){
avcodec_free_context(&audio_codec_ctx);
audio_codec_ctx=NULL;
}
return ret;
}
// 原始视频宽高以及像素格式
int sw,sh,dw,dh;
sw=codec_ctx->width;
sh=codec_ctx->height;
src_pix_fmt=codec_ctx->pix_fmt;
if(sw<=0|| sh <=0 || src_pix_fmt==AV_PIX_FMT_NONE){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(codec_ctx){
avcodec_free_context(&codec_ctx);
codec_ctx=NULL;
}
ret=-45;
return ret;
}
//目标视频宽高以及像素格式
dw=sw&(~3);
dh=sh&(~3);
dst_pix_fmt=AV_PIX_FMT_YUV420P;
//图像缩放以及像素格式转换
sws_ctx=sws_getContext(sw,sh,src_pix_fmt,dw,dh,dst_pix_fmt,SWS_BICUBIC,NULL,NULL,NULL);
if(sws_ctx==NULL){
if(fmt_ctx){
avformat_close_input(&fmt_ctx);
avformat_free_context(fmt_ctx);
fmt_ctx=NULL;
}
if(codec_ctx){
avcodec_free_context(&codec_ctx);
codec_ctx=NULL;
}
ret=-50;
return ret;
}
//视频帧存储
packet=av_packet_alloc();
frame=av_frame_alloc();
hframe.w=dw;
hframe.h=dh;
hframe.buf.resize(dw*dh*4);
if(dst_pix_fmt==AV_PIX_FMT_YUV420P){
hframe.type=PIX_FMT_IYUV;
hframe.bpp=12;
int y_size=dw*dh;
hframe.buf.len=y_size*3/2;
data[0]=(uint8_t*)hframe.buf.base;
data[1]=data[0]+y_size;
data[2]=data[1]+y_size/4;
linesize[0]=dw;
linesize[1]=linesize[2]=dw/2;
}
if (video_stream->avg_frame_rate.num && video_stream->avg_frame_rate.den) {
fps = video_stream->avg_frame_rate.num / video_stream->avg_frame_rate.den;
}
return ret;
}
再修改doTask
void HFFPlayer::doTask()
{
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 == video_stream_index) {
ret=avcodec_send_packet(this->codec_ctx,this->packet);
if(ret!=0){
av_packet_unref(this->packet);
return;
}
ret= avcodec_receive_frame(this->codec_ctx,this->frame);
if(ret!=0){
}else{
if(this->sws_ctx){
int h=sws_scale(this->sws_ctx,this->frame->data,
this->frame->linesize,0,this->frame->height,this->data,this->linesize);
if(h<=0 || h != this->frame->height){
return;
}
}
this->push_frame(&this->hframe);
break;
}
}
}
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;
}
}
}
}
2.5 sdl2播放音频帧
sdl2audiownd.h
class SDL2AudioWnd : public HAudioWnd
{
Q_OBJECT
public:
explicit SDL2AudioWnd(QWidget *parent = nullptr);
signals:
private:
SDL_AudioDeviceID m_audioDeviceID = 0;
SDL_AudioSpec m_obtainedSpec;
int m_sampleRate = 0;
int m_channels = 0;
int m_currentOffset=0;
SDL_AudioFormat m_format = AUDIO_S16;
SwrContext* m_swrCtx = nullptr; // 声明重采样上下文变量
AVChannelLayout out_ch_layout; // 输出声道布局
std::queue<QByteArray> m_audioQueue;
std::mutex m_audioMutex;
AVSampleFormat m_outSampleFmt;
public:
bool initSDLAudio();
void fillAudioBuffer(Uint8* stream, int len);
void addAudioFrame(AVFrame *frame);
void processAudioFrame(AVFrame* frame);
SDL_AudioFormat convertFFmpegToSDLFormat(AVSampleFormat fmt);
AVSampleFormat convertSDLToFFmpegFormat(SDL_AudioFormat sdlFormat);
// HAudioWnd interface
public:
void playAudio() override;
};
sdl2audiownd.cpp
SDL2AudioWnd::SDL2AudioWnd(QWidget *parent)
: HAudioWnd(parent)
{
this->initSDLAudio();
}
static void audioCallback(void *userdata, Uint8 *stream, int len)
{
SDL2AudioWnd* audioWnd=static_cast<SDL2AudioWnd*>(userdata);
audioWnd->fillAudioBuffer(stream,len);
}
bool SDL2AudioWnd::initSDLAudio()
{
// 1. 初始化SDL2音频子系统
if (SDL_Init(SDL_INIT_AUDIO) != 0) {
qCritical() << "SDL_Init error:" << SDL_GetError();
return false;
}
// 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:" << SDL_GetPixelFormatName(m_format)
<< "Sample rate:" << m_sampleRate
<< "Channels:" << m_channels
<< "Buffer size:" << m_obtainedSpec.samples;
return true;
}
AVSampleFormat SDL2AudioWnd::convertSDLToFFmpegFormat(SDL_AudioFormat sdlFormat) {
switch (sdlFormat) {
case AUDIO_U8: return AV_SAMPLE_FMT_U8;
case AUDIO_S16: return AV_SAMPLE_FMT_S16;
case AUDIO_S32: return AV_SAMPLE_FMT_S32;
case AUDIO_F32: return AV_SAMPLE_FMT_FLT;
default: return AV_SAMPLE_FMT_NONE;
}
}
void SDL2AudioWnd::fillAudioBuffer(Uint8 *stream, int len)
{
// 清空目标缓冲区(填充静音)
SDL_memset(stream, 0, len);
// 锁定音频队列
std::unique_lock<std::mutex> lock(m_audioMutex, std::try_to_lock);
if (!lock.owns_lock() || m_audioQueue.empty()) {
return; // 无法锁定或队列为空
}
int bytesWritten = 0;
// 持续填充直到缓冲区满或队列空
while (bytesWritten < len && !m_audioQueue.empty()) {
QByteArray& audioData = m_audioQueue.front();
int remaining = audioData.size() - m_currentOffset;
// 计算本次可写入的字节数
int toCopy = std::min(len - bytesWritten, remaining);
// 复制数据到音频缓冲区
SDL_memcpy(stream + bytesWritten,
audioData.constData() + m_currentOffset,
toCopy);
// 更新指针和计数器
bytesWritten += toCopy;
m_currentOffset += toCopy;
// 检查当前数据块是否用完
if (m_currentOffset >= audioData.size()) {
// 移除已用完的数据块
m_audioQueue.pop();
m_currentOffset = 0;
}
}
}
void SDL2AudioWnd::addAudioFrame(AVFrame *frame)
{
// 确保帧有效
if (!frame || !frame->data[0] || frame->nb_samples <= 0) {
return;
}
// 计算PCM数据大小
int bytesPerSample = SDL_AUDIO_BITSIZE(m_format) / 8;
int dataSize = frame->nb_samples * m_channels * bytesPerSample;
// 转换为QByteArray
QByteArray audioData(reinterpret_cast<const char*>(frame->buf[0]), dataSize);
// 添加到队列(线程安全)
std::lock_guard<std::mutex> lock(m_audioMutex);
m_audioQueue.push(audioData);
// 限制队列大小(防止内存无限增长)
const int maxQueueSize = 10; // 最多存储10个数据块
while (m_audioQueue.size() > maxQueueSize) {
m_audioQueue.pop();
qWarning() << "Audio buffer overflow, dropped frame";
}
}
SDL_AudioFormat SDL2AudioWnd::convertFFmpegToSDLFormat(AVSampleFormat fmt)
{
switch (fmt) {
case AV_SAMPLE_FMT_U8:
return AUDIO_U8;
case AV_SAMPLE_FMT_S16:
return AUDIO_S16SYS;
case AV_SAMPLE_FMT_S32:
return AUDIO_S32SYS;
case AV_SAMPLE_FMT_FLT:
return AUDIO_F32SYS;
case AV_SAMPLE_FMT_DBL:
qWarning() << "Unsupported sample format: AV_SAMPLE_FMT_DBL";
return AUDIO_F32SYS; // 降级处理
case AV_SAMPLE_FMT_U8P:
qWarning() << "Unsupported planar format: AV_SAMPLE_FMT_U8P";
return AUDIO_U8;
case AV_SAMPLE_FMT_S16P:
qWarning() << "Unsupported planar format: AV_SAMPLE_FMT_S16P";
return AUDIO_S16SYS;
case AV_SAMPLE_FMT_S32P:
qWarning() << "Unsupported planar format: AV_SAMPLE_FMT_S32P";
return AUDIO_S32SYS;
case AV_SAMPLE_FMT_FLTP:
qWarning() << "Unsupported planar format: AV_SAMPLE_FMT_FLTP";
return AUDIO_F32SYS;
case AV_SAMPLE_FMT_DBLP:
qWarning() << "Unsupported planar format: AV_SAMPLE_FMT_DBLP";
return AUDIO_F32SYS;
default:
qWarning() << "Unknown sample format:" << fmt;
return AUDIO_S16SYS; // 默认格式
}
}
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);
}
void SDL2AudioWnd::playAudio()
{
this->processAudioFrame(this->last_frame);
// this->addAudioFrame(this->last_frame);
}