【问题标题】:Realtime streaming with QAudioOutput (qt)使用 QAudioOutput (qt) 进行实时流式传输
【发布时间】:2015-11-10 01:32:33
【问题描述】:

我想播放实时声音响应,而不会明显滞后于用户交互。

为了降低延迟,我必须发送小块 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 个样本,“粒度”约为 6 毫秒。

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

但在我看来,存在缓冲区欠载的情况,因为声音在播放但有一种“噼啪”的常规噪音。

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

平台是 Windows 和 Qt 5.3。

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

【问题讨论】:

  • Enqueue 在哪里使用?谁叫它?
  • Enqueue 从提供数据块的高优先级线程中的循环调用。那里没有延迟,因为它调用 Enqueue 的速度远远快于渲染音频数据。 (我会把这个添加到帖子中)
  • 如果你打电话给Sleep(1),怎么没有延迟?请编辑

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


【解决方案1】:

问题是关于

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

有点天真。

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

第二。当音频输出无法消耗提供的数据量时,您真的需要睡觉吗?您真正想要的是在音频输出消耗了一些之后提供一些音频。具体来说就是:

  1. 设置QAudioOutput notify interval,即系统将消耗音频数据并告诉您的时间段。
  2. 收到有关QAudioOutput 消耗一些数据的通知。在连接到QAudioOutput::notify() 的插槽中
  3. 当音频输出已满时缓冲来自高优先级线程的数据块。

这个给:

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 免受并发访问。提供足够的互斥量。

【讨论】:

  • 我忘了提到我也试过用完全相同的结果注释掉 Sleep(1)。出于我的目的,通知间隔不应超过 10 毫秒。如果我理解你,你建议只是改变通知机制的等待循环。这可能会在喂食过程中释放时间,但看到它在不睡觉的情况下是如何做的,我不太有信心。
  • 是的,这是我的建议。但更重要的是,这个缓冲区调节数据块生产者和音频输出消费者之间的流量。它还避免了两件事:audiooutput 没有任何音频可播放(因为您正在睡觉\吃 cpu 和繁忙的循环)或没有循环丢弃音频的代码(因为写入的数据多于 audiooutput 允许的数据将失败\drop 部分或全部音频写的)
猜你喜欢
  • 1970-01-01
  • 2017-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多