De Mediastreamer2 VoIP-engine verkennen. Deel 11

Het materiaal van het artikel is afkomstig uit mijn zen-kanaal.

De Mediastreamer2 VoIP-engine verkennen. Deel 11

Mechanisme voor gegevensverplaatsing

  • Gegevensblok dblk_t
  • Bericht mblk_t
  • Functies voor het werken met berichten mblk_t
  • Wachtrij wachtrij_t
  • Functies voor het werken met wachtrijen wachtrij_t
  • Filters aansluiten
  • Signaalpunt van gegevensverwerkingsgrafiek
  • Achter de schermen activiteiten van de ticker
  • Bufferizer (MSBufferizer)
  • Functies voor het werken met MSBufferizer

In het verleden статье wij hebben ons eigen filter ontwikkeld. Dit artikel zal zich richten op het interne mechanisme voor het verplaatsen van gegevens tussen mediastreamerfilters. Hierdoor kunt u in de toekomst met minder moeite geavanceerde filters schrijven.

Mechanisme voor gegevensverplaatsing

Gegevensverplaatsing in de mediastreamer wordt uitgevoerd met behulp van wachtrijen die door de structuur worden beschreven wachtrij_t. Reeksen berichten zoals mblk_t, die zelf geen signaalgegevens bevatten, maar alleen links naar het vorige, volgende bericht en naar het datablok. Daarnaast wil ik vooral benadrukken dat er ook een veld is voor een link naar een bericht van hetzelfde type, waarmee je een enkelvoudig gekoppelde lijst met berichten kunt organiseren. We zullen een groep berichten verenigd door zo'n lijst een tupel noemen. Elk element van de wachtrij kan dus één enkel bericht zijn mblk_t, en misschien de kop van een berichttupel mblk_t. Elk tupelbericht kan zijn eigen afdelingsgegevensblok hebben. We zullen later bespreken waarom tupels nodig zijn.

Zoals hierboven vermeld, bevat het bericht zelf geen gegevensblok; in plaats daarvan bevat het alleen een verwijzing naar het geheugengebied waar het blok is opgeslagen. In dit deel doet het totaalbeeld van het werk van de mediastreamer denken aan het deurenmagazijn in de cartoon 'Monsters, Inc.', waar deuren (links naar data - kamers) met een waanzinnige snelheid langs hangbanden bewegen, terwijl de kamers zelf roerloos blijven.

Laten we nu, langs de hiërarchie van onder naar boven, de opgesomde entiteiten van het datatransmissiemechanisme in de mediastreamer in detail bekijken.

Gegevensblok dblk_t

Het datablok bestaat uit een header en een databuffer. De header wordt beschreven door de volgende structuur,

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

De velden van de structuur bevatten verwijzingen naar het begin van de buffer, het einde van de buffer en de functie voor het verwijderen van de gegevensbuffer. Laatste element in koptekst db_ref — referentieteller, als deze nul bereikt, dient dit als signaal om dit blok uit het geheugen te verwijderen. Als het datablok door de functie is gemaakt datab_alloc() , dan wordt de databuffer onmiddellijk na de header in het geheugen geplaatst. In alle overige gevallen kan de buffer ergens apart geplaatst worden. De databuffer bevat signaalmonsters of andere gegevens die we met filters willen verwerken.

Er wordt een nieuw exemplaar van een datablok gemaakt met behulp van de functie:

dblk_t *datab_alloc(int size);

Als invoerparameter krijgt deze de grootte van de gegevens die het blok zal opslaan. Er wordt meer geheugen toegewezen om een ​​header (structuur) aan het begin van het toegewezen geheugen te plaatsen gegevensbestand. Maar bij gebruik van andere functies gebeurt dit niet altijd; in sommige gevallen kan de databuffer zich afzonderlijk van de datablokheader bevinden. Bij het maken van een structuur worden de velden zo geconfigureerd dat het veld db_basis wees naar het begin van het gegevensgebied, en db_lim tot zijn einde. Aantal koppelingen db_ref is ingesteld op één. De aanwijzer voor de functie voor het wissen van gegevens is ingesteld op nul.

bericht mblk_t

