Hola a todos
Hace algún tiempo,
escribimos sobre cómo logramos lanzar un teléfono SIP en el STM32F4-Discovery con 1 MB de ROM y 192 KB de RAM) basado en
Embox . Aquí hay que decir que esa versión era mínima y conectaba dos teléfonos directamente sin un servidor y con transmisión de voz solo en una dirección. Por lo tanto, decidimos lanzar un teléfono más completo con una llamada a través del servidor, transmisión de voz en ambas direcciones, pero al mismo tiempo mantener el tamaño de memoria más pequeño posible.
Para el teléfono, se decidió elegir la aplicación
simple_pjsua como parte de la biblioteca PJSIP. Esta es una aplicación mínima que puede registrarse en el servidor, recibir y responder llamadas. A continuación, daré inmediatamente una descripción de cómo ejecutar esto en el STM32F7-Discovery.
Como correr
- Configurar Embox
make confload-platform/pjsip/stm32f7cube
- En el archivo conf / mods.config, configure la cuenta SIP deseada.
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), nombre de usuario y contraseña son el nombre de usuario y la contraseña de la cuenta.
- Construye Embox con el comando make . Sobre el firmware de la placa que tenemos en la wiki y en el artículo .
- Ejecute el comando "simple_pjsua_imported" en la consola de 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
- Finalmente, queda insertar altavoces o auriculares en 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. Bueno, o puedes usar cualquier otro tipo de linphone.
Todo esto se describe en nuestra
wiki .
¿Cómo llegamos a esto?
Entonces, inicialmente surgió la pregunta acerca de elegir una plataforma de hardware. Como estaba claro que el STM32F4-Discovery no cabía en la memoria, se eligió el STM32F7-Discovery. Ella tiene una unidad flash de 1 MB y 256 KB de RAM (+ 64 memoria rápida especial, que también usaremos). Además, no hay muchas llamadas a través del servidor, pero decidí intentar entrar.
Convencionalmente, la tarea se dividió en varias etapas:
- Ejecutando PJSIP en QEMU. Era conveniente para la depuración, además de que ya teníamos compatibilidad con el códec AC97.
- Grabación y reproducción de voz en QEMU y STM32.
- Portar una aplicación simple_pjsua desde PJSIP. Le permite registrarse en el servidor SIP y hacer llamadas.
- Implemente su propio servidor basado en Asterisk y pruébelo en él, luego pruebe los externos como sip.linphone.org
El sonido en Embox funciona a través de Portaudio, que también se usa en PISIP. Los primeros problemas aparecieron en QEMU - WAV jugó bien a 44100 Hz, pero obviamente algo salió mal en 8000. Resultó que era una cuestión de establecer la frecuencia; por defecto, era 44100 en el equipo, y esto no cambió programáticamente con nosotros.
Aquí, probablemente valga la pena explicar un poco cómo se reproduce el sonido en general. La tarjeta de sonido puede establecer algún puntero en un trozo de memoria desde el que desea reproducir o grabar a una frecuencia predeterminada. Una vez que finaliza el almacenamiento intermedio, se genera una interrupción y la ejecución continúa desde el siguiente almacenamiento intermedio. El hecho es que estos búferes deben llenarse con anticipación antes de jugar el anterior. Este problema nos encontraremos más adelante en el STM32F7.
Luego, alquilamos un servidor e implementamos Asterisk en él. Como era necesario depurar mucho, pero no quería hablar mucho en el micrófono, era necesario hacer una reproducción y grabación automáticas. Para hacer esto, parcheamos simple_pjsua para que fuera posible deslizar archivos en lugar de dispositivos de audio. En PJSIP, esto se hace de manera bastante simple, ya que tienen el concepto de un puerto, que puede ser un dispositivo o un archivo. Y estos puertos se pueden conectar de manera flexible a otros puertos. Puedes ver el código en nuestro
repositorio pjsip. Como resultado, el esquema fue el siguiente. Creé dos cuentas en el servidor Asterisk: para Linux y para Embox. Luego, el comando
simple_pjsua_imported se ejecuta en Embox, Embox se registra en el servidor, después de lo cual llamamos a Embox desde Linux. En el momento de la conexión, verificamos en el servidor Asterisk que toda la conexión está establecida, y después de un tiempo deben escuchar el sonido de Linux en Embox, y en Linux guardamos el archivo que se reproduce desde Embox.
Después de que funcionó en QEMU, cambiamos a portar a STM32F7-Discovery. El primer problema: no entraron en 1 MB de ROM sin la optimización incluida del compilador "-Os" en términos de tamaño de imagen. Por lo tanto incluido "-Os". Además, el parche deshabilitó la compatibilidad con C ++, por lo que solo es necesario para pjsua, y usamos simple_pjsua.
Después de
ajustar simple_pjsua , decidimos que ahora hay posibilidades de lanzarlo. Pero primero, tenía que lidiar con la grabación y reproducción de voz. Pregunta: ¿dónde escribir? Elegimos una memoria externa: SDRAM (128 MB). Puedes probarlo tú mismo:
Crea 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
Perdemos:
play -m C0000000
Hubo dos problemas. El primero con un códec es WM8994, y tiene un concepto como una ranura, y estas ranuras son 4. Entonces, de forma predeterminada, si esto no está configurado, cuando se reproduce audio, la reproducción se produce en las cuatro ranuras. Por lo tanto, a una frecuencia de 16000 Hz, recibimos 8000 Hz, pero para 8000 Hz la reproducción simplemente no funcionó. Cuando solo se seleccionaron las ranuras 0 y 2, funcionó como debería. Otro problema era la interfaz de audio en el STM32Cube, en el que la salida de audio funciona a través de SAI (Interfaz de audio serie) de forma sincronizada con la entrada de audio (no entendía los detalles, pero resulta que comparten un reloj común y de alguna manera el audio está conectado al inicializar la salida de audio) entrada). Es decir, no se pueden iniciar por separado, por lo que hicieron lo siguiente: la entrada de audio y la salida de audio siempre funcionan (incluidas las interrupciones generadas). Pero cuando no se pierde nada en el sistema, simplemente deslizamos un búfer vacío en la salida de audio, y cuando comienza la reproducción, honestamente comenzamos a llenarlo.
Además, se encontraron con el hecho de que el sonido al grabar la voz era muy silencioso. Esto se debe al hecho de que los micrófonos MEMS en el STM32F7-Discovery de alguna manera no funcionan bien en frecuencias inferiores a 16000 Hz. Por lo tanto, establecemos 16000 Hz, incluso si viene 8000 Hz. Para hacer esto, la verdad era agregar una conversión de software de una frecuencia a otra.
Luego, tuve que aumentar el tamaño del montón, que se encuentra en la RAM. Según nuestras estimaciones, pjsip requería aproximadamente 190 Kb, y solo teníamos unos 100 Kb. Aquí tuve que usar un poco de memoria externa: SDRAM (aproximadamente 128 Kb).
Después de todas estas ediciones, vi los primeros paquetes entre Linux y Embox, ¡y escuché un sonido! Pero el sonido era terrible, para nada como en QEMU, no se podía distinguir nada. Luego pensamos en lo que podría ser el problema. La depuración mostró que Embox simplemente no tiene tiempo para llenar / descargar buffers de audio. Mientras pjsip está procesando un cuadro, se han producido 2 interrupciones antes de completar el procesamiento del búfer, que es demasiado. El primer pensamiento para acelerar fue optimizar el compilador, pero ya estaba incluido en PJSIP. El segundo es un punto flotante de hardware, hablamos de ello en el
artículo . Pero como ha demostrado la práctica, FPU no dio un aumento significativo en la velocidad. El siguiente paso fue priorizar los hilos. Embox tiene diferentes estrategias de programación, e incluí una que admite prioridades y establece transmisiones de audio a la máxima prioridad. Tampoco ayudó.
La siguiente fue la idea de que estamos trabajando con memoria externa y sería bueno mover las estructuras allí, a las que se accede con mucha frecuencia. Hice un análisis preliminar de cuándo y bajo qué
simple_pjsua 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 mucha frecuencia. Luego, durante una llamada entrante, se llama a la función pjsua_call_answer, en la que se asignan las memorias intermedias para trabajar con tramas entrantes y salientes. Fue alrededor de 100 kb. Y aquí actuamos de la siguiente manera. Antes de la llamada, los datos se colocan en la memoria externa. Tan pronto como la llamada, reemplace inmediatamente el montón con otro, en RAM. Por lo tanto, todos los datos "activos" se transfirieron a una memoria más rápida y predecible.
Como resultado, todo esto en conjunto permitió iniciar
simple_pjsua y hacer una llamada a través de su servidor. Y luego a través de otros servidores como sip.linphone.org.
Conclusiones
Como resultado, resultó ejecutar
simple_pjsua con transmisión de voz en ambas direcciones a través del servidor. El problema con la SDRAM de 128 Kb adicionalmente gastada se puede resolver utilizando un Cortex-M7 un poco más potente (por ejemplo, STM32F769NI con 512 Kb de RAM), pero al mismo tiempo todavía no tenemos la esperanza de encajar en 256 Kb :) Nos alegrará que alguien esté interesado y aún mejor, inténtalo. Todas las fuentes, como de costumbre, están en nuestro
repositorio .