使用QAudioOutput(qt)实时流式传输

时间:2015-08-17 12:06:06

标签: c++ qt audio streaming low-latency

我希望播放实时响应,并且没有明显的用户交互延迟。

要获得低延迟,我必须发送一小块pcm数据。 我在做什么:

    QAudioFormat format;
    format.setSampleRate(22050);
    format.setChannelCount(1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(format)) {
        qWarning()<<"raw audio format not supported by backend, cannot play audio.";
        return;
    }

    qAudioOutput = new QAudioOutput(format, NULL);

    qAudioDevice=qAudioOutput->start();

以后

void Enqueue(TYPESAMPLEPCM *data,int countBytes){
    while(qAudioOutput->bytesFree()<countBytes){
          Sleep(1);
    }
    qAudioDevice->write((char *)data,countBytes);
}

数据块是256个字节(128个样本会给出&#34;粒度&#34;大约6毫秒。

从具有高优先级的线程中的循环调用Enqueue,该线程提供数据块。因为它所谓的Enqueue速度远远快于渲染音频数据,所以没有延迟。

但它看起来有一个缓冲区不足的情况,因为声音播放但是有一种&#34;噼啪声&#34;经常发出噪音。

如果我将块大小提高到256个样本,问题几乎消失了。开头只有一些噼啪声(?)

平台是Windows和Qt 5.3。

这是正确的程序还是我遗漏了什么?

1 个答案:

答案 0 :(得分:6)

问题在于

void Enqueue(TYPESAMPLEPCM *data,int countBytes){
    while(qAudioOutput->bytesFree()<countBytes){
          Sleep(1);
    }
    qAudioDevice->write((char *)data,countBytes);
}

有点天真。

首先Sleep(1);。你在窗户上。问题是windows不是实时操作系统,并且预计时间分辨率约为10 - 15 ms。 这意味着当没有传入音频的地方时,你的睡眠时间比预期的多得多。

<强>第二即可。当音频输出不能消耗所提供的数据量时,你真的需要睡觉吗?你真正想要的是在音频输出消耗一些后提供一些音频。具体而言,它意味着:

  1. 设置QAudioOutput notify interval,即系统消耗音频数据的时间段并告诉您相关信息。
  2. 获取有关QAudioOutput消耗某些数据的通知。哪个位于与QAudioOutput::notify()
  3. 相关联的广告位中
  4. 当音频输出已满时,缓冲来自高优先级线程的数据块。
  5. 这给:

    QByteArray samplebuffer;
    
    //init code
    {
         qAudioOutput = new QAudioOutput(format, NULL);
         ...
         qAudioOutput->setNotifyInterval(128); //play with this number
         connect(qAudioOutput, SIGNAL(notify), someobject, SLOT(OnAudioNotify));
         ...
         qAudioDevice=qAudioOutput->start();
    }
    
    void EnqueueLock(TYPESAMPLEPCM *data,int countBytes)
    {
        //lock mutex
        samplebuffer.append((char *)data,countBytes);
        tryWritingSomeSampleData();
        //unlock mutex
    }
    
    //slot
    void SomeClass::OnAudioNotify()
    {
       //lock mutex
       tryWritingSomeSampleData()
       //unlock mutex
    }
    
    void SomeClass::tryWritingSomeSampleData()
    {
        int towritedevice = min(qAudioOutput->bytesFree(), samplebuffer.size());
        if(towritedevice > 0)
        {
            qAudioDevice->write(samplebuffer.data(),towritedevice);
            samplebuffer.remove(0,towritedevice); //pop front what is written
        }
    }
    

    如您所见,您需要保护samplebuffer免受并发访问。提供足够的互斥量。