2016-03-24 4 views
1

Verzeihen Sie mir, wenn mein Fragetitel schrecklich ist. Meine Frau sagt mir immer, ich bin nicht gut darin, Dinge zu formulieren.Ruft eine Blockierung snd_pcm_writei nach dem Decodieren von Audio seltsame Wiedergabe auf?

Ich habe einen Code geschrieben, der einen Puffer liest, der von einem anderen Thread gefüllt wird. Der Puffer ist mit Audiodaten gefüllt, die durch den Opus-Codec codiert sind. VoIP-Daten werden von der Gegenseite jeweils zu 20ms empfangen. Bei einem Versuch, Audio so schnell wie möglich in einer Schleife abzuspielen, nehme ich 20ms Daten zu einer bestimmten Zeit aus dem Puffer und decide sie dann und sende sie dann direkt zum Abspielen auf snd_pcm_wreitei.

Ich habe auf Google nach einigen Beispielen zur Verwendung von snd_pcm_writei mit zuvor codiertem Audio gesucht, um zu sehen, wie andere es tun. Ich hatte nicht viel Glück.

Mein Gedanke ist, wenn ich auf einen Mutex warte und auf die Kodierung warte, kann ich logisch nicht sehen, dass der Ton "glatt" ist. Ich könnte mir vorstellen, dass es zwischen jedem 20ms-Frame Zeitlücken gibt, in denen kein Audio an die Lautsprecher gesendet wird. Ist mein Verdacht richtig, dass dies wahrscheinlich zu fehlerhaften Audiodaten führen wird?

Mein Code diesbezüglich:

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) 
+0

Der PCM-Puffer ist größer als 20 ms, oder? –

+0

Ja. Ich habe möglicherweise nicht erwähnt, warum ich das tat. Ich werde meine Frage auch bearbeiten. Es ist für eine Anwendung vom Typ VoIP, bei der Daten 20 ms gleichzeitig empfangen werden. Also versuche ich es so schnell wie möglich zu spielen. –

+0

Also die Uhr des Senders und die Uhr des Gerätes sind nicht synchronisiert? –

Antwort

2

Wenn die Zeit zwischen dem Schreiben von 20 ms Chunks genau 20 ms ist, dann mit dem Puffer-Gerät wird leer sein, wenn Sie einen neuen Chunk schreiben. Schon die kleinste Verzögerung führt dann zu einem Unterlauf.

Um Unterbrüche zu vermeiden, müssen Sie den Puffer so voll wie möglich halten. Dies bedeutet, dass Sie es am Anfang füllen müssen, ohne zwischen den Blöcken zu warten.

Wenn die Uhr des Senders schneller als die Uhr des Geräts läuft, wird der Stream eventuell unterlaufen. Dies kann vermieden werden, indem die Taktdifferenz gemessen wird und entweder die Senderate des Senders geändert oder die Daten dynamisch neu abgetastet werden.