Zoals gezegd zijn wachtrij-elementen van het type mblk_t, het wordt als volgt gedefinieerd:

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;

Structuur mblk_t bevat aanwijzingen aan het begin b_vorige, b_volgende, die nodig zijn om een ​​dubbel gekoppelde lijst te organiseren (wat een wachtrij is wachtrij_t).

Dan komt de wijzer b_cont, die alleen wordt gebruikt als het bericht deel uitmaakt van een tupel. Voor het laatste bericht in het tupel blijft deze aanwijzer nul.

Vervolgens zien we een verwijzing naar een datablok b_datap, waarvoor het bericht bestaat. Het wordt gevolgd door verwijzingen naar het gebied binnen de blokgegevensbuffer. Veld b_rptr specificeert de locatie waarvandaan gegevens uit de buffer worden gelezen. Veld b_wptr geeft de locatie aan vanwaar schrijfbewerkingen naar de buffer zullen worden uitgevoerd.

De overige velden zijn servicegericht en hebben geen betrekking op de werking van het gegevensoverdrachtmechanisme.

Hieronder staat een enkel bericht met de naam m1 en datablok d1.
De Mediastreamer2 VoIP-engine verkennen. Deel 11
De volgende afbeelding toont een tupel van drie berichten m1, m1_1, m1_2.
De Mediastreamer2 VoIP-engine verkennen. Deel 11

Berichtenfuncties mblk_t

Een nieuw bericht mblk_t gemaakt door de functie:

mblk_t *allocb(int size, int pri); 

ze plaatst een nieuw bericht in haar geheugen mblk_t met een datablok van de opgegeven grootte grootte, tweede argument - pri niet gebruikt in deze versie van de bibliotheek. Het moet nul blijven. Tijdens de werking van de functie wordt geheugen toegewezen voor de structuur van het nieuwe bericht en wordt de functie aangeroepen mblk_init(), waarmee alle velden van het gemaakte exemplaar van de structuur worden gereset en vervolgens met behulp van het hierboven genoemde datab_alloc(), zal een gegevensbuffer creëren. Hierna worden de velden in de structuur geconfigureerd:

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

Aan de uitgang ontvangen we een nieuw bericht met geïnitialiseerde velden en een lege gegevensbuffer. Om gegevens aan een bericht toe te voegen, moet u het naar de datablokbuffer kopiëren:

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

waar gegevens is een verwijzing naar de gegevensbron, en grootte - hun grootte.
dan moet je de pointer naar het schrijfpunt bijwerken, zodat deze weer naar het begin van het vrije gebied in de buffer wijst:

msg->b_wptr = msg->b_wptr + size

Als u een bericht wilt maken vanuit een bestaande buffer, zonder te kopiëren, gebruik dan de functie:

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

De functie zal, na het creëren van het bericht en de structuur van het datablok, de verwijzingen naar de gegevens op het adres configureren buf. Die. in dit geval bevindt de databuffer zich niet achter de headervelden van het datablok, zoals het geval was bij het maken van een datablok met de functie datab_alloc(). De buffer met gegevens die naar de functie worden doorgegeven, blijft waar hij was, maar wordt met behulp van pointers gekoppeld aan de nieuw gemaakte header van het datablok, en dienovereenkomstig aan het bericht.

Op één bericht mblk_t Verschillende datablokken kunnen opeenvolgend worden samengevoegd. Dit wordt gedaan door de functie:

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

mp — een bericht waaraan een ander datablok zal worden toegevoegd;
gegevens — verwijzing naar het blok, waarvan een kopie aan het bericht wordt toegevoegd;
grootte — gegevensgrootte;
stootkussen — een vlag die aangeeft dat de grootte van het toegewezen geheugen moet worden uitgelijnd langs een grens van 4 bytes (opvulling gebeurt met nullen).

Als er voldoende ruimte is in de bestaande berichtgegevensbuffer, worden de nieuwe gegevens achter de reeds aanwezige gegevens geplakt. Als er minder vrije ruimte is in de berichtgegevensbuffer dan grootte, vervolgens wordt er een nieuw bericht gemaakt met voldoende buffergrootte en worden de gegevens naar de buffer gekopieerd. Dit is een nieuw bericht, gekoppeld aan het originele bericht met behulp van een aanwijzer b_cont. In dit geval verandert het bericht in een tuple.

