Utforska Mediastreamer2 VoIP-motorn. Del 11

Materialet i artikeln är hämtat från min zen kanal.

Utforska Mediastreamer2 VoIP-motorn. Del 11

Datarörelsemekanism

  • Datablock dblk_t
  • Meddelande mblk_t
  • Funktioner för att arbeta med meddelanden mblk_t
  • Kö kö_t
  • Funktioner för att arbeta med köer queue_t
  • Ansluta filter
  • Signalpunkt för databehandlingsdiagram
  • Bakom kulisserna aktiviteter av ticker
  • Bufferizer (MSBufferizer)
  • Funktioner för att arbeta med MSBufferizer

Förr artikeln vi har utvecklat ett eget filter. Den här artikeln kommer att fokusera på den interna mekanismen för att flytta data mellan mediastreamerfilter. Detta gör att du kan skriva sofistikerade filter med mindre ansträngning i framtiden.

Datarörelsemekanism

Datarörelse i mediastreamern utförs med hjälp av köer som beskrivs av strukturen queue_t. Strängar av meddelanden som mblk_t, som själva inte innehåller signaldata, utan endast länkar till föregående, nästa meddelande och till datablocket. Dessutom vill jag särskilt betona att det också finns ett fält för en länk till ett meddelande av samma typ, vilket gör att du kan organisera en enskild länkad lista med meddelanden. Vi kommer att kalla en grupp meddelanden som förenas av en sådan lista för en tupel. Således kan vilket element som helst i kön vara ett enda meddelande mblk_t, och kanske chefen för en meddelandetupel mblk_t. Varje tupelmeddelande kan ha sitt eget avdelningsdatablock. Vi kommer att diskutera varför tupler behövs lite senare.

Som nämnts ovan innehåller meddelandet i sig inte ett datablock, utan det innehåller bara en pekare till minnesområdet där blocket är lagrat. I den här delen påminner helhetsbilden av mediastreamerns arbete om dörrlagret i den tecknade filmen "Monsters, Inc.", där dörrar (länkar till data - rum) rör sig i en vansinnig hastighet längs överliggande transportörer, medan själva rummen förbli orörlig.

Nu, rör oss längs hierarkin från botten till toppen, låt oss i detalj överväga de listade enheterna för dataöverföringsmekanismen i mediastreamern.

Datablock dblk_t

Datablocket består av ett huvud och en databuffert. Rubriken beskrivs av följande struktur,

typedef struct datab
{
unsigned char *db_base; // Указатель на начало буфер данных.
unsigned char *db_lim;  // Указатель на конец буфер данных.
void (*db_freefn)(void*); // Функция освобождения памяти при удалении блока.
int db_ref; // Счетчик ссылок.
} dblk_t;

Fälten i strukturen innehåller pekare till början av bufferten, slutet av bufferten och funktionen för att radera databufferten. Sista elementet i rubriken db_ref — referensräknare, om den når noll, fungerar detta som en signal för att radera detta block från minnet. Om datablocket skapades av funktionen data_alloc() , kommer databufferten att placeras i minnet omedelbart efter rubriken. I alla andra fall kan bufferten placeras någonstans separat. Databufferten kommer att innehålla signalsampel eller annan data som vi vill bearbeta med filter.

En ny instans av ett datablock skapas med funktionen:

dblk_t *datab_alloc(int size);

Som en indataparameter ges den storleken på data som blocket kommer att lagra. Mer minne tilldelas för att placera en rubrik - struktur - i början av det tilldelade minnet datab. Men när du använder andra funktioner händer detta inte alltid, i vissa fall kan databufferten vara placerad separat från datablockets rubrik. När du skapar en struktur konfigureras fälten så att dess fält db_base pekade på början av dataområdet, och db_lim till dess slut. Antal länkar db_ref är inställd på ett. Datarensningsfunktionspekaren är inställd på noll.

meddelande mblk_t

Som sagt är köelement av typen mblk_t, den definieras enligt följande:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktur mblk_t innehåller pekare i början b_prev, b_nästa, som är nödvändiga för att organisera en dubbellänkad lista (som är en kö queue_t).

Sedan kommer pekaren b_forts, som endast används när meddelandet är en del av en tupel. För det sista meddelandet i tuppeln förblir denna pekare null.

Därefter ser vi en pekare till ett datablock b_datap, för vilket meddelandet finns. Den följs av pekare till området inuti blockdatabufferten. Fält b_rptr anger den plats från vilken data från bufferten ska läsas. Fält b_wptr indikerar den plats från vilken skrivning till bufferten kommer att utföras.

