Telefone SIP em STM32F7-Discovery

Olá a todos.

Um tempo atrás nós писали sobre como conseguimos lançar um telefone SIP no STM32F4-Discovery com 1 MB de ROM e 192 KB de RAM) com base em Caixa de entrada. Aqui deve ser dito que essa versão era mínima e conectava dois telefones diretamente sem servidor e com transmissão de voz em apenas uma direção. Por isso, decidimos lançar um telefone mais completo com chamada pelo servidor, transmissão de voz nos dois sentidos, mas ao mesmo tempo mantendo o menor tamanho de memória possível.


Para o telefone, optou-se por escolher um aplicativo simples_pjsua como parte da biblioteca PJSIP. Este é um aplicativo mínimo que pode se registrar no servidor, receber e atender chamadas. Abaixo, darei imediatamente uma descrição de como executá-lo no STM32F7-Discovery.

Como lançar

  1. Configurando o Embox
    make confload-platform/pjsip/stm32f7cube
  2. Defina a conta SIP necessária no arquivo conf/mods.config.
    
    include platform.pjsip.cmd.simple_pjsua_imported(
        sip_domain="server", 
        sip_user="username",
        sip_passwd="password")
    

    onde servidor é um servidor SIP (por exemplo, sip.linphone.org), nome de usuário и senha - nome de usuário e senha da conta.

  3. Montando o Embox como uma equipe fazer. Sobre o firmware da placa que temos вики e статье.
  4. Execute o comando “simple_pjsua_imported” no console do 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:[email protected]: 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 falar em dois pequenos microfones MEMS próximos ao display. Chamamos do Linux através do aplicativo simple_pjsua, pjsua. Bem, ou você pode usar qualquer outro tipo de linphone.

Tudo isso está descrito em nosso вики.

Como chegamos lá

Então, inicialmente surgiu a questão de escolher uma plataforma de hardware. Como estava claro que o STM32F4-Discovery não caberia na memória, o STM32F7-Discovery foi escolhido. Ela tem um pendrive de 1 MB e 256 KB de RAM (+64 de memória rápida especial, que também usaremos). Também não há muito para chamadas pelo servidor, mas decidimos tentar nos encaixar.

Condicionalmente para si, a tarefa foi dividida em várias etapas:

  • Executando PJSIP no QEMU. Era conveniente para depuração, além disso, já tínhamos suporte para o codec AC97 lá.
  • Gravação e reprodução de voz no QEMU e no STM32.
  • Portando um aplicativo simples_pjsua da 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 os 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 tocou bem em 44100 Hz, mas em 8000 algo claramente deu errado. Descobriu-se que era uma questão de definir a frequência - por padrão era 44100 no equipamento, e isso não mudava programaticamente.

Aqui, talvez, valha a pena explicar um pouco como o som é tocado em geral. A placa de som pode ser configurada para algum ponteiro para um pedaço de memória do qual você deseja reproduzir ou gravar em uma frequência predeterminada. Depois que o buffer termina, uma interrupção é gerada e a execução continua com o próximo buffer. O fato é que esses buffers precisam ser preenchidos antecipadamente enquanto o anterior está sendo reproduzido. Enfrentaremos esse problema 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, foi necessário fazer reprodução e gravação automáticas. Para fazer isso, corrigimos simple_pjsua para que você possa deslizar arquivos em vez de dispositivos de áudio. No PJSIP isso é feito de forma bastante simples, pois 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 pjsip repositórios. Como resultado, o esquema foi o seguinte. No servidor Asterisk, iniciei duas contas - para Linux e para Embox. Em seguida, o comando é executado no Embox simple_pjsua_importado, Embox é registrado no servidor, após o qual chamamos Embox do Linux. No momento da conexão, verificamos no servidor Asterisk se a conexão foi estabelecida, e depois de um tempo devemos ouvir o som do Linux no Embox, e no Linux salvamos o arquivo que é reproduzido do Embox.

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

Depois de ser colocado simples_pjsua, decidiu que agora há uma chance de lançá-lo. Mas primeiro era necessário lidar com a gravação e reprodução da voz. A questão é onde escrever? Escolhemos memória externa - SDRAM (128 MB). Você pode tentar isso sozinho:

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


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

Nós perdemos:


play -m C0000000

Existem dois problemas aqui. O primeiro com o codec - WM8994 é usado, e tem um slot, e são 4 desses slots. Portanto, por padrão, se não estiver configurado, ao reproduzir áudio, a reprodução ocorre em todos os quatro slots . Portanto, na frequência de 16000 Hz, recebemos 8000 Hz, mas para 8000 Hz, a reprodução simplesmente não funcionou. Quando apenas os slots 0 e 2 foram selecionados, funcionou como deveria. Outro problema era a interface de áudio do STM32Cube, em que a saída de áudio funciona via SAI (Serial Audio Interface) de forma sincronizada com a entrada de áudio (não entendi os detalhes, mas acontece que eles compartilham um clock comum e quando o a saída de áudio é inicializada, o áudio está de alguma forma conectado à sua entrada). Ou seja, você não pode executá-los separadamente, então fizemos o seguinte - a entrada e a saída de áudio sempre funcionam (incluindo interrupções são geradas). Mas quando nada está sendo reproduzido no sistema, simplesmente colocamos um buffer vazio na saída de áudio e, quando a reprodução começa, começamos a preenchê-lo honestamente.

Além disso, encontramos o fato de que o som durante a gravação de voz era muito baixo. 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, configuramos 16000 Hz, mesmo que venham 8000 Hz. Para fazer isso, porém, foi necessário adicionar um software de conversão de uma frequência para outra.

Em seguida, tive que aumentar o tamanho da pilha, localizada na RAM. De acordo com nossos cálculos, o pjsip exigia cerca de 190 KB e temos apenas cerca de 100 KB restantes. Aqui tive que usar alguma memória externa - SDRAM (cerca de 128 KB).

Depois de todas essas edições, vi os primeiros pacotes entre Linux e Embox, e ouvi o som! Mas o som era terrível, nada igual ao do QEMU, não dava para distinguir nada. Então pensamos sobre o que poderia ser o problema. A depuração mostrou que o Embox simplesmente não tem tempo para preencher / descarregar os buffers de áudio. Enquanto pjsip estava processando um quadro, 2 interrupções ocorreram sobre a conclusão do processamento do buffer, o que é demais. O primeiro pensamento para velocidade foi a otimização do compilador, mas já estava incluída no PJSIP. O segundo é um ponto flutuante de hardware, falamos sobre isso em статье. Mas, como mostra a prática, o FPU não deu um aumento significativo na velocidade. O próximo passo foi priorizar os threads. O Embox tem diferentes estratégias de agendamento, e incluí uma que oferece suporte a prioridades e define fluxos de áudio com a prioridade mais alta. Isso também não ajudou.

A próxima ideia era que estávamos trabalhando com memória externa e seria bom mover para lá estruturas que são acessadas com muita frequência. Fiz uma análise preliminar de quando e em que simples_pjsua aloca memória. Descobriu-se que dos 190 Kb, os primeiros 90 Kb são alocados para necessidades internas do PJSIP e não são acessados ​​com muita frequência. Além disso, durante uma chamada recebida, a função pjsua_call_answer é chamada, na qual os buffers são alocados para trabalhar com quadros de entrada e saída. Ainda era cerca de 100 Kb. E então fizemos o seguinte. Até o momento da chamada, colocamos os dados na memória externa. Assim que a chamada, substituímos 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 possibilitou o lançamento simples_pjsua e ligue através do seu servidor. E então através de outros servidores como sip.linphone.org.

Descobertas

Com isso, foi possível lançar simples_pjsua com transmissão de voz em ambas as direções através do servidor. O problema com 128 KB de SDRAM gastos 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 perdemos a esperança de entrar em 256 KB 🙂 Ficaremos felizes se alguém estiver interessado, Ou melhor ainda, tente. Todas as fontes, como sempre, estão em nosso repositórios.

Fonte: habr.com

Adicionar um comentário