Als u nog een gegevensblok aan het tuple wilt toevoegen, moet u de functie gebruiken:

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

zij zal het laatste bericht in de tuple vinden (hij heeft b_cont zal null zijn) en zal de functie voor dit bericht aanroepen appendb().

U kunt de gegevensgrootte in een bericht of tupel achterhalen met behulp van de functie:

int msgdsize(const mblk_t *mp);

het doorloopt alle berichten in de tupel en retourneert de totale hoeveelheid gegevens in de gegevensbuffers van die berichten. Voor elk bericht wordt de hoeveelheid gegevens als volgt berekend:

 mp->b_wptr - mp->b_rptr

Om twee tupels te combineren, gebruik je de functie:

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

ze voegt de tupel toe NEWM naar de staart van de tuple mp en retourneert een verwijzing naar het laatste bericht van de resulterende tuple.

Indien nodig kan een tupel worden omgezet in één bericht met een enkel gegevensblok; dit wordt gedaan door de functie:

void msgpullup(mblk_t *mp,int len);

als argument len -1 is, wordt de grootte van de toegewezen buffer automatisch bepaald. Als len een positief getal is, wordt er een buffer van deze omvang gemaakt en worden de tupelberichtgegevens daarin gekopieerd. Als de buffer leeg is, stopt het kopiëren daar. Het eerste bericht van de tuple ontvangt een buffer van nieuwe grootte met de gekopieerde gegevens. De resterende berichten worden verwijderd en het geheugen wordt teruggezet naar de heap.

Bij het verwijderen van een structuur mblk_t er wordt rekening gehouden met de referentietelling van het datablok als u belt gratis() het blijkt nul te zijn, waarna de gegevensbuffer samen met de instantie wordt verwijderd mblk_t, die ernaar verwijst.

De velden van een nieuw bericht initialiseren:

void mblk_init(mblk_t *mp);

Nog een stukje gegevens aan het bericht toevoegen:

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

Als de nieuwe gegevens niet in de vrije ruimte van de berichtgegevensbuffer passen, wordt een afzonderlijk aangemaakt bericht met een buffer van de vereiste grootte aan het bericht toegevoegd (een verwijzing naar het toegevoegde bericht wordt in het eerste bericht ingesteld) en de bericht verandert in een tupel.

Een stukje gegevens toevoegen aan een tuple:

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

De functie roept appendb() in een lus aan.

Twee tupels combineren tot één:

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

bericht NEWM aan zal worden gehecht mp.

Een kopie maken van één bericht:

mblk_t *copyb(const mblk_t *mp);

Volledig kopiëren van een tupel met alle datablokken:

mblk_t *copymsg(const mblk_t *mp);

De elementen van het tupel worden door de functie gekopieerd kopieer().

Maak eenvoudig een kopie van een bericht mblk_t. In dit geval wordt het datablok niet gekopieerd, maar wordt de referentieteller verhoogd db_ref:

mblk_t *dupb(mblk_t *mp);

Een lichtgewicht kopie maken van een tuple. Datablokken worden niet gekopieerd, alleen hun referentietellers worden verhoogd db_ref:

mblk_t *dupmsg(mblk_t* m);

Alle berichten van een tupel in één bericht lijmen:

void msgpullup(mblk_t *mp,size_t len);

Als de argumentatie len -1 is, wordt de grootte van de toegewezen buffer automatisch bepaald.

Een bericht verwijderen, tuple:

void freemsg(mblk_t *mp);

De referentietelling van het datablok wordt met één verlaagd. Als deze nul bereikt, wordt het datablok ook verwijderd.

Berekening van de totale hoeveelheid gegevens in een bericht of tupel.

size_t msgdsize(const mblk_t *mp);

Een bericht ophalen uit de staart van de wachtrij:

mblk_t *ms_queue_peek_last (q);

Het kopiëren van de inhoud van de gereserveerde velden van het ene bericht naar een ander bericht (in feite bevatten deze velden vlaggen die door de mediastreamer worden gebruikt):

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

