ffmpeg播放器22(音频播放)

52 阅读11分钟

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

image.png

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);
}

3代码分支

github.com/xcyxiner/xp…

总分支 github.com/xcyxiner/xp…

4 目前全是杂音,下次再找哪里出的问题 未完待续