VERMELHO: melhorando a qualidade do som com redundância



Em abril de 2020, a Citizenlab relatou uma criptografia bastante fraca para o Zoom e declarou que o Zoom estava usando o codec de áudio SILK. Infelizmente, o artigo não continha os dados iniciais para confirmar isso e me dar a oportunidade de consultá-lo no futuro. No entanto, obrigado a Natalie Silvanovich do Google Project Zeroe para a ferramenta de rastreamento Frida, consegui obter um despejo de alguns quadros SILK brutos. Sua análise me inspirou a dar uma olhada em como o WebRTC lida com áudio. Quando se trata da qualidade percebida da chamada em geral, é a qualidade do áudio que mais afeta, pois tendemos a notar até mesmo pequenas falhas. Apenas dez segundos de análise foram suficientes para iniciar uma verdadeira aventura - em busca de opções para melhorar a qualidade de som fornecida pelo WebRTC.



Lidei com o cliente Zoom nativo em 2017 (antes da postagem DataChannel ) e percebi que seus pacotes de áudio às vezes eram muito grandes em comparação com os pacotes de soluções baseadas em WebRTC:



O gráfico acima mostra o número de pacotes com um comprimento de carga útil UDP específico. Pacotes entre 150 e 300 bytes são incomuns quando comparados a uma chamada WebRTC típica. Eles são muito mais longos do que os pacotes que normalmente recebemos da Opus. Suspeitamos que havia controle de erro de encaminhamento (FEC) ou redundância, mas sem acesso a quadros não criptografados, era difícil tirar outras conclusões ou fazer algo.



Os quadros SILK não criptografados no novo dump mostraram uma distribuição muito semelhante. Depois de converter os frames em um arquivo e, em seguida, reproduzir uma mensagem curta (obrigado a Giacomo Vacca por uma postagem de blog muito útildescrevendo as etapas necessárias) Voltei ao Wireshark e olhei os pacotes. Aqui está um exemplo de três pacotes que achei particularmente interessantes:



packet 7:
e9e4ab17ad8b9b5176b1659995972ac9b63737f8aa4d83ffc3073d3037b452fe6e1ee
5e6e68e6bcd73adbd59d3d31ea5fdda955cbb7f

packet 8: 
e790ba4908639115e02b457676ea75bfe50727bb1c44144d37f74756f90e1ab926ef
930a3ffc36c6a8e773a780202af790acfbd6a4dff79698ea2d96365271c3dff86ce6396
203453951f00065ec7d26a03420496f

packet 9:
e93997d503c0601e918d1445e5e985d2f57736614e7f1201711760e4772b020212dc
854000ac6a80fb9a5538741ddd2b5159070ebbf79d5d83363be59f10ef
e790ba4908639115e02b457676ea75bfe50727bb1c44144d37f74756f90e1ab926ef
930a3ffc36c6a8e773a780202af790acfbd6a4dff79698ea2d96365271c3dff86ce6396
203453951f00065ec7d26a03420496f
e9e4ab17ad8b9b5176b1659995972ac9b63737f8aa4d83ffc3073d3037b452fe6e1ee
5e6e68e6bcd73adbd59d3d31ea5fdda955cbaef


O pacote 9 contém dois pacotes anteriores, pacote 8 - 1 pacote anterior. Essa redundância é causada pelo uso do formato LBRR - Low Bit-Rate Redundancy, que foi demonstrado por um estudo aprofundado do decodificador SILK (pode ser encontrado no projeto de Internet fornecido pela equipe do Skype , ou no repositório no GitHub ):





O Zoom usa SKP_SILK_LBRR_VER1, mas com dois pacotes de fallback. Se cada pacote UDP contiver não apenas o quadro de áudio atual, mas também os dois anteriores, ele será robusto mesmo se você perder dois dos três pacotes. Então, talvez a chave para a qualidade do som do Zoom seja a receita secreta da vovó no Skype?



Opus FEC



Como posso conseguir o mesmo com WebRTC? O próximo passo óbvio foi considerar o Opus FEC.



O LBRR (Low Rate Reservation) da SILK também é encontrado no Opus (lembre-se de que o Opus é um codec híbrido que usa SILK para a extremidade inferior da faixa de taxa de bits). No entanto, o Opus SILK é muito diferente do SILK original, cujo código-fonte já foi descoberto pelo Skype, pois é a parte do LBRR que é usada no modo de controle de erros.



No Opus, o controle de erros não é simplesmente adicionado após o quadro de áudio original, ele o precede e é codificado no fluxo de bits. Tentamos fazer experiências com a adição de nosso próprio controle de erro usando a API Insertable Streams , mas isso exigia uma transcodificação completa para inserir as informações no fluxo de bits antes do pacote real.





