QT + FFMPEG实现基本播放器(四):音频播放实现

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";
}
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值