2016-12-02 121 views
0

收到消息「緩衝區下溢!」在每個寫這個簡單的程序後。QAudioOutput緩衝區下溢

Beep.hpp:

#pragma once 

#include <QTimer> 
#include <QAudioOutput> 

class Beep: public QObject 
{ 
    Q_OBJECT 

public: 
    explicit Beep(); 
    virtual ~Beep(); 

    void onTimer(); 

private: 
    QAudioOutput m_out; 
    QIODevice *m_outDev; 
    QTimer m_timer; 
}; 

Beep.cpp:

#include "Beep.hpp" 

int ms = 100; 

const QAudioFormat defaultAudioFormat = []() 
{ 
    QAudioFormat format; 
    format.setSampleRate(8000); 
    format.setChannelCount(1); 
    format.setSampleSize(16); 
    format.setCodec("audio/pcm"); 
    format.setByteOrder(QAudioFormat::LittleEndian); 
    format.setSampleType(QAudioFormat::SignedInt); 
    return format; 
}(); 

Beep::Beep() : 
     m_out(defaultAudioFormat), 
     m_outDev() 
{ 
    m_out.setBufferSize(16 * ms); 
    m_outDev = m_out.start(); 

    QObject::connect(&m_timer, &QTimer::timeout, this, &Beep::onTimer); 

    m_timer.setSingleShot(false); 
    m_timer.start(ms); 
} 

Beep::~Beep() 
{ 
} 

void Beep::onTimer() 
{ 
    std::vector<uint8_t> samples(16 * ms); 
    m_outDev->write((char*) &samples.front(), samples.size()); 
} 

main.cpp中:

#include <QCoreApplication> 

#include "Beep.hpp" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication app(argc, argv); 
    Beep beep; 
    return app.exec(); 
} 

這個測試程序是隻寫與零的緩衝器。有了真實的數據,就會有開裂聲音。

寫入更多數據或更改計時會使情況變得更糟。這段代碼有什麼問題?

回答

0

使用計時器是做錯的一種方法。

當音頻播放需要更多的數據

void AudioManager::audio_out_notify() { 
    qDebug() << "Audio notify"; 
    check_audio_playback(); 
} 

大多數下面的代碼將是無關緊要的,但它也被稱爲是音頻使用的通知()信號

void AudioManager::init_audio(AudioManager *mgr) { 
    if (mgr->stream_id == -1) return; 

    mgr->audio_format.setSampleRate(mgr->context->time_base.den); 
    mgr->audio_format.setSampleSize(16); 
    mgr->audio_format.setChannelCount(2); 
    mgr->audio_format.setCodec("audio/pcm"); 
    mgr->audio_format.setSampleType(QAudioFormat::SignedInt); 
    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); 
    if (!info.isFormatSupported(mgr->audio_format)) { 
     mgr->audio_format = info.nearestFormat(mgr->audio_format); 
    } 

    mgr->audio_out = new QAudioOutput(mgr->audio_format, nullptr); 
    mgr->audio_out->setNotifyInterval(15); 
    mgr->audio_out->setBufferSize(mgr->context->time_base.den * 4); // 1 second worth of stereo data 

    connect(mgr->audio_out, SIGNAL(notify()), mgr, SLOT(audio_out_notify())); 
    connect(mgr->audio_out, SIGNAL(stateChanged(QAudio::State)), mgr, SLOT(audio_out_state_changed(QAudio::State))); 
    qreal volume_out = (qreal)parent->volume/100.0f; 
    mgr->audio_out->setVolume(volume_out); 
    mgr->audio_out_device = mgr->audio_out->start(); 
} 

將其稱爲已經停止播放。

void AudioManager::check_audio_playback() { 
    if (stream_id == -1) return; 

    pthread_mutex_lock(&audio_mutex); 

    if (!audio_out->state() == QAudio::State::IdleState) { 
     pthread_mutex_unlock(&audio_mutex); 
     return; 
    } 

    if (parent->pts_start_time < 0.0) { 
     if (parent->Video.stream_id == -1 && decode_pos > 65) { // start playback 
      parent->pts_start_time = buffers[0].frame_time; 
      parent->sys_start_time = (double)parent->timer.elapsed()/1000.0; 
      qDebug() << "Audio playback started"; 
     } else { 
      pthread_mutex_unlock(&audio_mutex); 
      return; 
     } 
    } 

    if (playback_pos == decode_pos) { 
     pthread_mutex_unlock(&audio_mutex); 
     return; 
    } 



    AudioBuffer *buffer = nullptr; 
    double current_sys_time = ((double)parent->timer.elapsed()/1000.0) - parent->sys_start_time; 

    bool bounds = false; 
    int skipped = 0; 

    while (!bounds) { 
     if (playback_pos == decode_pos) bounds = true; 
     else { 
      AudioBuffer *temp_buffer = &buffers[playback_pos]; 
      double temp_time = temp_buffer->frame_time - parent->pts_start_time; 

      if (temp_time < current_sys_time) { 
       if (buffer) { 
        buffer->used = false; 
        skipped++; 
       } 
       buffer = temp_buffer; 
       playback_pos++; playback_pos %= MAX_AUD_BUFFERS; 
      } else { 
       bounds = true; 
      } 
     } 
    } 

    if (skipped > 0) qDebug("Skipped %d audio buffers on playback", skipped); 

    if (buffer) { 
     audio_out_device->write((const char *)buffer->data, buffer->buffer_size); 
     buffer->used = false; 
    } 

    pthread_mutex_unlock(&audio_mutex); 
} 

Qt的網站上的例子是,起初並不那麼明顯http://qt.apidoc.info/5.1.1/qtmultimedia/audiooutput.html但是當我把它在測試它是不是太糟糕了。

+0

通知是不是在這種情況下非常有用。我有一個外部計時器,所以它是「拉模式」(根據'audiooutput.html'示例)。 – Velkan

0

原因是音頻數據源不是「生產質量模塊」(這是一個虛擬測試類):定時器漂移的原因是其實際間隔爲10ms加上處理時間。

其他意見:

  • 使QAudioOutput::setBufferSize()更大
  • QAudioInput::read()QAudioOutput::write()在塊大小爲匹配QAudioInput::periodSize()QAudioOutput::periodSize()