Вызовет ли блокирующий snd_pcm_writei после декодирования аудио странное воспроизведение?

Прости меня, если мой заголовок вопроса ужасен. Моя жена всегда говорит мне, что я не очень хорош в выражениях.

Я написал некоторый код, который читает буфер, который заполнен другим потоком. Буфер заполнен аудиоданными, закодированными кодеком opus. Данные VoIP принимаются с удаленной стороны по 20 мс одновременно. В попытке воспроизвести аудио как можно быстрее, в цикле я вынимаю 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 )

1

Решение

Если время между записью 20-миллисекундных блоков составляет ровно 20 мс, то при записи нового фрагмента буфер устройства будет пустым. Даже самая маленькая задержка приведет к недогрузке.

Чтобы предотвратить недогрузку, вы должны держать буфер как можно более полным. Это означает, что в начале вы должны заполнить его, не ожидая между кусками.

Когда часы отправителя работают быстрее, чем часы устройства, поток в конечном итоге будет опустошен. Этого можно избежать, измеряя разницу часов и либо изменяя скорость передачи отправителя, либо динамически перебирая данные.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]