Wachtrij wachtrij_t

De berichtenwachtrij in de mediastreamer is geïmplementeerd als een cirkelvormige, dubbel gekoppelde lijst. Elk lijstelement bevat een verwijzing naar een datablok met signaalmonsters. Het blijkt dat alleen verwijzingen naar het datablok beurtelings bewegen, terwijl de data zelf bewegingloos blijven. Die. alleen links ernaartoe worden verplaatst.
Structuur die de wachtrij beschrijft wachtrij_t, hieronder weergegeven:

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

De structuur bevat een veld - een aanwijzer _q_stopper typ *mblk_t, het verwijst naar het eerste element (bericht) in de wachtrij. Het tweede veld van de structuur is de teller van berichten in de wachtrij.
De onderstaande afbeelding toont een wachtrij met de naam q1 met 4 berichten m1, m2, m3, m4.
De Mediastreamer2 VoIP-engine verkennen. Deel 11
De volgende afbeelding toont een wachtrij met de naam q1 die 4 berichten m1,m2,m3,m4 bevat. Bericht m2 is de kop van een tupel die nog twee berichten m2_1 en m2_2 bevat.

De Mediastreamer2 VoIP-engine verkennen. Deel 11

Functies voor het werken met wachtrijen wachtrij_t

Initialisatie van wachtrij:

void qinit(queue_t *q);

Veld _q_stopper (hierna zullen we het “stopper” noemen) wordt geïnitialiseerd door de functie mblk_init(), worden het vorige element en de aanwijzer van het volgende element zo aangepast dat ze naar zichzelf wijzen. De wachtrij-elementteller wordt op nul teruggezet.

Een nieuw element toevoegen (berichten):

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

Nieuw element m wordt toegevoegd aan het einde van de lijst, worden de elementaanwijzers aangepast zodat de stopper het volgende element ervoor wordt, en het wordt het vorige element voor de stopper. De wachtrij-elementteller wordt verhoogd.

Een element uit de wachtrij ophalen:

mblk_t * getq(queue_t *q); 

Het bericht dat na de stop komt, wordt opgehaald en de elemententeller wordt verlaagd. Als er behalve de stopper geen elementen in de wachtrij staan, wordt 0 geretourneerd.

Een bericht in een wachtrij invoegen:

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

Элемент mp vóór het element ingevoegd emp. als emp=0, waarna het bericht aan de staart van de wachtrij wordt toegevoegd.

Een bericht ophalen van de kop van de wachtrij:

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

De elemententeller wordt verlaagd.

Een verwijzing naar het eerste element in de wachtrij lezen:

mblk_t * peekq(queue_t *q); 

Alle elementen uit de wachtrij verwijderen terwijl u de elementen zelf verwijdert:

void flushq(queue_t *q, int how);

argument hoe niet gebruikt. De wachtrij-elementteller wordt op nul gezet.

Macro voor het lezen van een verwijzing naar het laatste element van de wachtrij:

mblk_t * qlast(queue_t *q);

Houd er bij het werken met berichtenwachtrijen rekening mee dat wanneer u belt ms_queue_put(q, m) met een nulwijzer naar het bericht loopt de functie door. Je programma loopt vast. gedraagt ​​zich hetzelfde ms_queue_next(q, m).

Filters aansluiten

De hierboven beschreven wachtrij wordt gebruikt om berichten van het ene filter naar het andere of van het ene naar meerdere filters door te geven. Filters en hun verbindingen vormen een gerichte grafiek. De in- of uitgang van het filter wordt het algemene woord “pin” genoemd. Om de volgorde te beschrijven waarin filters met elkaar zijn verbonden, gebruikt de mediastreamer het concept van een ‘signaalpunt’. Signaalpunt is structuur _MSCPoint, die een verwijzing naar het filter en het nummer van een van de pinnen bevat; dienovereenkomstig beschrijft het de aansluiting van een van de in- of uitgangen van het filter.

Signaalpunt van gegevensverwerkingsgrafiek

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

Filterpinnen zijn genummerd vanaf nul.

