我有一个音乐播放器,可以生成模拟声音并使用一些音频库在PC上播放它。
在portaudio库中,我有一个线程来生成声音,然后将其推入出队列,说明在正确的时间过去了,并处理了另一帧声音。
portaudio在另一个线程中具有回调函数,该线程在适当的时候要求使用音频数据。
在此例行程序中,我从出站队列中获取数据(用作FIFO)并使用它们。
程序运行良好,持续了几秒钟/分钟,然后AddressSanitizer从双端队列捕获了堆缓冲区溢出。
这里有很多有关代码的知识。
产生声音并挤入出场的常规程序:
void AudioDriverPortAudio::play(void* buffer, unsigned long int bufferSize)
{
unsigned char* memory=(unsigned char*)buffer;
#ifdef XSID_PORTAUDIO_DEBUG
cout << "AudioDriverPortAudio::play" << endl;
#endif
// insert the data at the end of queue
for (unsigned long int i=0; i<bufferSize; i++) {
memoryDeque.push_back(*memory);
memory++;
}
std::cerr << memoryDeque.size() << std::endl;
int nSize; // size of one sample
if (config.precision == AudioConfig::BITS_8) nSize=1;
else nSize=2;
nSize*=config.channels;
// delay needed by actual configuration
unsigned int delay=1000*(bufferSize/nSize)/config.frequency;
// compensate for not integer delay to use
if (memoryDeque.size() > 3*bufferSize) delay++;
// passed time from last call
unsigned long int passedTime=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()-portTime;
// calculate new delay to use
signed int newDelay=delay-passedTime;
if (memoryDeque.size()>bufferSize)
if (newDelay>0) Pa_Sleep(newDelay);
if (memoryDeque.size()>2*bufferSize) Pa_StartStream( stream );
// remember the actual time
portTime=std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
portaudio的回调功能:
int AudioDriverPortAudio:: portaudio_callback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) {
#ifdef XSID_PORTAUDIO_DEBUG
cout << "portaudio_callback " << endl;
#endif
//unsigned char value;
unsigned char *out = (unsigned char*)outputBuffer;
AudioDriverPortAudio* userD=(AudioDriverPortAudio *) userData;
(void) inputBuffer; /* Prevent unused variable warning. */
(void) timeInfo; /* Prevent unused variable warning. */
(void) statusFlags; /* Prevent unused variable warning. */
(void) userData; /* Prevent unused variable warning. */
std::cerr << userD->memoryDeque.size() << " " << framesPerBuffer << std::endl;
int nSize; // size of one sample
if (userD->lastConfig.precision == AudioConfig::BITS_8) nSize=1;
else nSize=2;
nSize*=userD->lastConfig.channels;
if (userD->memoryDeque.size()< nSize*framesPerBuffer) {
std::cerr << "Under-run buffer";
return 0;
}
for (int i=0; i<nSize*framesPerBuffer; i++) {
// prevent empty buffer usage
if (userD->memoryDeque.empty()) break;
*out++=userD->memoryDeque.front();
userD->memoryDeque.pop_front();
}
return paContinue;
}
这是错误:
=================================================================
==136487==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x615000affc80 at pc 0x000000525ed0 bp 0x7fcda22a1bb0 sp 0x7fcda22a1ba0
READ of size 1 at 0x615000affc80 thread T35
76672
#0 0x525ecf in AudioDriverPortAudio::portaudio_callback(void const*, void*, unsigned long, PaStreamCallbackTimeInfo const*, unsigned long, void*) ../src/audio/AudioDriverPortAudio.cpp:301
#1 0x7fce04a52d7c (/lib64/libportaudio.so.2+0x9d7c)
#2 0x7fce04a54680 (/lib64/libportaudio.so.2+0xb680)
#3 0x7fce04a5d895 (/lib64/libportaudio.so.2+0x14895)
#4 0x7fce04a844e1 in start_thread (/lib64/libpthread.so.0+0x94e1)
#5 0x7fce022b26a2 in clone (/lib64/libc.so.6+0x1016a2)
0x615000affc80 is located 0 bytes to the right of 512-byte region [0x615000affa80,0x615000affc80)
allocated by thread T34 (QThread) here:
#0 0x7fce04c94a97 in operator new(unsigned long) (/lib64/libasan.so.5+0x10fa97)
#1 0x51d45e in __gnu_cxx::new_allocator<unsigned char>::allocate(unsigned long, void const*) /usr/include/c++/9/ext/new_allocator.h:114
#2 0x51d3d6 in std::allocator_traits<std::allocator<unsigned char> >::allocate(std::allocator<unsigned char>&, unsigned long) /usr/include/c++/9/bits/alloc_traits.h:444
#3 0x51d2c9 in std::_Deque_base<unsigned char, std::allocator<unsigned char> >::_M_allocate_node() /usr/include/c++/9/bits/stl_deque.h:620
#4 0x5227be in void std::deque<unsigned char, std::allocator<unsigned char> >::_M_push_back_aux<unsigned char const&>(unsigned char const&) /usr/include/c++/9/bits/deque.tcc:492
#5 0x5220fb in std::deque<unsigned char, std::allocator<unsigned char> >::push_back(unsigned char const&) /usr/include/c++/9/bits/stl_deque.h:1579
#6 0x525681 in AudioDriverPortAudio::play(void*, unsigned long) ../src/audio/AudioDriverPortAudio.cpp:181
#7 0x4f9e02 in Player::playAudio(void*, unsigned long) ../src/Player.cpp:620
#8 0x4fb07e in Player::playJob(QThread*) ../src/Player.cpp:783
#9 0x4fb29a in PlayerThread::run() ../src/Player.cpp:811
#10 0x7fce02795e25 (/lib64/libQt5Core.so.5+0xc0e25)
Thread T35 created by T34 (QThread) here:
#0 0x7fce04bbf955 in pthread_create (/lib64/libasan.so.5+0x3a955)
#1 0x7fce04a65f47 (/lib64/libportaudio.so.2+0x1cf47)
Thread T34 (QThread) created by T0 here:
#0 0x7fce04bbf955 in pthread_create (/lib64/libasan.so.5+0x3a955)
#1 0x7fce0279597e in QThread::start(QThread::Priority) (/lib64/libQt5Core.so.5+0xc097e)
SUMMARY: AddressSanitizer: heap-buffer-overflow ../src/audio/AudioDriverPortAudio.cpp:301 in AudioDriverPortAudio::portaudio_callback(void const*, void*, unsigned long, PaStreamCallbackTimeInfo const*, unsigned long, void*)
Shadow bytes around the buggy address:
0x0c2a80157f40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a80157f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157f70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c2a80157f90:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a80157fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a80157fe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==136487==ABORTING
基本上是这样说的:
* out ++ = userD-> memoryDeque.front();
使缓冲区溢出内部创建的数据:
memoryDeque.push_back(* memory);
因此,当我在输入缓冲区中获取数据时,通过推入双端队列将其复制(作为值,而不是引用),然后将从双端队列中再次读取该值,并将其作为值放入输出pulseaudio库中,我没弄错为什么双端队列在内部会引起错误。
如果我不使用AddressSanitizer运行,我会发现某些时候声音开始是错误的,再过几次,就会出现分段错误。
这种方法(具有双端队列和回调函数)即使在SDL中也可以使用,但我没有遇到这种问题。
仅作为参考,通过和使用的声音可以单声道或立体声方式为8位或16位。 它以 unsigned char 进行管理,因此,例如,对于16位立体声的pulseaudio期望有4个值,这就是为什么nSize * framesPerBuffer值是从回调函数中的双端队列中获取的。任何提示是什么问题?
谢谢
编辑: 忘记定义:
std::deque<unsigned char> memoryDeque;
edit2:
播放音频(它会调用相应的库:SDL,PortAudio ..):
void Player::playAudio(void* pBuf, unsigned long int bufSize)
{
audioDriverMutex.lock();
AudioDrivers::getDriver()->play(pBuf,bufSize);
audioDriverMutex.unlock();
}
播放作业,其中会生成声音,然后将其传递到音频库:
void Player::playJob(QThread* thread)
{
while ( playJobActive )
{
if ( ++currentBuffer == 2 )
currentBuffer = 0;
fillBuffer(pBuffer[currentBuffer],multiBufferSize);
playAudio(pBuffer[currentBuffer],multiBufferSize);
if ( endOfSong )
{
break;
}
}
}
edit3:
感谢所有人。 我上了这个课,现在问题就解决了:
#include <deque>
#include <mutex>
using namespace std;
template <typename T> class safedeque {
public:
void clear() {
lock_guard<mutex> lock(q_mutex);
memoryDeque.clear();
}
bool empty() {
lock_guard<mutex> lock(q_mutex);
return memoryDeque.empty();
}
std::size_t size() {
lock_guard<mutex> lock(q_mutex);
return memoryDeque.size();
}
void push_back(const T& __x) {
lock_guard<mutex> lock(q_mutex);
memoryDeque.push_back(__x);
}
void pop_front() {
lock_guard<mutex> lock(q_mutex);
memoryDeque.pop_front();
}
T front() {
lock_guard<mutex> lock(q_mutex);
return memoryDeque.front();
}
private:
deque<T> memoryDeque;
mutex q_mutex;
};