De återstående fälten är av tjänstekaraktär och hänför sig inte till driften av dataöverföringsmekanismen.

Nedan finns ett enda meddelande med namnet m1 och datablock d1.
Utforska Mediastreamer2 VoIP-motorn. Del 11
Följande bild visar en tupel med tre meddelanden m1, m1_1, m1_2.
Utforska Mediastreamer2 VoIP-motorn. Del 11

Meddelandefunktioner mblk_t

Ett nytt meddelande mblk_t skapad av funktionen:

mblk_t *allocb(int size, int pri); 

hon lägger ett nytt meddelande i minnet mblk_t med ett datablock av angiven storlek Storlek, andra argument - pri används inte i den här versionen av biblioteket. Den ska förbli noll. Under funktionens drift kommer minne att allokeras för strukturen för det nya meddelandet och funktionen kommer att anropas mblk_init(), som kommer att återställa alla fält i den skapade instansen av strukturen och sedan använda ovan nämnda data_alloc(), kommer att skapa en databuffert. Därefter kommer fälten i strukturen att konfigureras:

mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;

Vid utgången får vi ett nytt meddelande med initierade fält och en tom databuffert. För att lägga till data till ett meddelande måste du kopiera det till datablockbufferten:

memcpy(msg->b_rptr, data, size);

där datum är en pekare till datakällan, och Storlek - deras storlek.
sedan måste du uppdatera pekaren till skrivpunkten så att den pekar igen till början av det fria området i bufferten:

msg->b_wptr = msg->b_wptr + size

Om du behöver skapa ett meddelande från en befintlig buffert, utan att kopiera, använd då funktionen:

mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); 

Funktionen kommer, efter att ha skapat meddelandet och strukturen för datablocket, att konfigurera sina pekare till data på adressen buf. De där. i detta fall är databufferten inte placerad efter rubrikfälten i datablocket, vilket var fallet när ett datablock skapades med funktionen data_alloc(). Bufferten med data som skickas till funktionen kommer att förbli där den var, men med hjälp av pekare kommer den att kopplas till den nyskapade rubriken i datablocket, och det, i enlighet med detta, till meddelandet.

Till ett meddelande mblk_t Flera datablock kan sammanlänkas sekventiellt. Detta görs med funktionen:

mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); 

mp — Ett meddelande till vilket ytterligare ett datablock kommer att läggas till.
datum — pekare till blocket, varav en kopia kommer att läggas till meddelandet;
Storlek — Datastorlek;
vaddera — en flagga att storleken på det tilldelade minnet måste justeras längs en 4-byte gräns (utfyllnad kommer att göras med nollor).

Om det finns tillräckligt med utrymme i den befintliga meddelandedatabufferten kommer den nya datan att klistras in bakom den data som redan finns där. Om det finns mindre ledigt utrymme i meddelandedatabufferten än Storlek, sedan skapas ett nytt meddelande med en tillräcklig buffertstorlek och data kopieras till dess buffert. Detta är ett nytt meddelande, länkat till det ursprungliga med hjälp av en pekare b_forts. I det här fallet förvandlas meddelandet till en tupel.

Om du behöver lägga till ytterligare ett datablock till tuplen måste du använda funktionen:

void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);

hon kommer att hitta det sista meddelandet i tuppeln (han har b_forts kommer att vara null) och anropar funktionen för detta meddelande appendb().

Du kan ta reda på storleken på data i ett meddelande eller tupel med funktionen:

int msgdsize(const mblk_t *mp);

den kommer att gå igenom alla meddelanden i tupeln och returnera den totala mängden data i databuffertarna för dessa meddelanden. För varje meddelande beräknas mängden data enligt följande:

 mp->b_wptr - mp->b_rptr

För att kombinera två tuplar, använd funktionen:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

hon lägger till tupeln newm till tupelns svans mp och returnerar en pekare till det sista meddelandet i den resulterande tupeln.

Vid behov kan en tupel omvandlas till ett meddelande med ett enda datablock; detta görs med funktionen:

void msgpullup(mblk_t *mp,int len);

om argument Len är -1, så bestäms storleken på den allokerade bufferten automatiskt. Om Len är ett positivt tal, kommer en buffert av denna storlek att skapas och tupelmeddelandedata kopieras in i den. Om bufferten tar slut kommer kopieringen att sluta där. Det första meddelandet från tupeln kommer att få en ny storleksbuffert med de kopierade data. De återstående meddelandena kommer att raderas och minnet återförs till högen.

