【发布时间】:2016-07-12 01:05:15
【问题描述】:
如果我的问题标题很糟糕,请原谅我。我的妻子总是告诉我,我不擅长措辞。
我编写了一些代码来读取由另一个线程填充的缓冲区。缓冲区充满了由 opus 编解码器编码的音频数据。每次 20 毫秒从远程端接收 VoIP 数据。为了尽可能快地播放音频,在一个循环中,我一次从缓冲区中取出 20 毫秒的数据,然后对其进行解码,然后直接发送到 snd_pcm_writei 上播放。
我在 Google 上查看了一些关于将 snd_pcm_writei 与先前编码的音频一起使用的示例,以了解其他人是如何做到的。我运气不太好。
我的想法是,如果我正在等待互斥体并等待编码,我无法从逻辑上看到音频“流畅”。我想在每 20 毫秒帧之间会有一段时间没有音频发送到扬声器。我的怀疑是否正确,这可能会产生不完美的音频?
我的代码与此相关:
while( true )
{
// We need a positive lock
if( !buffer_lock )
buffer_lock.lock();
LOG_DEBUG( *logger_ ) << "After the mutex lock.";
LOG_DEBUG( *logger_ ) << "Buffer size: " << current_audio->buffer_size_;
LOG_DEBUG( *logger_ ) << "Read pointer: " << current_audio->read_pointer_;
opus_int32 payload_size;
LOG_DEBUG( *logger_ ) << "calling audioCanDecodeChunk()";
// Now fisticuffs do we have enouffs?
if( audioCanDecodeChunk( current_audio, payload_size ) )
{
LOG_DEBUG( *logger_ ) << "We have enough current_audio buffer.";
// Are we dank?
if( payload_size<0 or payload_size>MAX_PACKET )
{
LOG_ERROR( *logger_ ) << "Decoding error, payload size (" << payload_size << ") is outsize range.";
break; // Terminal
}
// We have enough!
// Advance the read pointer
current_audio->read_pointer_+= 4;
// Copy it out
memcpy( payload_buffer, current_audio->buffer_+current_audio->read_pointer_, payload_size );
// Release it
buffer_lock.unlock();
// Now thingify it
int samples_decoded = opus_decode( opus_decoder_,
(const unsigned char *)payload_buffer,
payload_size,
(opus_int16 *)pcm_buffer,
MAX_FRAME_SIZE,
0 );
// How did we do?
if( samples_decoded<0 )
{
// What hap?
LOG_ERROR( *logger_ ) << "Error decoding samples: " << opus_strerror( samples_decoded );
break;
}
else
{
// Now we have our PCM!
int bytes_decoded = current_audio->recording_.channels*sizeof( opus_int16 )*samples_decoded;
LOG_DEBUG( *logger_ ) << "We have decoded " << bytes_decoded << " bytes payload: " << payload_size;
// Now write
if( (error = snd_pcm_writei( playback_handle_, pcm_buffer, samples_decoded ))!=samples_decoded )
{
LOG_ERROR( *logger_ ) << "snd_pcm_writei error: " << snd_strerror( error );
}
}
// Advance pointer
current_audio->read_pointer_+= payload_size;
} // If we don't have enough let it slide and unlock
else if( current_audio->done_ ) // Were we issued a flush?
{
LOG_DEBUG( *logger_ ) << "We are done.";
// We are done with this loop
break;
}
else
{
// Wait for it (an update)
LOG_DEBUG( *logger_ ) << "Before wait_buffer wait. Done: " << ( current_audio->done_ ? "true" : "false" ) <<
"Size: " << current_audio->buffer_size_
<< ", Read: " << current_audio->read_pointer_;
current_audio->wait_buffer_.wait( buffer_lock );
LOG_DEBUG( *logger_ ) << "After wait_buffer wait";
}
} // End while( true )
【问题讨论】:
-
PCM 设备的缓冲区大于 20 毫秒,不是吗?
-
是的。我可能没有提到我为什么要这样做。我也会编辑我的问题。它适用于一次 20 毫秒接收数据的 VoIP 类型应用程序。所以我只是想尽快播放。
-
所以发送方时钟和设备时钟不同步?
-
问题更多是关于是否会有明显的延迟,因为我在调用函数之前正在解码。例如,如果我只是直接从 PCM 缓冲区传递数据,那么在获取缓冲区数据和调用 play 函数之间的时钟周期会少很多。但在我的示例中,我首先抓取缓冲区,然后对其进行解码,最后将其传递给播放函数。所以我担心所有这些计算都会导致音频播放速度变慢。
标签: c++ buffer latency alsa opus