De verbinding van twee pinnen door een berichtenwachtrij wordt beschreven door de structuur _MSQueue, die een berichtenwachtrij bevat en verwijzingen naar de twee signaalpunten die het verbindt:

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

We zullen deze structuur een signaalverbinding noemen. Elk mediastreamerfilter bevat een tabel met invoerlinks en een tabel met uitvoerlinks (MSQueue). De grootte van tabellen wordt ingesteld bij het maken van een filter; dit hebben we al gedaan met behulp van een geëxporteerde variabele van het type MSFilterBesch, toen we ons eigen filter ontwikkelden. Hieronder ziet u een structuur die elk filter in een mediastreamer beschrijft: 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;

Nadat we de filters in het C-programma volgens ons plan hadden aangesloten (maar de ticker niet hadden aangesloten), creëerden we daardoor een gerichte grafiek, waarvan de knooppunten instanties van de structuur zijn MSFilter, en randen zijn voorbeelden van koppelingen MSQueue.

Achter de schermen activiteiten van de ticker

Toen ik je vertelde dat de ticker een filter is voor de bron van teken, was dat niet de hele waarheid. Een ticker is een object dat functies op de klok uitvoert Verwerken() alle filters van het circuit (grafiek) waarmee het is verbonden. Wanneer we een ticker verbinden met een grafiekfilter in een C-programma, tonen we de ticker de grafiek die hij vanaf nu zal besturen totdat we hem uitschakelen. Na verbinding te hebben gemaakt, begint de ticker de grafiek te onderzoeken die aan zijn zorg is toevertrouwd, en stelt hij een lijst met filters samen die deze bevatten. Om hetzelfde filter niet twee keer te “tellen”, worden de gedetecteerde filters gemarkeerd door er een selectievakje in te plaatsen gezien. De zoekopdracht wordt uitgevoerd met behulp van de koppelingstabellen die elk filter heeft.

Tijdens de inleidende rondleiding door de grafiek controleert de ticker of er onder de filters minstens één is die als bron van gegevensblokken fungeert. Als er geen zijn, wordt de grafiek als onjuist beschouwd en crasht de ticker.

Als de grafiek “correct” blijkt te zijn, wordt voor elk gevonden filter de functie aangeroepen voor initialisatie voorbewerken(). Zodra het moment aanbreekt voor de volgende verwerkingscyclus (standaard elke 10 milliseconden), roept de ticker de functie op Verwerken() voor alle eerder gevonden bronfilters en vervolgens voor de overige filters in de lijst. Als het filter invoerkoppelingen heeft, wordt de functie uitgevoerd Verwerken() wordt herhaald totdat de invoerlinkwachtrijen leeg zijn. Hierna gaat het door naar het volgende filter in de lijst en “scrollt” het totdat de invoerlinks geen berichten meer bevatten. De ticker beweegt van filter naar filter totdat de lijst eindigt. Hiermee is de verwerking van de cyclus voltooid.

Nu keren we terug naar tupels en praten we over waarom zo'n entiteit aan de mediastreamer is toegevoegd. Over het algemeen valt de hoeveelheid gegevens die nodig zijn voor het algoritme dat binnen het filter werkt niet samen en is deze geen veelvoud van de grootte van de gegevensbuffers die aan de ingang worden ontvangen. We schrijven bijvoorbeeld een filter dat een snelle Fourier-transformatie uitvoert, die per definitie alleen datablokken kan verwerken waarvan de grootte een macht van twee is. Laat het 512 tellen zijn. Als de gegevens door een telefoonkanaal worden gegenereerd, levert de gegevensbuffer van elk bericht aan de ingang ons 160 signaalmonsters op. Het is verleidelijk om pas data uit de input te verzamelen als de benodigde hoeveelheid data aanwezig is. Maar in dit geval zal er een botsing plaatsvinden met de ticker, die tevergeefs zal proberen door het filter te scrollen totdat de invoerlink leeg is. Eerder hebben we deze regel aangemerkt als het derde principe van het filter. Volgens dit principe moet de functie process() van het filter alle gegevens uit de invoerwachtrijen halen.

