Teléfono SIP en STM32F7-Discovery

Hola a todos.

Hace algún tiempo, hemos escrito discutimos cómo logramos ejecutar un teléfono SIP en STM32F4-Discovery con 1 MB de ROM y 192 KB de RAM, basado en Embox. Es importante mencionar que esa versión era mínima y conectaba dos teléfonos directamente sin servidor y con transmisión de voz solo en una dirección. Así que decidimos lanzar un teléfono más completo, con llamadas a través de un servidor y transmisión de voz en ambas direcciones, pero tratando de utilizar la menor cantidad de memoria posible.

Reproducir video

Para el teléfono, decidimos elegir la aplicación simple_pjsua que forma parte de la biblioteca PJSIP. Esta es una aplicación mínima que puede registrarse en un servidor, recibir y hacer llamadas. A continuación, proporcionaré una descripción de cómo ejecutarla en STM32F7-Discovery.

Cómo ejecutar

  1. Configuramos Embox
    make confload-platform/pjsip/stm32f7cube
  2. En el archivo conf/mods.config, configuramos la cuenta SIP necesaria.
    
    include platform.pjsip.cmd.simple_pjsua_imported(
        sip_domain="server", 
        sip_user="username",
        sip_passwd="password")
    

    donde servidor — es el servidor SIP (por ejemplo, sip.linphone.org), username y password — el nombre de usuario y la contraseña de la cuenta.

  3. Compilamos Embox con el comando make. Tenemos información sobre la programación de la placa en wiki y en el artículo.
  4. Ejecutamos en la consola de Embox el comando “simple_pjsua_imported”
    
    00:00:12.870    pjsua_acc.c  ....El estado de salida SIP para el acc 0 no está activo
    00:00:12.884    pjsua_acc.c  ....sip:alexk2222@sip.linphone.org: registro exitoso, estado=200 (Registro exitoso)
    00:00:12.911    pjsua_acc.c  ....Temporizador de mantenimiento iniciado para acc 0, destino:91.121.209.194:5060, intervalo:15s
    

  5. Finalmente, solo queda conectar los altavoces o auriculares a la salida de audio y hablar en dos pequeños micrófonos MEMS cerca de la pantalla. Llamamos desde Linux a través de la aplicación simple_pjsua, pjsua. O se puede usar cualquier otra como linphone.

Todo esto está descrito en nuestro wiki.

Cómo llegamos a esto

Así que, inicialmente, surgió la cuestión de elegir la plataforma de hardware. Dado que estaba claro que STM32F4-Discovery no funcionaría debido a la memoria, se eligió STM32F7-Discovery. Tiene 1 MB de memoria flash y 256 KB de RAM (+ 64 KB de memoria rápida especial, que también utilizaremos). No es mucho para llamadas a través de un servidor, pero decidimos intentarlo.

Condicionalmente, dividimos la tarea en varias etapas:

  • Ejecutar PJSIP en QEMU. Esto fue conveniente para depuración, además ya teníamos soporte para el códec AC97.
  • Grabación de voz y reproducción en QEMU y en STM32.
  • Portar la aplicación simple_pjsua que forma parte de PJSIP. Permite registrarse en un servidor SIP y realizar llamadas.
  • Desplegar nuestro propio servidor basado en Asterisk y probar en él, después de lo cual intentar con externos como sip.linphone.org.

El sonido en Embox funciona a través de Portaudio, que también se utiliza en PISIP. En QEMU surgieron los primeros problemas: los archivos WAV se reproducían bien a 44100 Hz, pero a 8000 algo claramente no estaba bien. Resultó que el problema era la configuración de frecuencia: por defecto en el hardware era 44100, y esto no se cambiaba programáticamente.

Aquí, probablemente, valga la pena aclarar un poco cómo se produce la reproducción del sonido. Se puede establecer un puntero en la tarjeta de sonido hacia un bloque de memoria desde el cual se debe reproducir o grabar a una frecuencia previamente establecida. Una vez que el búfer se agota, se genera una interrupción y la ejecución continúa desde el siguiente búfer. El asunto es que estos búferes deben llenarse con anticipación mientras se reproduce el anterior. Este problema también lo encontraremos más adelante en STM32F7.

A continuación, alquilamos un servidor y desplegamos Asterisk en él. Como había mucho que depurar y no queríamos hablar demasiado por el micrófono, necesitábamos implementar reproducción y grabación automáticas. Para ello, parcheamos simple_pjsua para poder insertar archivos en lugar de dispositivos de audio. En PJSIP esto se hace de manera bastante sencilla, ya que tienen el concepto de puerto, que puede ser tanto un dispositivo como un archivo. Y estos puertos se pueden conectar de forma flexible a otros puertos. Puedes ver el código en nuestro pjsip. el repositorioComo resultado, el esquema fue el siguiente. En el servidor Asterisk creé dos cuentas: una para Linux y otra para Embox. Luego en Embox se ejecuta el comando simple_pjsua_imported, Embox se registra en el servidor, después de lo cual hacemos una llamada desde Linux a Embox. En el momento de la conexión, comprobamos en el servidor Asterisk que toda la conexión está establecida, y después de un tiempo debemos escuchar en Embox el sonido desde Linux, mientras que en Linux guardamos el archivo que se reproduce desde Embox.

