使用QAudioOutput(qt)进行实时流式传输 [英] Realtime streaming with QAudioOutput (qt)

查看:1347
本文介绍了使用QAudioOutput(qt)进行实时流式传输的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想播放实时声音,并且没有用户交互的明显滞后.

I want to play real-time sounds responding with no appreciable lag to user interaction.

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

To have low latency I have to send small chunks of pcm data. What I am doing:

    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毫秒的粒度".

The chunks of data are 256 bytes (128 samples that would give "granularity" of around 6 milliseconds.

队列从提供数据块的高优先级线程中的循环中调用.那里没有延迟,因为它调用Enqueue的速度比呈现音频数据快得多.

Enqueue is called from a loop in a thread with high priority that provides the data chunks. There's no latency there as the speed it calls Enqueue is by far faster than rendering the audio data.

但是在我看来,存在缓冲欠载的情况,因为声音播放但带有嘶哑"的常规噪声.

But it looks to me there's a buffer underrun situation because the sound plays but with kind of a "crackling" regular noise.

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

If I raise the chunk size to 256 samples the problem almost disappears. Only some crackling at the beginning (?)

该平台是Windows和Qt 5.3.

The platform is Windows and Qt 5.3.

是正确的程序还是我遗漏了一些东西?

Is that the right procedure or I am missing something?

推荐答案

问题与

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

有点天真

首先Sleep(1);.您在Windows上.问题在于,windows不是实时操作系统,并且其时间分辨率大约为10 - 15 ms. 这意味着当没有地方可以接收音频时,您的睡眠将比您预期的要多得多.

First of all, Sleep(1);. You are on windows. The problem is that windows is not a realtime os, and is expected to have a time resolution around 10 - 15 ms. Which means when there is no place for incoming audio you are going to sleep lot more than you expect.

第二.当音频输出无法使用所提供的数据量时,您真的需要睡觉吗?您真正想要的是在音频输出消耗一些音频之后提供一些音频.具体而言,它表示:

Second. Do you really need to sleep when audio output cannot consume the amount of data which was provided? What you really want to is to provide some audio after the audio output has consumed some. In concrete terms, it means :

  1. 设置 QAudioOutput通知间隔,即系统将使用音频数据并将其告知您.
  2. 获取有关QAudioOutput消耗某些数据的通知.在连接到QAudioOutput::notify()
  3. 的插槽中
  4. 当音频输出已满时,缓冲来自高优先级线程的数据块.
  1. Setting the QAudioOutput notify interval, ie the period at which the system will consume the audio data and tell you about it.
  2. Getting notified about QAudioOutput consuming some data. Which is in a slot connected to QAudioOutput::notify()
  3. Buffering data chunks which come from your high priority thread when audio output is full.

这给了:

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

As you see you need to protect samplebuffer from concurrent access. Provide adequate mutex.

这篇关于使用QAudioOutput(qt)进行实时流式传输的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