Bovendien zal het niet mogelijk zijn om slechts 512 monsters uit de invoer te nemen, omdat u alleen hele blokken kunt nemen, d.w.z. het filter zal 640 monsters moeten nemen en er 512 moeten gebruiken, de rest voordat een nieuw deel van de gegevens wordt verzameld. Ons filter moet dus, naast zijn hoofdwerk, aanvullende acties bieden voor de tussentijdse opslag van invoergegevens. De ontwikkelaars van de mediastreamer en de oplossing voor dit algemene probleem hebben een speciaal object ontwikkeld: MSBufferizer (bufferer), dat dit probleem oplost met behulp van tupels.

Bufferizer (MSBufferizer)

Dit is een object dat invoergegevens in het filter verzamelt en deze ter verwerking indient zodra de hoeveelheid informatie voldoende is om het filteralgoritme uit te voeren. Terwijl de buffer gegevens verzamelt, werkt het filter in de inactieve modus, zonder de verwerkingskracht van de processor te gebruiken. Maar zodra de leesfunctie van de bufferer een andere waarde dan nul retourneert, begint de functie process() van het filter gegevens van de bufferer op te nemen en te verwerken in gedeelten van de vereiste grootte, totdat deze uitgeput zijn.
Gegevens die nog niet nodig zijn, blijven in de buffer als het eerste element van de tupel, waaraan daaropvolgende blokken invoergegevens worden gekoppeld.

De structuur die de buffer beschrijft:

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

Functies voor het werken met MSBufferizer

Een nieuwe bufferinstantie maken:

MSBufferizer * ms_bufferizer_new(void);

Geheugen wordt toegewezen en geïnitialiseerd ms_bufferizer_init() en er wordt een pointer geretourneerd.

Initialisatiefunctie:

void ms_bufferizer_init(MSBufferizer *obj); 

De wachtrij wordt geïnitialiseerd q, veld grootte wordt op nul gezet.

Een bericht toevoegen:

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

Bericht m wordt aan de wachtrij toegevoegd. De berekende grootte van datablokken wordt opgeteld grootte.

Het overbrengen van alle berichten van de linkgegevenswachtrij naar de buffer q:

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

Berichten overbrengen vanaf een link q in de buffer wordt uitgevoerd met behulp van de functie ms_bufferizer_put().

Lezen uit de buffer:

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

Als de grootte van de in de buffer verzamelde gegevens kleiner is dan de gevraagde gegevens (datalen), retourneert de functie nul, gegevens worden niet naar gegevens gekopieerd. Anders wordt het sequentieel kopiëren van gegevens uit tupels die zich in de buffer bevinden, uitgevoerd. Na het kopiëren wordt de tupel verwijderd en wordt het geheugen vrijgegeven. Het kopiëren eindigt op het moment dat datalenbytes worden gekopieerd. Als er in het midden van een datablok geen ruimte meer is, wordt het datablok in dit bericht teruggebracht tot het resterende, niet-gekopieerde deel. De volgende keer dat u belt, wordt het kopiëren vanaf dit punt voortgezet.

De hoeveelheid gegevens lezen die momenteel beschikbaar is in de buffer:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Geeft het veld terug grootte bufferer.

Een deel van de gegevens in de buffer weggooien:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Het opgegeven aantal bytes aan gegevens wordt opgehaald en verwijderd. De oudste gegevens worden verwijderd.

Alle berichten in de buffer verwijderen:

void ms_bufferizer_flush(MSBufferizer *obj); 

De datateller wordt op nul gezet.

Alle berichten in de buffer verwijderen:

void ms_bufferizer_uninit(MSBufferizer *obj); 

De teller wordt niet gereset.

De buffer verwijderen en geheugen vrijmaken:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Voorbeelden van het gebruik van de bufferer zijn te vinden in de broncode van verschillende mediastreamerfilters. Bijvoorbeeld in het MS_L16_ENC-filter, dat de bytes in de samples herschikt van de netwerkvolgorde naar de hostvolgorde: l16.c

In het volgende artikel zullen we kijken naar de kwestie van de schatting van de tickerbelasting en hoe om te gaan met overmatige rekenbelasting in de mediastreamer.

Bron: www.habr.com

Voeg een reactie