Después de que esto funcionó en QEMU, pasamos a la portación a STM32F7-Discovery. El primer problema fue que no entrábamos en 1 MB de ROM sin activar la optimización del compilador “-Os” por el tamaño de la imagen. Por lo tanto, activamos “-Os”. Luego, mediante un parche, desactivamos el soporte para C++, que solo se necesita para pjsua, mientras que nosotros usamos simple_pjsua.

Después de que lo acomodamos simple_pjsua, decidimos que ahora había posibilidades de ejecutarlo. Pero primero teníamos que resolver el problema de grabación y reproducción de voz. La pregunta es: ¿dónde grabar? Elegimos la memoria externa: SDRAM (128 MB). Puedes probarlo tú mismo:

Se creará un WAV estéreo con una frecuencia de 16000 Hz y una duración de 10 segundos:


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

Reproduciendo:


play -m C0000000

Aquí surgieron dos problemas. El primero estaba relacionado con el códec: se utiliza WM8994, y hay un concepto llamado slot, y hay 4 de estos slots. Por defecto, si no se configura, la reproducción de audio se realiza en los cuatro slots. Por lo tanto, a una frecuencia de 16000 Hz obteníamos 8000 Hz, y para 8000 Hz la reproducción simplemente no funcionaba. Al elegir solo los slots 0 y 2, funcionó como era debido. Otro problema fue la interfaz de audio en STM32Cube, donde la salida de audio funciona a través de SAI (Serial Audio Interface) de manera síncrona con la entrada de audio (no profundicé en los detalles, pero parece que comparten un reloj común y, al inicializar la salida de audio, de alguna manera se vincula a la entrada de audio). Es decir, no se pueden iniciar por separado, por lo que hicimos lo siguiente: la entrada y salida de audio siempre están activas (incluyendo la generación de interrupciones). Pero cuando no se reproduce nada en el sistema, simplemente proporcionamos un búfer vacío a la salida de audio, y cuando se inicia la reproducción, comenzamos a llenarlo correctamente.

A continuación, nos encontramos con que el sonido al grabar la voz era muy bajo. Esto sucede porque los micrófonos MEMS en STM32F7-Discovery funcionan mal a frecuencias inferiores a 16000 Hz. Por lo tanto, configuramos 16000 Hz, incluso si llega a 8000 Hz. Para ello, de hecho, fue necesario agregar una conversión de frecuencia de audio a otra.

Luego tuvimos que aumentar el tamaño del heap que se ubica en la RAM. Según nuestros cálculos, pjsip requería alrededor de 190 Kb, y nosotros teníamos solo unos 100 Kb. Aquí fue necesario utilizar un poco de memoria externa: SDRAM (alrededor de 128 Kb).

Después de todas estas correcciones, vi los primeros paquetes entre Linux y Embox, ¡y escuché sonido! Pero el sonido era horrible, nada parecido al de QEMU, no se podía entender nada. Entonces, nos preguntamos cuál podría ser el problema. La depuración mostró que Embox simplemente no logra llenar/descargar los búferes de audio. Mientras pjsip procesa un marco, ocurren 2 interrupciones de finalización del procesamiento de búferes, lo que es demasiado. La primera idea para acelerar fue la optimización del compilador, pero ya estaba habilitada en PJSIP. La segunda - la coma flotante por hardware, de la cual hablamos en el artículo. Pero como ha demostrado la práctica, la FPU no ofreció un aumento significativo en la velocidad. El siguiente paso fue establecer prioridades para los flujos. En Embox hay diferentes estrategias de programación, y activé la que soporta prioridades, asignando la máxima prioridad a los flujos de audio. Esto tampoco ayudó.

La siguiente idea fue que trabajamos con memoria externa y sería bueno mover allí las estructuras a las que se accede con mucha frecuencia. Hice un análisis preliminar de cuándo y para qué simple_pjsua se asigna memoria. Resultó que de 190 Kb, los primeros 90 Kb se asignan para las necesidades internas de PJSIP y no se accede a ellos con frecuencia. Luego, durante una llamada entrante, se llama a la función pjsua_call_answer, donde se asignan los buffers para trabajar con los frames entrantes y salientes. Esto fue alrededor de 100 Kb más. Y aquí procederemos de la siguiente manera. Hasta el momento de la llamada, los datos se ubican en memoria externa. Una vez que hay una llamada, se sustituye inmediatamente el heap por otro en RAM. De este modo, todos los datos “calientes” se trasladan a una memoria más rápida y predecible.

Al final, todo esto permitió iniciar simple_pjsua y llamar a través de nuestro servidor. Luego también a través de otros servidores como sip.linphone.org.

Conclusiones

Como resultado, logramos iniciar simple_pjsua con la transmisión de voz en ambas direcciones a través del servidor. El problema de los 128 Kb SDRAM adicionales se puede resolver utilizando un Cortex-M7 un poco más potente (por ejemplo, STM32F769NI con 512 Kb de RAM), pero aún no hemos perdido la esperanza de entrar en 256 Kb 🙂 Estaremos encantados si alguien está interesado, y mejor aún, si lo intenta. Todo el código fuente, como siempre, está en nuestro el repositorio.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster