Telefono SIP su STM32F7-Discovery

Ciao a tutti.

Tempo fa, abbiamo scrivevano discusso su come siamo riusciti a far funzionare un telefono SIP su STM32F4-Discovery con 1 MB di ROM e 192 KB di RAM basato su Embox. È importante notare che quella versione era minimale e collegava direttamente due telefoni senza server, con la trasmissione della voce solo in una direzione. Pertanto, abbiamo deciso di implementare un telefono più completo con chiamate attraverso il server e trasmissione della voce in entrambe le direzioni, cercando di rimanere entro i limiti di memoria il più possibile.

Guarda il video

Per il telefono, abbiamo deciso di utilizzare l'applicazione simple_pjsua inclusa nella libreria PJSIP. Si tratta di un'applicazione minimale che può registrarsi su un server, ricevere e effettuare chiamate. Di seguito fornirò subito una descrizione di come avviarlo su STM32F7-Discovery.

Come avviare

  1. Configuriamo Embox
    make confload-platform/pjsip/stm32f7cube
  2. Nel file conf/mods.config impostiamo l'account SIP necessario.
    
    include platform.pjsip.cmd.simple_pjsua_imported(
        sip_domain="server", 
        sip_user="username",
        sip_passwd="password")
    

    dove server è il server SIP (ad esempio, sip.linphone.org), username e password è il nome utente e la password dell'account.

  3. Compiliamo Embox con il comando make. Abbiamo informazioni sulla programmazione della scheda su wiki e in abbiamo chiarito il corretto completamento dei programmi che utilizzano il mediastreamer..
  4. Eseguiamo nel terminale Embox il comando “simple_pjsua_imported”
    
    00:00:12.870    pjsua_acc.c  ....Lo stato outbound SIP per acc 0 non è attivo
    00:00:12.884    pjsua_acc.c  ....sip:alexk2222@sip.linphone.org: registrazione riuscita, stato=200 (Registrazione riuscita
    00:00:12.911    pjsua_acc.c  ....Timer keep-alive avviato per acc 0, destinazione:91.121.209.194:5060, intervallo:15s
    

  5. Infine, resta da inserire gli altoparlanti o le cuffie nell'uscita audio e parlare in due piccoli microfoni MEMS accanto al display. Chiamiamo da Linux tramite l'applicazione simple_pjsua, pjsua. Oppure si può utilizzare qualsiasi altra applicazione come linphone.

Tutto questo è descritto nel nostro wiki.

Come ci siamo arrivati

Quindi, inizialmente ci siamo posti la questione della scelta della piattaforma hardware. Poiché era chiaro che STM32F4-Discovery non fosse adeguata per la memoria, abbiamo scelto STM32F7-Discovery. Questa ha 1 MB di flash e 256 KB di RAM (+ 64 MB di memoria veloce speciale, che utilizzeremo anche). Anch'essa non è molto per le chiamate attraverso un server, ma abbiamo deciso di provare a starci dentro.

Abbiamo fondamentalmente diviso il compito in diverse fasi:

  • Avvio di PJSIP su QEMU. Questo era comodo per il debug, inoltre avevamo già supporto per il codec AC97.
  • Registrazione e riproduzione della voce su QEMU e su STM32.
  • Porting dell'applicazione simple_pjsua della libreria PJSIP. Questa permette di registrarsi su un server SIP e fare chiamate.
  • Implementare il proprio server basato su Asterisk e testarlo, dopo di che provare esterni, come sip.linphone.org.

Il suono in Embox funziona tramite Portaudio, che viene utilizzato anche in PISIP. Su QEMU sono emersi i primi problemi: i file WAV a 44100 Hz venivano riprodotti correttamente, ma a 8000 Hz qualcosa non andava. Si è scoperto che il problema era nella configurazione della frequenza: di default era impostata a 44100 sull'hardware e non poteva essere modificata programmando.

Qui, probabilmente, è utile spiegare brevemente come avviene effettivamente la riproduzione del suono. È possibile impostare un puntatore su una porzione di memoria nella scheda audio da cui è necessario riprodurre o registrare a una frequenza preimpostata. Dopo che il buffer si esaurisce, viene generata un'interruzione, e l'esecuzione continua con il buffer successivo. Il problema è che è necessario riempire questi buffer in anticipo, mentre il precedente viene riprodotto. Affronteremo ancora questa problematica su STM32F7.

Successivamente, abbiamo affittato un server e abbiamo installato Asterisk. Poiché era necessario effettuare molte debug e parlare al microfono non era piacevole, abbiamo dovuto implementare la riproduzione e registrazione automatica. A questo scopo abbiamo patchato simple_pjsua in modo da poter inserire file al posto dei dispositivi audio. In PJSIP è piuttosto semplice, poiché hanno un concetto di porta che può essere sia un dispositivo che un file. Queste porte possono essere collegate in modo flessibile ad altre porte. Puoi vedere il codice nel nostro pjsip. repository. Di conseguenza, lo schema era il seguente. Sul server Asterisk ho creato due account: uno per Linux e uno per Embox. Successivamente, su Embox viene eseguito il comando simple_pjsua_imported, Embox si registra sul server, dopo di che chiamiamo da Linux a Embox. Nel momento della connessione, verifichiamo sul server Asterisk che tutte le connessioni siano stabilite e dopo un certo tempo dovremmo sentire su Embox il suono proveniente da Linux, mentre in Linux salviamo il file che viene riprodotto da Embox.

Dopo che questo ha funzionato su QEMU, siamo passati al porting su STM32F7-Discovery. Il primo problema è stato che non riuscivamo a stare dentro 1 MB di ROM senza attivare l'ottimizzazione del compilatore "-Os" per dimensioni dell'immagine. Quindi abbiamo attivato "-Os". Inoltre, abbiamo disabilitato il supporto C++ tramite un patch, poiché è necessario solo per pjsua, mentre noi utilizziamo simple_pjsua.

Dopo averlo sistemato simple_pjsua, abbiamo deciso che ora c'erano buone possibilità di avviarlo. Ma prima dovevamo capire come registrare e riprodurre la voce. La domanda è: dove registrare? Abbiamo scelto la memoria esterna — SDRAM (128 MB). Puoi provare tu stesso:

Creerà un WAV stereo a 16000 Hz e una durata di 10 secondi:


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

Riproduzione:


play -m C0000000

Qui si sono presentati due problemi. Il primo riguarda il codec: si usa WM8994, e in esso esiste un concetto chiamato slot, e ci sono 4 slot. Quindi, per impostazione predefinita, se non si configura, durante la riproduzione audio, il suono si verifica in tutti e quattro gli slot. Di conseguenza, a 16000 Hz ottenevamo 8000 Hz, mentre per 8000 Hz la riproduzione semplicemente non funzionava. Quando abbiamo selezionato solo gli slot 0 e 2, ha funzionato correttamente. Un altro problema era l'interfaccia audio in STM32Cube, in cui l'uscita audio funziona tramite SAI (Serial Audio Interface) sincronizzata con l'ingresso audio (non ho approfondito i dettagli, ma sembra che condividano un clock comune e, durante l'inizializzazione dell'uscita audio, in qualche modo si associa all'ingresso audio). Quindi non era possibile farli funzionare separatamente, così abbiamo fatto quanto segue: l'ingresso e l'uscita audio lavorano sempre (inclusi gli interrupt generati). Ma quando non viene riprodotto nulla nel sistema, semplicemente forniamo un buffer vuoto all'uscita audio, e quando inizia la riproduzione, lo riempiamo correttamente.

Successivamente, ci siamo trovati di fronte al problema che il suono durante la registrazione della voce era molto debole. Questo accade perché i microfoni MEMS su STM32F7-Discovery non funzionano bene a frequenze inferiori a 16000 Hz. Pertanto impostiamo 16000 Hz, anche se arriva a 8000 Hz. Tuttavia, per questo è stato necessario aggiungere una conversione software da una frequenza all'altra.

Successivamente, è stato necessario aumentare la dimensione dello heap, che si trova nella RAM. Secondo i nostri calcoli, pjsip richiedeva circa 190 kB, mentre noi avevamo solo circa 100 kB disponibili. Qui è stato necessario utilizzare un po' di memoria esterna — SDRAM (circa 128 kB).

Dopo tutte queste modifiche, ho visto i primi pacchetti tra Linux ed Embox e ho sentito il suono! Ma il suono era orribile, non era affatto come su QEMU, nulla poteva essere compreso. Allora abbiamo inizato a chiedere quale potesse essere il problema. Il debug ha mostrato che Embox non riesce a riempire/svuotare i buffer audio in tempo. Mentre pjsip elabora un frame, avvenivano due interruzioni a completi di elaborazione dei buffer, il che è troppo. La prima idea per accelerare era l'ottimizzazione del compilatore, ma era già abilitata in PJSIP. La seconda era il punto in virgola mobile hardware, di cui abbiamo già parlato. abbiamo chiarito il corretto completamento dei programmi che utilizzano il mediastreamer.. Ma come ha dimostrato la pratica, l'FPU non ha portato a un incremento significativo della velocità. Il passo successivo è stato impostare le priorità dei flussi. In Embox ci sono diverse strategie di pianificazione, e ho attivato quella che supporta le priorità, dando il massimo delle priorità ai flussi audio. Anche questo non ha avuto effetto.

La prossima idea era che lavorassimo con la memoria esterna e sarebbe stato utile spostare lì le strutture a cui accediamo molto frequentemente. Ho effettuato un'analisi preliminare su quando e per cosa simple_pjsua viene allocata memoria. Si è scoperto che su 190 Kb, i primi 90 Kb sono allocati per le esigenze interne di PJSIP e a esse si accede non molto spesso. Inoltre, durante una chiamata in arrivo, viene chiamata la funzione pjsua_call_answer, in cui vengono allocati i buffer per gestire i frame in entrata e in uscita. Ciò ha comportato ulteriori circa 100 Kb. E qui ci siamo comportati nel seguente modo. Fino al momento della chiamata, i dati sono posizionati nella memoria esterna. Non appena arriva la chiamata, sostituiamo immediatamente l'heap con un altro in RAM. In questo modo, tutti i dati “caldi” sono stati trasferiti in una memoria più veloce e prevedibile.

In definitiva, tutto questo ha permesso di avviare simple_pjsua e effettuare chiamate attraverso il proprio server. E successivamente anche attraverso altri server come sip.linphone.org.

Conclusioni

Di conseguenza, siamo riusciti a far partire simple_pjsua con trasmissione vocale in entrambe le direzioni tramite il server. Il problema con i 128 Kb di SDRAM spesi in più può essere risolto utilizzando un Cortex-M7 leggermente più potente (ad esempio, STM32F769NI con 512 Kb di RAM), ma nel contempo non abbiamo ancora perso la speranza di rimanere all'interno dei 256 Kb 🙂 Saremmo felici se qualcuno si mostrasse interessato, e ancor meglio — provasse. Tutti i sorgenti, come al solito, sono nel nostro repository.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS - ProHoster