Telefone SIP no STM32F7-Discovery

Olá pessoal.

Algum tempo atrás, escrevemos sobre como conseguimos lançar um telefone SIP no STM32F4-Discovery com 1 MB de ROM e 192 KB de RAM) com base na Embox . Aqui devo dizer que essa versão era mínima e conectava dois telefones diretamente sem servidor e com transmissão de voz apenas em uma direção. Portanto, decidimos lançar um telefone mais completo com uma chamada através do servidor, transmissão de voz em ambas as direções, mas, ao mesmo tempo, manter o menor tamanho de memória possível.


Para o telefone, foi decidido escolher o aplicativo simple_pjsua como parte da biblioteca PJSIP. Esta é uma aplicação mínima que pode se registrar no servidor, receber e atender chamadas. Abaixo, darei uma descrição imediata de como executar isso no STM32F7-Discovery.

Como executar


  1. Configurando o Embox
    make confload-platform/pjsip/stm32f7cube 
  2. No arquivo conf / mods.config, defina a conta SIP desejada.

     include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password") 

    onde server é o servidor SIP (por exemplo, sip.linphone.org), nome de usuário e senha são o nome de usuário e a senha da conta.
  3. Crie o Embox com o comando make . Sobre o firmware da placa que temos no wiki e no artigo .
  4. Execute o comando “simple_pjsua_imported” no console da Embox

     00:00:12.870 pjsua_acc.c ....SIP outbound status for acc 0 is not active 00:00:12.884 pjsua_acc.c ....sip:alexk2222@sip.linphone.org: registration success, status=200 (Registration succes 00:00:12.911 pjsua_acc.c ....Keep-alive timer started for acc 0, destination:91.121.209.194:5060, interval:15s 

  5. Por fim, resta inserir alto-falantes ou fones de ouvido na saída de áudio e conversar em dois pequenos microfones MEMS próximos à tela. Chamamos do Linux através do aplicativo simple_pjsua, pjsua. Bem, ou você pode qualquer outro tipo de linphone.

Tudo isso é descrito em nosso wiki .

Como chegamos a isso?


Então, inicialmente surgiu a questão sobre a escolha de uma plataforma de hardware. Como ficou claro que o STM32F4-Discovery não caberia na memória, o STM32F7-Discovery foi escolhido. Ela tem uma unidade flash de 1 MB e 256 KB de RAM (+ 64 memória rápida especial, que também usaremos). Também não é muito para chamadas através do servidor, mas decidiu tentar entrar.

Convencionalmente, a tarefa foi dividida em várias etapas:

  • Executando o PJSIP no QEMU. Era conveniente para depuração, e já tínhamos suporte ao codec AC97 lá.
  • Gravação e reprodução de voz no QEMU e STM32.
  • Portando um aplicativo simple_pjsua de dentro do PJSIP. Ele permite que você se registre no servidor SIP e faça chamadas.
  • Implante seu próprio servidor baseado em Asterisk e teste nele, depois tente outros externos, como sip.linphone.org

O som no Embox funciona através do Portaudio, que também é usado no PISIP. Os primeiros problemas apareceram no QEMU - WAV tocaram bem em 44100 Hz, mas algo obviamente deu errado em 8000. Aconteceu que se tratava de definir a frequência - por padrão, era 44100 no equipamento, e isso não mudou programaticamente conosco.

Aqui, provavelmente vale a pena explicar um pouco como o som é reproduzido em geral. A placa de som pode definir algum ponteiro para um pedaço de memória a partir do qual você deseja reproduzir ou gravar em uma frequência predeterminada. Após o término do buffer, uma interrupção é gerada e a execução continua a partir do próximo buffer. O fato é que esses buffers precisam ser preenchidos com antecedência antes que o anterior seja reproduzido. Este problema iremos encontrar mais adiante no STM32F7.

Em seguida, alugamos um servidor e implantamos o Asterisk nele. Como era necessário depurar muito, mas não queria falar muito no microfone, era necessário executar a reprodução e a gravação automáticas. Para fazer isso, corrigimos simple_pjsua para que fosse possível inserir arquivos no lugar de dispositivos de áudio. No PJSIP, isso é feito de maneira simples, uma vez que eles têm o conceito de porta, que pode ser um dispositivo ou um arquivo. E essas portas podem ser conectadas de forma flexível a outras portas. Você pode ver o código em nosso repositório pjsip. Como resultado, o esquema foi o seguinte. Criei duas contas no servidor Asterisk - para Linux e Embox. Em seguida, o comando simple_pjsua_imported é executado na Embox, a Embox é registrada no servidor, após o que chamamos Embox do Linux. No momento da conexão, verificamos no servidor Asterisk se toda a conexão está estabelecida e, após algum tempo, eles devem ouvir o som do Linux na Embox, e no Linux salvamos o arquivo que é reproduzido na Embox.

Depois que funcionou no QEMU, passamos a migrar para o STM32F7-Discovery. O primeiro problema - eles não entraram em 1 MB de ROM sem a otimização incluída do compilador "-Os" em termos de tamanho da imagem. Portanto, inclui "-Os". Além disso, o patch desativou o suporte ao C ++, por isso é necessário apenas para o pjsua e usamos simple_pjsua.

Depois de ajustar o simple_pjsua , decidimos que agora há chances de iniciá-lo. Mas primeiro, você tinha que lidar com a gravação e reprodução de voz. Pergunta - onde escrever? Escolhemos uma memória externa - SDRAM (128 MB). Você pode tentar você mesmo:

Cria WAV estéreo com uma frequência de 16000 Hz e uma duração de 10 segundos:

 record -r 16000 -c 2 -d 10000 -m C0000000 

Perdemos:

 play -m C0000000 

Houve dois problemas. O primeiro com um codec é o WM8994, e possui um conceito de slot, e esses slots são 4. Portanto, por padrão, se não estiver configurado, ao reproduzir áudio, a reprodução ocorre nos quatro slots. Portanto, em uma frequência de 16000 Hz, recebemos 8000 Hz, mas em 8000 Hz a reprodução simplesmente não funcionou. Quando apenas os slots 0 e 2 foram selecionados, funcionou como deveria. Outro problema foi a interface de áudio no STM32Cube, na qual a saída de áudio funciona através da SAI (Serial Audio Interface) de forma síncrona com a entrada de áudio (não entendeu os detalhes, mas acontece que eles compartilham um relógio comum e, de alguma forma, o áudio é anexado ao inicializar a saída de áudio entrada). Ou seja, eles não podem ser iniciados separadamente; portanto, eles fizeram o seguinte - a entrada e a saída de áudio sempre funcionam (incluindo interrupções são geradas). Mas quando nada se perde no sistema, simplesmente inserimos um buffer vazio na saída de áudio e, quando a reprodução começa, honestamente começamos a preenchê-lo.

Além disso, eles encontraram o fato de que o som durante a gravação de voz era muito silencioso. Isso se deve ao fato de que os microfones MEMS no STM32F7-Discovery de alguma forma não funcionam bem em frequências abaixo de 16000 Hz. Portanto, definimos 16000 Hz, mesmo que 8000 Hz ocorram. Para fazer isso, a verdade era adicionar uma conversão de software de uma frequência para outra.

Em seguida, tive que aumentar o tamanho do heap, localizado na RAM. De acordo com nossas estimativas, o pjsip exigia cerca de 190 Kb e só tínhamos cerca de 100 Kb. Aqui eu tive que usar um pouco de memória externa - SDRAM (cerca de 128 Kb).

Após todas essas edições, vi os primeiros pacotes entre Linux e Embox e ouvi um som! Mas o som era terrível, nada parecido com o QEMU, nada podia ser descoberto. Então pensamos sobre o que poderia ser o problema. A depuração mostrou que a Embox simplesmente não tem tempo para preencher / descarregar buffers de áudio. Enquanto o pjsip está processando um quadro, duas interrupções ocorreram antes da conclusão do processamento do buffer, o que é muito. O primeiro pensamento para acelerar foi otimizar o compilador, mas ele já estava incluído no PJSIP. O segundo é um ponto flutuante de hardware, falamos sobre isso no artigo . Mas, como a prática demonstrou, a FPU não deu um aumento significativo na velocidade. O próximo passo foi priorizar os threads. A Embox possui diferentes estratégias de agendamento, e eu incluí uma que suporta prioridades e define fluxos de áudio com a prioridade mais alta. Também não ajudou.

A próxima foi a ideia de que estamos trabalhando com memória externa e seria bom mover estruturas para lá, que são acessadas com muita frequência. Fiz uma análise preliminar de quando e sob o que simple_pjsua aloca memória. Verificou-se que dos 190 Kb, os primeiros 90 Kb são alocados para as necessidades internas do PJSIP e não são acessados ​​com muita frequência. Em seguida, durante uma chamada recebida, a função pjsua_call_answer é chamada, na qual os buffers para trabalhar com quadros de entrada e saída são alocados. Foi cerca de 100 kb. E aqui agimos da seguinte maneira. Antes da chamada, os dados são colocados na memória externa. Assim que a chamada - substitua imediatamente a pilha por outra - na RAM. Assim, todos os dados "quentes" foram transferidos para uma memória mais rápida e previsível.

Como resultado, tudo isso junto permitiu iniciar o simple_pjsua e fazer uma chamada através do servidor. E então através de outros servidores, como sip.linphone.org.

Conclusões


Como resultado, acabou rodando simple_pjsua com transmissão de voz nas duas direções através do servidor. O problema com SDRAM de 128 Kb gasto adicionalmente pode ser resolvido usando um Cortex-M7 um pouco mais poderoso (por exemplo, STM32F769NI com 512 Kb de RAM), mas, ao mesmo tempo, ainda não temos esperança de caber em 256 Kb :) Ficamos felizes se alguém estiver interessado e ainda melhor - tente. Todas as fontes, como sempre, estão em nosso repositório .

Source: https://habr.com/ru/post/pt431134/


All Articles