Embora os esforços tenham sido infrutíferos, eles geraram algumas estatísticas sobre o impacto do LBRR, que são mostradas na figura acima. O LBRR usa taxas de bits de até 10 kbps (ou dois terços da taxa de dados) para alta perda de pacotes. O repositório está disponível aqui . Essas estatísticas não são exibidas ao chamar a getStats() API WebRTC , portanto, os resultados foram bastante interessantes.



A necessidade de transcodificação não é o único problema com o Opus FEC. Como se viu, suas configurações no WebRTC são um tanto inúteis:





Subtrair a taxa de bits FEC da taxa de bits máxima alvo não faz sentido algum - a FEC está reduzindo ativamente a taxa de bits do stream principal. Um fluxo de taxa de bits inferior geralmente resulta em qualidade inferior. Se não houver perda de pacote que possa ser corrigida com o FEC, o FEC apenas degradará a qualidade, não melhorará. Por que isso acontece? A teoria principal é que o congestionamento é uma das razões para a perda de pacotes. Se você estiver enfrentando congestionamento, não desejará enviar mais dados, pois isso só piorará o problema. No entanto, como Emil Ivov descreve em sua excelente palestra na KrankyGeek de 2017, o congestionamento nem sempre é a causa da perda de pacotes. Além disso, essa abordagem também ignora qualquer fluxo de vídeo que o acompanhe. A estratégia FEC baseada em congestionamento para áudio Opus não faz muito sentido quando você está enviando centenas de kilobits de vídeo junto com um fluxo Opus de 50 kbps relativamente pequeno. Talvez no futuro veremos algumas mudanças no libopus, mas por enquanto eu gostaria de tentar desabilitá-lo, porque atualmente ele está habilitado no WebRTC por padrão .



Concluímos que isso não nos convém ...



VERMELHO



Se quisermos redundância real, a RTP tem uma solução chamada RTP Payload for Redundant Audio Data, ou RED. É bem antigo, o RFC 2198 foi escrito em 1997 . A solução permite que várias cargas úteis RTP com carimbos de data / hora diferentes sejam colocadas no mesmo pacote RTP a um custo relativamente baixo.



Usar RED para colocar um ou dois quadros de áudio redundantes em cada pacote seria muito mais robusto contra a perda de pacotes do que o Opus FEC. Mas isso só é possível dobrando ou triplicando a taxa de bits de áudio de 30 kbps para 60 ou 90 kbps (com 10 kbps adicionais para o cabeçalho). Em comparação com mais de 1 megabit de dados de vídeo por segundo, no entanto, isso não é tão ruim.



A biblioteca WebRTC incluiu um segundo codificador e decodificador para RED, que agora é redundante! Apesar das tentativas de remover o código RED de áudio não utilizado , fui capaz de aplicar este codificador com relativamente pouco esforço. O histórico completo da solução está disponível no sistema de rastreamento de bugs WebRTC.



E está disponível como um teste que é incluído quando o Chrome é iniciado com os seguintes sinalizadores:

--force-fieldtrials=WebRTC-Audio-Red-For-Opus/Enabled/


Então RED pode ser habilitado via negociação SDP; ele será exibido assim:

a=rtpmap:someid red/48000/2


Não é habilitado por padrão, pois há ambientes em que usar largura de banda extra não é uma boa ideia. Para usar o RED, altere a ordem dos codecs para que seja anterior ao codec Opus. Isso pode ser feito usando a API RTCRtpTransceiver.setCodecPreferencesconforme mostrado aqui . Obviamente, outra alternativa é alterar manualmente o SDP. O formato SDP também pode fornecer uma maneira de configurar o nível máximo de redundância, mas a semântica de oferta-resposta RFC 2198 não era completamente clara, então decidi adiar isso por um tempo.



Você pode demonstrar como tudo isso funciona executando-o em um exemplo de áudio . É assim que a versão anterior se parece com um pacote de backup:





Por padrão, a taxa de bits de carga útil (linha vermelha) é quase duas vezes mais alta do que sem redundância, em quase 60 kbps. DTX (transferência descontínua) é um mecanismo de conservação de largura de banda que apenas envia pacotes quando a voz é detectada. Como esperado, ao usar DTX, o efeito da taxa de bits diminui um pouco, como podemos ver no final da chamada.







Verificar o comprimento do pacote mostra o resultado esperado: os pacotes são, em média, duas vezes mais longos (mais altos) em comparação com a distribuição normal do comprimento da carga útil mostrado abaixo.