När du tar bort en struktur mblk_t referenstalet för datablocket beaktas om, vid anrop freeb() det visar sig vara noll, då raderas databufferten tillsammans med instansen mblk_t, vilket pekar på det.

Initiera fälten i ett nytt meddelande:

void mblk_init(mblk_t *mp);

Lägga till ytterligare data till meddelandet:

mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);

Om den nya datan inte passar in i det lediga utrymmet i meddelandedatabufferten, bifogas ett separat skapat meddelande med en buffert av önskad storlek till meddelandet (en pekare till det tillagda meddelandet sätts i det första meddelandet) och meddelande förvandlas till en tuppel.

Lägga till en bit data till en tupel:

void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); 

Funktionen anropar appendb() i en loop.

Kombinera två tuplar till en:

mblk_t *concatb(mblk_t *mp, mblk_t *newm);

meddelande newm kommer att kopplas till mp.

Göra en kopia av ett enda meddelande:

mblk_t *copyb(const mblk_t *mp);

Komplett kopiering av en tupel med alla datablock:

mblk_t *copymsg(const mblk_t *mp);

Elementen i tupeln kopieras av funktionen copyb().

Skapa en enkel kopia av ett meddelande mblk_t. I detta fall kopieras inte datablocket utan dess referensräknare ökas db_ref:

mblk_t *dupb(mblk_t *mp);

Att göra en lätt kopia av en tuppel. Datablock kopieras inte, bara deras referensräknare inkrementeras db_ref:

mblk_t *dupmsg(mblk_t* m);

Limma alla meddelanden från en tupel till ett meddelande:

void msgpullup(mblk_t *mp,size_t len);

Om argumentet Len är -1, så bestäms storleken på den allokerade bufferten automatiskt.

Ta bort ett meddelande, tuple:

void freemsg(mblk_t *mp);

Datablockets referensräkning minskas med ett. Om den når noll raderas även datablocket.

Beräkning av den totala mängden data i ett meddelande eller tupel.

size_t msgdsize(const mblk_t *mp);

Hämta ett meddelande från slutet av kön:

mblk_t *ms_queue_peek_last (q);

Kopiera innehållet i de reserverade fälten i ett meddelande till ett annat meddelande (i själva verket innehåller dessa fält flaggor som används av mediastreamern):

mblk_meta_copy(const mblk_t *source, mblk *dest);

vända queue_t

Meddelandekön i mediastreamern är implementerad som en cirkulär dubbellänkad lista. Varje listelement innehåller en pekare till ett datablock med signalsampel. Det visar sig att endast pekare till datablocket rör sig i tur och ordning, medan själva datan förblir orörlig. De där. endast länkar till dem flyttas.
Struktur som beskriver kön queue_t, visas nedan:

typedef struct _queue
{
   mblk_t _q_stopper; /* "Холостой" элемент очереди, не указывает на данные, используется только для управления очередью. При инициализации очереди (qinit()) его указатели настраиваются так, чтобы они указывали на него самого. */
   int q_mcount;        // Количество элементов в очереди.
} queue_t;

Strukturen innehåller ett fält - en pekare _q_stopper skriv *mblk_t, den pekar på det första elementet (meddelandet) i kön. Det andra fältet i strukturen är räknaren för meddelanden i kön.
Bilden nedan visar en kö med namnet q1 som innehåller 4 meddelanden m1, m2, m3, m4.
Utforska Mediastreamer2 VoIP-motorn. Del 11
Följande figur visar en kö med namnet q1 som innehåller 4 meddelanden m1,m2,m3,m4. Meddelande m2 är huvudet på en tuppel som innehåller ytterligare två meddelanden m2_1 och m2_2.

Utforska Mediastreamer2 VoIP-motorn. Del 11

Funktioner för att arbeta med köer queue_t

Köinitiering:

void qinit(queue_t *q);

Fält _q_stopper (hädanefter kommer vi att kalla det "stopper") initieras av funktionen mblk_init(), dess föregående element och nästa elementpekare justeras för att peka på sig själv. Köelementräknaren återställs till noll.

Lägga till ett nytt element (meddelanden):

void putq(queue_t *q, mblk_t *m);

Nytt element m läggs till i slutet av listan, justeras elementpekarna så att stopperen blir nästa element för den, och den blir föregående element för stopperen. Köelementräknaren inkrementeras.

Hämta ett element från kön:

mblk_t * getq(queue_t *q); 

