Salutare tuturor.
Cu ceva timp în urmă, noi am reușit să lansăm un telefon SIP pe STM32F4-Discovery cu 1 MB ROM și 192 KB RAM) bazat pe . Aici trebuie să menționez că acea versiune a fost minimă și conecta două telefoane direct, fără server și cu transmiterea vocii doar într-o direcție. Astfel, am decis să lansăm un telefon mai complet, cu apeluri prin server și transmiterea vocii în ambele direcții, dar în același timp să ne încadram în un spațiu de memorie cât mai mic.

Pentru telefon, am decis să alegem aplicația simple_pjsua din biblioteca PJSIP. Este o aplicație minimă care poate să se înregistreze pe server, să primească și să răspundă la apeluri. Mai jos voi prezenta imediat descrierea despre cum să o lansăm pe STM32F7-Discovery.
Cum să lansăm
- Configurăm Embox
make confload-platform/pjsip/stm32f7cube - În fișierul conf/mods.config setăm contul SIP dorit.
include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password")unde server — acesta este serverul SIP (de exemplu, sip.linphone.org), username și parola — numele de utilizator și parola contului.
- Construim Embox cu comanda make. Despre flashing-ul plăcii avem pe și în .
- Lansăm în consolă comanda Embox „simple_pjsua_imported”
00:00:12.870 pjsua_acc.c ....Starea outbound SIP pentru acc 0 nu este activă 00:00:12.884 pjsua_acc.c ....sip:alexk2222@sip.linphone.org: înregistrare reușită, status=200 (Înregistrare reușită 00:00:12.911 pjsua_acc.c ....Timer-ul keep-alive a fost pornit pentru acc 0, destinație:91.121.209.194:5060, interval:15s - În cele din urmă, rămâne să conectăm difuzoarele sau căștile la ieșirea audio și să vorbim în cele două mici microfoane MEMS de lângă ecran. Facem apeluri din Linux prin aplicația simple_pjsua, pjsua. Sau putem folosi orice altă aplicație de tip linphone.
Toate acestea sunt descrise pe .
Cum am ajuns aici
Așadar, la început, s-a pus problema alegerii platformei hardware. Fiindcă era clar că STM32F4-Discovery nu se va potrivi din punct de vedere al memoriei, am ales STM32F7-Discovery. Are un flash de 1 MB și 256 KB RAM (+ 64 de memorie rapidă specială, pe care o vom folosi de asemenea). De asemenea, nu este prea mult pentru apeluri prin server, dar am decis să încercăm să ne încadrăm.
Pentru noi, am împărțit sarcina în mai multe etape:
- Lansarea PJSIP pe QEMU. A fost convenabil pentru depanare, plus că deja aveam suport pentru codec AC97.
- Înregistrarea vocii și redarea pe QEMU și STM32.
- Portarea aplicației simple_pjsua din cadrul PJSIP. Aceasta permite înregistrarea pe serverul SIP și efectuarea apelurilor.
- Dezvoltarea propriului server pe baza Asterisk și testarea acestuia, după care încercarea unor servicii externe, precum sip.linphone.org.
Sunetul în Embox funcționează prin Portaudio, care este folosit și în PISIP. Pe QEMU au apărut primele probleme - fișierele WAV la 44100 Hz se redau bine, dar la 8000 ceva nu mergea cum trebuie. S-a dovedit că problema era setarea frecvenței - implicit, hardware-ul era setat pe 44100, iar noi nu am modificat acest lucru din program.
Aici, probabil, ar trebui să explicăm puțin cum se desfășoară redarea sunetului. Plăcii de sunet îi poți seta un pointer la un bloc de memorie din care trebuie să redai sau să înregistrezi la o frecvență stabilită anterior. După ce bufferul se termină, se generează o întrerupere și execuția continuă cu următorul buffer. Problema este că aceste buffere trebuie umplute din timp, în timp ce se redă cel precedent. Această problemă o vom întâlni din nou pe STM32F7.
Ulterior, am închiriat un server și am desfășurat pe el Asterisk. Deoarece trebuie să facem multe teste și nu voiam să vorbim mult la microfon, trebuia să creăm o redare și înregistrare automată. Pentru asta, am modificat simple_pjsua astfel încât să putem introduce fișiere în loc de dispozitive audio. În PJSIP, acest lucru se face destul de simplu, deoarece au un concept de port, care poate fi atât un dispozitiv, cât și un fișier. Și aceste porturi pot fi conectate flexibil la alte porturi. Poți consulta codul în pjsip. . În cele din urmă, schema a fost următoarea. Pe serverul Asterisk, am creat două conturi - pentru Linux și pentru Embox. Apoi, pe Embox se execută comanda simple_pjsua_imported, Embox se înregistrează pe server, după care, din Linux, sunăm la Embox. În momentul conexiunii, verificăm pe serverul Asterisk că toate conexiunile sunt stabilite și, după un timp, ar trebui să auzim în Embox sunetul din Linux, iar în Linux salvăm acel fișier care se redă din Embox.
După ce acest lucru a funcționat pe QEMU, am trecut la portarea pe STM32F7-Discovery. Prima problemă - nu încăpeam în 1 MB ROM fără activarea opțiunii de optimizare a compilatorului "-Os" pentru dimensiunea imaginii. Așa că am activat "-Os". Ulterior, printr-un patch am dezactivat suportul pentru C++, deoarece era necesar doar pentru pjsua, iar noi folosim simple_pjsua.
După ce am reușit să încăpem simple_pjsua, am decis că acum avem șanse să-l lansăm. Dar mai întâi trebuia să ne ocupăm de înregistrarea și redarea vocii. Întrebarea - unde să înregistrăm? Am ales memoria externă - SDRAM (128 MB). Poți încerca și tu acest lucru:
Va crea un WAV stereo cu o frecvență de 16000 Hz și o durată de 10 secunde:
record -r 16000 -c 2 -d 10000 -m C0000000
Redăm:
play -m C0000000
Aici au apărut două probleme. Prima este legată de codec — se folosește WM8994, care are un concept numit slot, și sunt patru astfel de sloturi. Așadar, implicit, dacă nu se configurează, la redarea audio se va întâmpla în toate cele patru sloturi. De aceea, la frecvența de 16000 Hz am obținut 8000 Hz, iar pentru 8000 Hz redarea pur și simplu nu a funcționat. Când s-au ales doar sloturile 0 și 2, a funcționat așa cum trebuia. O altă problemă a fost interfața audio în STM32Cube, în care ieșirea audio funcționează prin SAI (Interfață Audio Serială) sincronizat cu intrarea audio (nu am intrat în detalii, dar se pare că ele împărtășesc un ceas comun și, la inițializarea ieșirii audio, ceva se leagă de intrarea audio). Asta înseamnă că nu le putem rula separat, așa că am făcut următoarele — întotdeauna funcționează (inclusiv sunt generate întreruperi) intrarea audio și ieșirea audio. Dar când în sistem nu se redă nimic, pur și simplu furnizăm ieșirii audio un buffer gol, iar când începe redarea, începem să-l umplem corect.
Apoi ne-am confruntat cu faptul că sunetul în timpul înregistrării vocii era foarte slab. Acest lucru se întâmplă deoarece microfoanele MEMS de pe STM32F7-Discovery funcționează imperfect la frecvențe sub 16000 Hz. Așadar, setăm 16000 Hz, chiar dacă primim 8000 Hz. În acest sens, a fost necesar să adăugăm o conversie software a unei frecvențe în alta.
De asemenea, a fost necesar să măresc dimensiunea heap-ului din RAM. Conform calculelor noastre, pjsip necesita aproximativ 190 KB, iar noi aveam doar aproximativ 100 KB rămase. Aici a fost necesar să utilizăm puțină memorie externă — SDRAM (aproximativ 128 KB).
După toate aceste corecturi, am văzut primele pachete între Linux și Embox și am auzit sunet! Dar sunetul era groaznic, deloc la fel ca în QEMU, nu se putea distinge nimic. Atunci ne-am gândit care ar putea fi problema. Depanarea a arătat că Embox pur și simplu nu reușea să umple/elibereze bufferele audio. În timp ce pjsip procesa un cadru, aveau loc două întreruperi cu finalizarea procesării bufferelor, ceea ce era prea mult. Prima idee pentru accelerare a fost optimizarea compilatorului, dar aceasta era deja inclusă în PJSIP. A doua — punctul flotant hardware, despre care am vorbit în . Dar, așa cum a arătat practica, FPU nu a adus o creștere semnificativă a vitezei. Următorul pas a fost stabilirea priorităților pentru fluxuri. În Embox există diferite strategii de planificare, iar eu am activat cea care susține prioritățile, atribuindo fluxurilor audio cea mai mare prioritate. Asta nu a ajutat nici de această dată.
Următoarea idee a fost că lucrăm cu memorie externă și ar fi bine să mutăm structurile acolo, deoarece accesul la ele are loc extrem de frecvent. Am realizat o analiză preliminară a momentelor și scopurilor pentru care simple_pjsua se alocă memorie. S-a dovedit că din cele 190 KB, primele 90 KB sunt alocate pentru nevoile interne ale PJSIP și la acestea accesul nu se face foarte frecvent. Apoi, în timpul unui apel incoming, este apelată funcția pjsua_call_answer, în cadrul căreia sunt alocate apoi buferele pentru a lucra cu cadrele incoming și outgoing. Aceasta a consumat încă aproximativ 100 KB. Așadar, am procedat în felul următor. Până la momentul apelului, datele sunt plasate în memorie externă. Odată ce apelul a început, imediat înlocuim heap-ul cu unul diferit — în RAM. Astfel, toate datele
au fost mutate într-o memorie mai rapidă și mai predictibilă. simple_pjsua În final, toate acestea au permis inițierea
Conclusions
apelurilor prin serverul nostru. Apoi, și prin alte servere, cum ar fi sip.linphone.org. simple_pjsua Astfel, am reușit să finalizăm .
Sursa: habr.com