Isso ainda é um pouco diferente do que o Zoom faz, onde vimos reservas fracionárias. Vamos revisitar o gráfico de comprimento do pacote Zoom mostrado anteriormente para ver uma comparação:



Adicionando suporte para detecção de atividade de voz (VAD)



O Opus FEC envia dados de backup apenas se houver atividade de voz no pacote. O mesmo deve ser aplicado à implementação do RED. Para isso, o codificador Opus deve ser alterado para exibir as informações VAD corretas , que são definidas no nível SILK. Com esta configuração, a taxa de bits atinge 60 kbps apenas na presença de voz (em comparação com 60+ kbps constantes):





e o "espectro" fica mais parecido com o que vimos com o Zoom:





A mudança para conseguir isso ainda não apareceu.



Encontrando a distância certa



Distância é o número de pacotes de backup, ou seja, o número de pacotes anteriores no atual. No processo de trabalho para encontrar a distância certa, descobrimos que se RED na distância 1 é legal, então RED na distância 2 é ainda mais legal. Nossa estimativa de laboratório simulou uma perda de pacote aleatória de 60%. Neste ambiente, o Opus + RED apresentou um som excelente, enquanto o Opus sem RED teve um desempenho muito pior. A API WebRTC getStats () fornece uma capacidade muito útil para medir isso, comparando a porcentagem de amostras ocultas obtidas dividindo concealedSamples por totalSamplesReceived .



Na página de amostras de áudio, esses dados podem ser facilmente recuperados com este snippet de JavaScript colado no console:

(await pc2.getReceivers()[0].getStats()).forEach(report => {
  if(report.type === "track") console.log(report.concealmentEvents, report.concealedSamples, report.totalSamplesReceived, report.concealedSamples / report.totalSamplesReceived)})


Executei alguns testes de perda de pacotes usando um sinalizador não tão famoso, mas muito útil WebRTCFakeNetworkReceiveLossPercent:

--force-fieldtrials=WebRTC-Audio-Red-For-Opus/Enabled/WebRTCFakeNetworkReceiveLossPercent/20/


Com 20% de perda de pacote e FEC habilitado por padrão, não houve muita diferença na qualidade do áudio, mas houve uma ligeira diferença na métrica:

cenário porcentagem de perda
sem vermelho dezoito%
sem vermelho, FEC desativado 20%
vermelho com distância 1 4%
vermelho com distância 2 0,7%


Sem RED ou FEC, a métrica quase corresponde à perda de pacote solicitada. Existe um efeito do FEC, mas é pequeno.



Sem RED, com perda de 60%, a qualidade do som tornou-se bastante pobre, um pouco metálica e as palavras difíceis de entender:

cenário porcentagem de perda
sem vermelho 60%
vermelho com distância 1 32%
vermelho com distância 2 dezoito%


Havia alguns artefatos audíveis em RED com distância = 1, mas som quase perfeito na distância 2 (que é a quantidade de redundância atualmente em uso).

Há uma sensação de que o cérebro humano pode suportar um certo nível de silêncio que ocorre de forma irregular. (E o Google Duo parece estar usando um algoritmo de aprendizado de máquina para preencher o silêncio.)



Medindo o desempenho no mundo real



Esperamos que a inclusão de RED no Opus melhore a qualidade do som, embora em alguns casos possa piorá-la. Emil Ivov se ofereceu para conduzir alguns testes de audição usando o método POLQA-MOS. Isso já foi feito para o Opus, então temos uma linha de base para comparação.

Se os testes iniciais mostrarem resultados promissores, então conduziremos um experimento em grande escala na varredura principal do Jitsi Meet, aplicando as métricas de perda de porcentagem que usamos acima.



Observe que para servidores de mídia e SFUs, habilitar RED é um pouco mais difícil, pois o servidor pode precisar gerenciar a retransmissão RED para clientes selecionados, como se nem todos os clientes suportassem conferências RED. Além disso, alguns clientes podem estar em um canal de largura de banda limitado onde RED não é necessário. Se o ponto de extremidade não suportar RED, o SFU pode remover a codificação desnecessária e enviar Opus sem um wrapper. Da mesma forma, ele pode implementar o próprio RED e usá-lo ao reenviar pacotes de um terminal que transmita Opus para um terminal que suporte RED.



Muito obrigado à Jitsi / 8 × 8 Inc por patrocinar esta aventura emocionante e ao pessoal do Google que analisou e forneceu feedback sobre as mudanças necessárias.



E sem Natalie Silvanovich, eu teria ficado olhando os bytes criptografados!



All Articles