Meddelandet som kommer efter att proppen har hämtats och elementräknaren minskas. Om det inte finns några element i kön förutom stopparen, returneras 0.

Infoga ett meddelande i en kö:

void insq(queue_t *q, mblk_t *emp, mblk_t *mp); 

Элемент mp infogas före elementet emp. om emp=0, då läggs meddelandet till i slutet av kön.

Hämtar ett meddelande från chefen för kön:

void remq(queue_t *q, mblk_t *mp); 

Elementräknaren minskas.

Läsa en pekare till det första elementet i kön:

mblk_t * peekq(queue_t *q); 

Ta bort alla element från kön samtidigt som elementen själva tas bort:

void flushq(queue_t *q, int how);

argument hur inte använd. Köelementräknaren är inställd på noll.

Makro för att läsa en pekare till det sista elementet i kön:

mblk_t * qlast(queue_t *q);

När du arbetar med meddelandeköer, var medveten om att när du ringer ms_queue_put(q, m) med en nollpekare till meddelandet, slingrar funktionen. Ditt program kommer att frysa. beter sig likadant ms_queue_next(q, m).

Ansluta filter

Kön som beskrivs ovan används för att skicka meddelanden från ett filter till ett annat eller från ett till flera filter. Filter och deras kopplingar bildar en riktad graf. Filtrets ingång eller utgång kommer att kallas det allmänna ordet "stift". För att beskriva i vilken ordning filter är anslutna till varandra använder mediastreamern begreppet "signalpunkt". Signalpunkt är struktur _MSCPoint, som innehåller en pekare till filtret och numret på ett av dess stift; följaktligen beskriver den anslutningen av en av filtrets ingångar eller utgångar.

Signalpunkt för databehandlingsdiagram

typedef struct _MSCPoint{
struct _MSFilter *filter; // Указатель на фильтр медиастримера.
int pin;                        // Номер одного из входов или выходов фильтра, т.е. пин.
} MSCPoint;

Filterstiften är numrerade från noll.

Anslutningen av två stift genom en meddelandekö beskrivs av strukturen _MSQueue, som innehåller en meddelandekö och pekare till de två signalpunkterna som den ansluter:

typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;

Vi kommer att kalla denna struktur en signallänk. Varje mediastreamerfilter innehåller en tabell med ingångslänkar och en tabell med utgående länkar (MSQueue). Storleken på tabeller ställs in när ett filter skapas, vi har redan gjort detta med en exporterad variabel av typen MSFilterDesc, när vi utvecklade vårt eget filter. Nedan finns en struktur som beskriver alla filter i en mediastreamer, MSFilter:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Efter att vi kopplade filtren i C-programmet i enlighet med vår plan (men inte kopplade tickern), skapade vi därmed en riktad graf, vars noder är instanser av strukturen MSFilter, och kanter är instanser av länkar MSQueue.

Bakom kulisserna aktiviteter av ticker

När jag berättade att tickern är ett filter för källan till fästingar, var det inte hela sanningen om det. En ticker är ett objekt som kör funktioner på klockan bearbeta() alla filter i kretsen (grafen) som den är ansluten till. När vi kopplar en ticker till ett graffilter i ett C-program visar vi tickern den graf som den kommer att styra från och med nu tills vi stänger av den. Efter anslutningen börjar tickern undersöka grafen som anförtrotts till dess vård och sammanställer en lista över filter som inkluderar den. För att inte "räkna" samma filter två gånger, markerar det de upptäckta filtren genom att placera en kryssruta i dem sett. Sökningen görs med hjälp av länktabellerna som varje filter har.

Under sin inledande rundtur i grafen kontrollerar tickern om det bland filtren finns åtminstone ett som fungerar som en källa till datablock. Om det inte finns några, anses grafen vara felaktig och tickern kraschar.

Om grafen visar sig vara "korrekt" för varje hittat filter, anropas funktionen för initialisering förprocess(). Så snart ögonblicket kommer för nästa bearbetningscykel (var tionde millisekund som standard), anropar tickern funktionen bearbeta() för alla tidigare hittade källfilter och sedan för de återstående filtren i listan. Om filtret har ingångslänkar kör du funktionen bearbeta() upprepas tills ingångslänkköerna är tomma. Efter detta går det vidare till nästa filter i listan och "rullar" det tills inmatningslänkarna är fria från meddelanden. Tickern flyttas från filter till filter tills listan slutar. Detta avslutar behandlingen av cykeln.

