解码音频后调用阻塞的snd_pcm_writei会导致奇怪的播放吗
Will calling a blocking snd_pcm_writei after decoding audio cause weird playback?
如果我的问题标题很糟糕,请原谅。我妻子总是告诉我,我不善于措辞。
我已经编写了一些代码来读取由另一个线程填充的缓冲区。缓冲区中充满了由opus编解码器编码的音频数据。VoIP数据从远程端接收,每次20ms。为了尽可能快地播放音频,在一个循环中,我一次从缓冲区中取出20ms的数据,然后解码,然后直接发送到snd_pcm_writei上播放。
我在谷歌上搜索了一些使用snd_pcm_writei和以前编码的音频的例子,看看其他人是怎么做的。我运气不太好。
我的想法是,如果我在等待互斥锁并等待编码,我就无法从逻辑上看到音频是"平滑的"。我可以想象,在每20ms的帧之间,会有一段时间间隔,没有音频被发送到扬声器。我怀疑这可能会产生不完美的音频,这是正确的吗?
我的代码与此相关:
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 )
如果写入20ms区块之间的时间正好是20ms,那么在写入新区块时,设备的缓冲区将为空。即使是最小的延迟也会导致欠载。
为了防止欠载,您必须保持缓冲区尽可能满。这意味着在开始时,您必须填充它,而无需在块之间等待。
当发送器的时钟运行得比设备的时钟快时,流最终会不足。这可以通过测量时钟差、改变发送器的传输速率或动态地重新采样数据来避免。
相关文章:
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- SFML纹理像播放器
- 根据用户回答声明"Players"。用户选择玩家数量。播放器是结构体
- 如何收听Windows当前正在播放的声音?
- GStreamer在开始任何播放之前进行搜索
- (SFML)按下键时,播放器构造函数未使用正确的动画进行更新
- 如何使用libav编写.mov文件,如果文件未正确完成,则可以播放
- C++新手,想知道如何使用VS code 2019播放音频文件
- 在C++不适用于猜数字游戏的情况下再次播放选项
- 按列随机播放 2D 矢量
- 如何以编程方式将音频从任何录制设备路由到任何播放设备
- 使用 IMFSinkWriter 编码的视频的播放速度会根据宽度而变化
- 测试驱动开发 c++:如何将对象添加到向量中,将歌曲添加到播放列表并对其进行测试
- 如何在C++中播放声音?
- 旋转播放器模型以指向一个点
- 大声音频无法播放
- 怎么可能只让设备使用 pjsua2 捕获或播放
- 播放器未在程序中的"X"和"O"之间切换
- 为什么这个函数会循环播放?
- 有没有办法毫不拖延地播放声音?