BPlay1.0系列(7:音频播放)

BPlay1.0系列(6:视频播放) 已经完成了媒体文件的视频数据播放功能,但是播放时速度(帧率)无法控制并且是没有声音的,这就需要实现音频播放的功能来解决这两个问题。

1、音频解码

音频解码和视频解码流程上有很大的相似,都是将解封装后的流数据进行解码,音视频主控线程Bffmpeg::run()里面根据流索引将对应的流数据加入音频流队列后,音频解码/播放线程Baudio::run()就可以取到数据并进行相关处理(解码),此处可以复用视频解码里面的Bffmpeg::Decode(&pkt)方法:

/********************************
 * void BAudio::run()
 * 功能:音频解码/播放线程
 * *****************************/
void Baudio::run()
{
    while (audiorun)
    {
        AVPacket pkt;
        memset(&pkt , 0, sizeof(pkt));
        if (Bffmpeg::GetInstance()->GetAudioQue().que.size() == 0)  {
            msleep(1);
            continue;
        } else {
            /* 音频流编码数据出队列 */
            QMutexLocker Locker(&Bffmpeg::GetInstance()->GetAudioQue().mtx);
            pkt = Bffmpeg::GetInstance()->GetAudioQue().que.front();
            Bffmpeg::GetInstance()->GetAudioQue().que.pop_front();
        }
        
        /* 解码 */
        AVFrame* frame = Bffmpeg::GetInstance()->Decode(&pkt);
        av_packet_unref(&pkt); 
        if (frame == NULL) {
            continue;
        }
    }
    
    return;
}

2、重采样

在得到音频解码数据(PCM)之后,需要将其送到声卡才可播放,但是如果PCM的格式和声卡的配置参数不一样时,就会出现声音异常的现象,所以需要将两者的信息进行一次同步,这个同步的过程就是重采样。
采样率、样本大小和通道数是音频最关键的三个参数,重采样主要就是针对这三个参数进行了一次音频数据重新构建,知道达到声卡可以识别的格式。
首先需要为BPlay设置声卡的配置参数,这块直接使用QT的QAudioOutput类(音频播放),而此步放在媒体文件加载完成后(Bffmpeg::BLoadMediaFile(QString FilePath))进行即可:

/********************************
 * void Baudio::OpenAudioOutput()
 * 功能:初始化设置音频设备参数
 * *****************************/
void Baudio::OpenAudioOutput()
{
    QAudioFormat  fmt;                            // 设置音频输出格式
    fmt.setSampleRate(44100);                     // 1秒的音频采样率
    fmt.setSampleSize(16);                        // 声音样本的大小
    fmt.setChannelCount(Bffmpeg::GetInstance()->GetFormatContext()->streams[Bffmpeg::GetInstance()->GetAudioIndex()]->codec->channels);                 // 声道
    fmt.setCodec("audio/pcm");                    // 解码格式
    fmt.setByteOrder(QAudioFormat::LittleEndian);
    fmt.setSampleType(QAudioFormat::UnSignedInt); // 设置音频类型
    output = new QAudioOutput(fmt);
    io = output->start();                         // 播放开始

    return;
}

此处将采样率设置成了44100,样本大小为16bit,声道不变,接着在音频数据解码完成后将参数设置与其对应上即可。设置重采样参数:

    Swr = swr_alloc();
    AVCodecContext *AudioCodec = FormatContext->streams[Audio_index]->codec;
    swr_alloc_set_opts(Swr, AudioCodec->channel_layout, AV_SAMPLE_FMT_S16, 44100,                                 /* 目标格式 */
                           AudioCodec->channel_layout, AudioCodec->sample_fmt, AudioCodec->sample_rate, 0, 0);    /* 源格式 */
    swr_init(Swr);

对音频码流重采样并且播放:

/********************************
 * void BAudio::run()
 * 功能:音频解码/播放线程
 * *****************************/
void Baudio::run()
{
    char out[20000] = { 0 };

    while (audiorun)
    {
        AVPacket pkt;
        memset(&pkt , 0, sizeof(pkt));

        BLOG("output->bytesFree()[%d], output->periodSize()[%d]", output->bytesFree(), output->periodSize());
        if ((Bffmpeg::GetInstance()->GetAudioQue().que.size() == 0) || (output->bytesFree() < output->periodSize())) {
            msleep(1);
            continue;
        } else {
            /* 音频流编码数据出队列 */
            QMutexLocker Locker(&Bffmpeg::GetInstance()->GetAudioQue().mtx);
            pkt = Bffmpeg::GetInstance()->GetAudioQue().que.front();
            Bffmpeg::GetInstance()->GetAudioQue().que.pop_front();
        }
        
        /* 解码 */
        AVFrame* frame = Bffmpeg::GetInstance()->Decode(&pkt);
        av_packet_unref(&pkt); 
        if (frame == NULL) {
            continue;
        }
        
        /* 音频重采样 */
        AVFormatContext *FormatContext = Bffmpeg::GetInstance()->GetFormatContext();
        AVCodecContext *CodecContext = FormatContext->streams[Bffmpeg::GetInstance()->GetAudioIndex()]->codec;

        uint8_t *data[1];
        data[0] = (uint8_t *)out;
        int len = swr_convert(Bffmpeg::GetInstance()->GetSwrContext(), data, sizeof(out), (const uint8_t **)frame->data, frame->nb_samples);
        if (len <= 0)
        {
            return;
        }

        int datasize =  av_samples_get_buffer_size(NULL, CodecContext->channels, frame->nb_samples, AV_SAMPLE_FMT_S16, 0);

        /* 播放 */
        io->write(out, datasize);
    }
    
    return;
}

此时音频流数据就可以正常播放了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值