Nu ska vi återvända till tupler och prata om varför en sådan enhet lades till mediestreamern. I allmänhet sammanfaller inte mängden data som krävs av algoritmen som arbetar inuti filtret och är inte en multipel av storleken på databuffertarna som tas emot vid ingången. Till exempel skriver vi ett filter som utför en snabb Fourier-transform, som per definition bara kan bearbeta datablock vars storlek är en potens av två. Låt det vara 512 punkter. Om datan genereras av en telefonkanal kommer databufferten för varje meddelande vid ingången att ge oss 160 signalsampel. Det är frestande att inte samla in data från ingången förrän den nödvändiga mängden data finns där. Men i det här fallet kommer en kollision att inträffa med tickern, som utan framgång kommer att försöka rulla filtret tills ingångslänken är tom. Tidigare utsåg vi denna regel som den tredje principen för filtret. Enligt denna princip måste filtrets process()-funktion ta all data från ingångsköerna.

Dessutom kommer det inte att vara möjligt att ta endast 512 prover från ingången, eftersom man bara kan ta hela block, d.v.s. filtret måste ta 640 prover och använda 512 av dem, resten innan en ny del av data samlas. Således måste vårt filter, utöver sitt huvudsakliga arbete, tillhandahålla hjälpåtgärder för mellanlagring av indata. Utvecklarna av mediastreamern och lösningen på detta allmänna problem har utvecklat ett speciellt objekt - MSBufferizer (buffert), som löser detta problem med hjälp av tupler.

Bufferizer (MSBufferizer)

Detta är ett objekt som kommer att ackumulera indata i filtret och börja skicka in det för bearbetning så snart mängden information är tillräcklig för att köra filteralgoritmen. Medan bufferten ackumulerar data kommer filtret att arbeta i viloläge, utan att använda upp processorkraften. Men så snart läsfunktionen från bufferten returnerar ett annat värde än noll, börjar filtrets process()-funktion att ta och bearbeta data från bufferten i delar av den önskade storleken, tills den är slut.
Data som ännu inte krävs förblir i bufferten som det första elementet i tupeln, till vilken efterföljande block av indata är kopplade.

Strukturen som beskriver bufferten:

struct _MSBufferizer{
queue_t q; /* Очередь сообщений. */
int size; /* Суммарный размер данных находящихся в буферизаторе в данный момент. */
};
typedef struct _MSBufferizer MSBufferizer;

Funktioner för att arbeta med MSBufferizer

Skapa en ny buffertinstans:

MSBufferizer * ms_bufferizer_new(void);

Minnet tilldelas, initieras in ms_bufferizer_init() och en pekare returneras.

Initieringsfunktion:

void ms_bufferizer_init(MSBufferizer *obj); 

Kön initieras q, fält Storlek är inställd på noll.

Lägga till ett meddelande:

void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); 

Meddelande m läggs till i kön. Den beräknade storleken på datablock läggs till Storlek.

Överför alla meddelanden från länkdatakön till bufferten q:

void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   

Överföra meddelanden från en länk q i bufferten utförs med funktionen ms_bufferizer_put().

Läser från bufferten:

int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); 

Om storleken på data som samlas i bufferten är mindre än den begärda (datalen), då returnerar funktionen noll, data kopieras inte till data. I annat fall utförs sekventiell kopiering av data från tupler som finns i bufferten. Efter kopiering raderas tuppeln och minnet frigörs. Kopieringen slutar i det ögonblick då datalenbytes kopieras. Om utrymmet tar slut i mitten av ett datablock, kommer i detta meddelande datablocket att reduceras till den återstående delen som inte har kopierats. Nästa gång du ringer fortsätter kopieringen från denna punkt.

Läser mängden data som för närvarande är tillgänglig i bufferten:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Returnerar fältet Storlek buffert.

Kasta en del av data i bufferten:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Det angivna antalet byte med data hämtas och kasseras. De äldsta uppgifterna kasseras.

Ta bort alla meddelanden i bufferten:

void ms_bufferizer_flush(MSBufferizer *obj); 

Dataräknaren återställs till noll.

Ta bort alla meddelanden i bufferten:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Räknaren är inte nollställd.

Ta bort bufferten och frigöra minne:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Exempel på användning av bufferten finns i källkoden för flera mediastreamerfilter. Till exempel, i MS_L16_ENC-filtret, som omarrangerar byten i samplen från nätverksordningen till värdordningen: l16.c

I nästa artikel kommer vi att titta på frågan om att uppskatta belastningen på en ticker och sätt att bekämpa överdriven datorbelastning i en mediastreamer.

Källa: will.com

Lägg en kommentar