Der Inhalt dieses Artikels stammt von meinem .

Datenverschiebungsmechanismus
- Datenblock dblk_t
- Nachricht mblk_t
- Funktionen zur Verarbeitung von Nachrichten mblk_t
- Warteschlange queue_t
- Funktionen zur Verarbeitung von Warteschlangen queue_t
- Filterverbindung
- Signalpunkt des Datenverarbeitungsgrafen
- HintergrundaktivitÀten des Tickers
- Puffer (MSBufferizer)
- Funktionen zur Verarbeitung mit MSBufferizer
Im letzten Wir haben unseren eigenen Filter entwickelt. In diesem Artikel widmen wir uns dem internen Mechanismus der Datenverschiebung zwischen den Medienstreamern. Dies wird es in Zukunft ermöglichen, raffinierte Filter mit geringerem Aufwand zu schreiben.
Datenverschiebungsmechanismus
Die Datenverschiebung im Medienstreamer erfolgt ĂŒber Warteschlangen, die durch die Struktur queue_tbeschrieben werden. Ăber die Warteschlangen werden Sequenzen von Nachrichten des Typs mblk_tverschoben, die selbst keine Signaldaten enthalten, sondern lediglich Verweise auf die vorherige, nĂ€chste Nachricht und auf den Datenblock. DarĂŒber hinaus möchte ich besonders betonen, dass es auch ein Feld fĂŒr einen Verweis auf eine Nachricht desselben Typs gibt, das es ermöglicht, eine verkettete Liste von Nachrichten zu organisieren. Die Gruppe von Nachrichten, die durch eine solche Liste verbunden sind, nennen wir ein Tuple. Somit kann jedes Element der Warteschlange eine einzelne Nachricht sein. mblk_t, und vielleicht auch als Nachrichten-Tuple. mblk_t. Jedes Nachrichtentuple kann sein eigenes Datenblock haben. Warum Tuples benötigt werden, werden wir etwas spĂ€ter besprechen.
Wie bereits erwĂ€hnt, enthĂ€lt die Nachricht selbst keinen Datenblock; sie enthĂ€lt nur einen Verweis auf den Speicherbereich, in dem der Block gespeichert ist. In diesem Teil Ă€hnelt das Gesamtbild der Funktionsweise des Media Streamers einem Lagerhaus voller TĂŒren aus dem Cartoon "Die Monster AG", in dem die TĂŒren (Datenverlinkungen â RĂ€ume) mit einer verrĂŒckten Geschwindigkeit ĂŒber FörderbĂ€nder bewegt werden, wĂ€hrend die RĂ€ume selbst unbeweglich bleiben.
Nun gehen wir die Hierarchie von unten nach oben durch und betrachten die genannten EntitĂ€ten des DatenĂŒbertragungsmechanismus im Media Streamer im Detail.
Datenblock dblk_t
Ein Datenblock besteht aus einem Header und einem Datenpuffer. Der Header wird durch die folgende Struktur beschrieben,
typedef struct datab
{
unsigned char *db_base; // Zeiger auf den Beginn des Datenpuffers.
unsigned char *db_lim; // Zeiger auf das Ende des Datenpuffers.
void (*db_freefn)(void*); // Funktion zur Freigabe des Speichers bei Löschung des Blocks.
int db_ref; // ZĂ€hler fĂŒr Verweise.
} dblk_t;Die Felder der Struktur enthalten Verweise auf den Anfang des Puffers, das Ende des Puffers und die Funktion zum Löschen des Datenpuffers. Das letzte Element im Header db_ref â ein ReferenzzĂ€hler, der, wenn er null erreicht, ein Signal zum Löschen dieses Blocks aus dem Speicher gibt. Wenn der Datenblock durch die Funktion datab_alloc() erstellt wurde, wird der Datenpuffer direkt hinter dem Header im Speicher platziert. In allen anderen FĂ€llen kann der Puffer an einem anderen Ort liegen. Im Datenpuffer werden die Signalwerte oder andere Daten gespeichert, die wir durch Filter bearbeiten möchten.
Ein neuer Datenblock wird durch die Funktion erstellt:
dblk_t *datab_alloc(int size);Der Eingabeparameter ist die GröĂe der Daten, die der Block speichern wird. Es wird zusĂ€tzlicher Speicher zugewiesen, um am Anfang des zugewiesenen Speichers den Header â die Struktur databâ unterzubringen. Aber bei der Verwendung anderer Funktionen kommt das nicht immer vor; in einigen FĂ€llen kann der Datenpuffer vom Header des Datenblocks getrennt sein. Die Felder der Struktur werden beim Erstellen so konfiguriert, dass das Feld db_base auf den Anfang des Datenbereichs verweist, und db_lim auf dessen Ende. Der ReferenzzĂ€hler db_ref wird in die Einheit gesetzt. Der Zeiger fĂŒr die Datenbereinigung wird auf Null gesetzt.
Nachricht mblk_t
Wie bereits erwÀhnt, haben die Warteschlangenelemente den Typ mblk_t, er ist wie folgt definiert:
typedef struct msgb
{
struct msgb *b_prev; // Zeiger auf das vorherige Listenelement.
struct msgb *b_next; // Zeiger auf das nÀchste Listenelement.
struct msgb *b_cont; // Zeiger zum AnhÀngen anderer Nachrichten an die Nachricht, um ein Nachrichten-Tuple zu erstellen.
struct datab *b_datap; // Zeiger auf die Datenblockstruktur.
unsigned char *b_rptr; // Zeiger auf den Beginn des Datenbereichs zur Lesung von Daten aus dem Puffer b_datap.
unsigned char *b_wptr; // Zeiger auf den Beginn des Datenbereichs zur Schreibung von Daten in den Puffer b_datap.
uint32_t reserved1; // Reserviertes Feld 1, der Medienstremer platziert dort Verwaltungsinformationen.
uint32_t reserved2; // Reserviertes Feld 2, der Medienstremer platziert dort Verwaltungsinformationen.
#if defined(ORTP_TIMESTAMP)
struct timeval timestamp;
#endif
ortp_recv_addr_t recv_addr;
} mblk_t;Struktur mblk_t enthÀlt am Anfang die Zeiger b_prev, b_next, die erforderlich sind, um eine doppelt verkettete Liste (die Warteschlange ist) zu organisieren. queue_t).
Dann folgt der Zeiger b_cont, der nur verwendet wird, wenn die Nachricht in das Tuple eintritt. FĂŒr die letzte Nachricht im Tuple bleibt dieser Zeiger nullă
Als nÀchstes sehen wir einen Zeiger auf einen Datenblock b_datap, der das zugrunde liegende Nachrichtenobjekt reprÀsentiert. Danach folgen Zeiger auf den Bereich innerhalb des Datenpuffers des Blocks. Das Feld b_rptr weist auf die Stelle hin, von der aus Daten aus dem Puffer gelesen werden. Das Feld b_wptr bezeichnet den Ort, an dem Daten in den Puffer geschrieben werden.
Die verbleibenden Felder sind administrativen Charakters und betreffen nicht den Betrieb des DatenĂŒbertragungsmechanismus.
Unten ist eine einzelne Nachricht mit dem Namen m1 und einem Datenblock dargestellt d1.

Im nÀchsten Bild sehen wir ein Tuple aus drei Nachrichten m1, m1_1, m1_2.

Die Funktionen zur Verarbeitung von Nachrichten mblk_t
Eine neue Nachricht mblk_t wird erstellt durch die Funktion:
mblk_t *allocb(int size, int pri); dies reserviert im Speicher eine neue Nachricht mblk_t mit einem Datenblock der angegebenen GröĂe GröĂe, das zweite Argument â pri wird in dieser Version der Bibliothek nicht verwendet. Es sollte null bleiben. WĂ€hrend der AusfĂŒhrung der Funktion wird Speicher fĂŒr die Struktur der neuen Nachricht reserviert und die Funktion mblk_init(), die alle Felder des erstellten Strukturbeispiels zurĂŒcksetzt und dann mit Hilfe des oben genannten datab_alloc(), einen Datenpuffer erstellt. Danach werden die Felder in der Struktur konfiguriert:
mp->b_datap=datab;
mp->b_rptr=mp->b_wptr=datab->db_base;
mp->b_next=mp->b_prev=mp->b_cont=NULL;Am Ende erhalten wir eine neue Nachricht mit initialisierten Feldern und einem leeren Datenpuffer. Um Daten zur Nachricht hinzuzufĂŒgen, mĂŒssen diese in den Datenblockpuffer kopiert werden:
memcpy(msg->b_rptr, data, size);wobei data â der Zeiger auf die Datenquelle, und GröĂe â deren GröĂe.
danach muss der Schreibzeiger aktualisiert werden, sodass er wieder auf den Anfang des freien Bereichs im Puffer zeigt:
msg->b_wptr = msg->b_wptr + sizeSoll eine Nachricht aus einem bereits vorhandenen Puffer erstellt werden, ohne Kopieren, wird dafĂŒr die Funktion verwendet:
mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); Die Funktion wird nach der Erstellung der Nachricht und der Struktur des Datenblocks die Zeiger auf die Daten an der Adresse buf. Das heiĂt, in diesem Fall befindet sich der Datenpuffer nicht hinter den Feldern des Datenblock-Headers, wie es bei der Erstellung des Datenblocks durch die Funktion war. datab_alloc(). Der an die Funktion ĂŒbergebene Datenpuffer bleibt an seiner Stelle, wird jedoch mit Hilfe von Zeigern auf den neu erstellten Datenblockheader gelenkt, der wiederum auf die Nachricht zeigt.
Zu einer Nachricht mblk_t können mehrere Datenblöcke nacheinander angehÀngt werden. Dies geschieht durch die Funktion:
mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); mp â die Nachricht, zu der ein weiterer Datenblock hinzugefĂŒgt wird;
data â ein Zeiger auf den Block, dessen Kopie in die Nachricht eingefĂŒgt wird;
GröĂe â die GröĂe der Daten;
pad â ein Flag, das angibt, dass die GröĂe des reservierten Speichers auf 4 Bytes ausgerichtet werden soll (die AuffĂŒllung erfolgt mit Nullen).
Wenn im vorhandenen Datenpuffer der Nachricht ausreichend Platz vorhanden ist, werden die neuen Daten hinter den bereits dort befindlichen Daten eingefĂŒgt. Ist der freie Platz im Datenpuffer der Nachricht jedoch geringer als GröĂe, dann wird eine neue Nachricht mit ausreichender PufferspeichergröĂe erstellt und die Daten werden in ihren Puffer kopiert. Diese neue Nachricht wird ĂŒber einen Zeiger b_contan die ursprĂŒngliche angehĂ€ngt. In diesem Fall wird die Nachricht zu einem Tuple.
Wenn ein weiterer Datenblock zum Tuple hinzugefĂŒgt werden soll, muss die Funktion verwendet werden:
void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);es wird die letzte Nachricht im Tuple gefunden (die ist b_cont null) und dafĂŒr wird die Funktion appendb() aufgerufen..
Um die GröĂe der Daten in einer Nachricht oder Tuple zu ermitteln, kann die Funktion verwendet werden:
int msgdsize(const mblk_t *mp);Diese durchlĂ€uft alle Nachrichten des Tuples und gibt die Gesamtdatenmenge in den Datenpuffern dieser Nachrichten zurĂŒck. FĂŒr jede Nachricht wird die Datenmenge wie folgt berechnet:
mp->b_wptr - mp->b_rptrUm zwei Tuples zu verbinden, wird die Funktion verwendet:
mblk_t *concatb(mblk_t *mp, mblk_t *newm);Diese fĂŒgt das Tuple newm ans Ende des Tuples an mp und gibt einen Zeiger auf die letzte Nachricht des resultierenden Tuples zurĂŒck.
Falls erforderlich, kann das Tuple in eine einzige Nachricht mit einem einzigen Datenblock umgewandelt werden, dies geschieht durch die Funktion:
void msgpullup(mblk_t *mp,int len);wenn das Argument len gleich -1 ist, wird die GröĂe des reservierten Puffers automatisch bestimmt. Wenn len Wenn es sich um eine positive Zahl handelt, wird ein Puffer dieser GröĂe erstellt, in den die Daten der Tuple-Nachrichten kopiert werden. Wenn der Puffer voll ist, wird das Kopieren gestoppt. Die erste Nachricht des Tuples erhĂ€lt einen neuen Puffer mit den kopierten Daten. Die ĂŒbrigen Nachrichten werden gelöscht und der Speicher wird wieder in den Heap zurĂŒckgegeben.
Beim Löschen der Struktur mblk_t wird der ReferenzzĂ€hler des Datenblocks berĂŒcksichtigt. Wenn beim Aufruf von freeb() dieser gleich null ist, wird der Datenpuffer zusammen mit dem Exemplar gelöscht, mblk_tauf das verwiesen wird.
Initialisierung der Felder der neuen Nachricht:
void mblk_init(mblk_t *mp);HinzufĂŒgen eines weiteren Datenabschnitts zur Nachricht:
mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);Wenn die neuen Daten nicht in den freien Speicher des Datenpuffers der Nachricht passen, wird der Nachricht ein separat erstelltes Nachricht mit dem benötigten Puffer angehĂ€ngt (im ersten Nachricht wird ein Zeiger auf die hinzugefĂŒgte Nachricht gesetzt), die Nachricht wird zu einem Tuple.
HinzufĂŒgen eines Datenabschnitts zum Tuple:
void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); Die Funktion ruft appendb() in einer Schleife auf.
ZusammenfĂŒhren von zwei Tuples zu einem:
mblk_t *concatb(mblk_t *mp, mblk_t *newm);Nachricht newm wird angehÀngt an mp.
Erstellen einer Kopie eines einzelnen Nachrichtenblocks:
mblk_t *copyb(const mblk_t *mp);VollstÀndige Kopie eines Tupels mit allen Datenblöcken:
mblk_t *copymsg(const mblk_t *mp);Die Elemente des Tupels werden durch die Funktion copyb().
Erstellen einer leichten Kopie der Nachricht mblk_t. Dabei wird der Datenblock nicht kopiert, sondern nur der ZÀhler seiner Verweise erhöht. db_ref:
mblk_t *dupb(mblk_t *mp);Erstellen einer leichten Kopie des Tupels. Die Datenblöcke werden nicht kopiert, nur ihre ZĂ€hler fĂŒr Verweise erhöht. db_ref:
mblk_t *dupmsg(mblk_t* m);ZusammenfĂŒgen aller Nachrichten des Tupels zu einer einzigen Nachricht:
void msgpullup(mblk_t *mp, size_t len);Wenn das Argument len gleich -1 ist, wird die GröĂe des reservierten Puffers automatisch bestimmt.
Löschen einer Nachricht oder eines Tupels:
void freemsg(mblk_t *mp);Der ZÀhler der Verweise des Datenblocks wird um eins verringert. Erreicht dieser dabei null, wird der Datenblock ebenfalls gelöscht.
Berechnung der GesamtgröĂe der Daten in einer Nachricht oder einem Tupel.
size_t msgdsize(const mblk_t *mp);Abrufen einer Nachricht aus dem Ende der Warteschlange:
mblk_t *ms_queue_peek_last (q);Kopieren des Inhalts der reservierten Felder einer Nachricht in eine andere Nachricht (tatsÀchlich befinden sich in diesen Feldern Flags, die vom Mediastreamer verwendet werden):
mblk_meta_copy(const mblk_t *source, mblk *dest);Queue queue_t
Die Nachrichtenwarteschlange im Media-Streamer ist als zirkulÀre doppelt verkettete Liste implementiert. Jeder Listeneintrag enthÀlt einen Zeiger auf einen Datenblock mit Signalwerten. Das bedeutet, dass nur die Zeiger auf die Datenblöcke bewegt werden, wÀhrend die tatsÀchlichen Daten unverÀndert bleiben. Mit anderen Worten, es werden nur die Referenzen auf die Daten verschoben.
Die Struktur, die die Warteschlange beschreibt queue_t, ist unten dargestellt:
typedef struct _queue
{
mblk_t _q_stopper; /* "Leeres" Element der Warteschlange, zeigt nicht auf Daten, wird nur zur Verwaltung der Warteschlange verwendet. Bei der Initialisierung der Warteschlange (qinit()) werden seine Zeiger so eingestellt, dass sie auf sich selbst zeigen. */
int q_mcount; // Anzahl der Elemente in der Warteschlange.
} queue_t;Die Struktur enthĂ€lt ein Feld â einen Zeiger _q_stopper vom Typ *mblk_t, der auf das erste Element (Nachricht) in der Warteschlange zeigt. Das zweite Feld der Struktur ist ein ZĂ€hler fĂŒr die in der Warteschlange befindlichen Nachrichten.
Im folgenden Bild ist die Warteschlange mit dem Namen q1 zu sehen, die 4 Nachrichten m1, m2, m3, m4 enthÀlt.

Im nĂ€chsten Bild ist die Warteschlange mit dem Namen q1 zu sehen, die 4 Nachrichten m1, m2, m3, m4 enthĂ€lt. Die Nachricht m2 ist der Kopf eines Tupels, zu dem noch zwei Nachrichten m2_1 und m2_2 fĂŒhren.

Funktionen zur Verarbeitung von Warteschlangen queue_t
Initialisierung der Warteschlange:
void qinit(queue_t *q);Feld _q_stopper (weiterhin als "Stopp" bezeichnet) wird durch eine Funktion initialisiert. mblk_init(), sein Zeiger auf das vorherige und das nĂ€chste Element wird so eingestellt, dass sie auf ihn selbst zeigen. Der ElementzĂ€hler der Warteschlange wird auf null zurĂŒckgesetzt.
HinzufĂŒgen eines neuen Elements (Nachricht):
void putq(queue_t *q, mblk_t *m);Das neue Element m wird am Ende der Liste hinzugefĂŒgt, die Zeiger des Elements werden so eingestellt, dass der Stopp das nĂ€chste Element fĂŒr es wird, und es wird fĂŒr den Stopp das vorherige. Der ZĂ€hler der Elemente der Warteschlange wird inkrementiert.
Entziehen eines Elements aus der Warteschlange:
mblk_t * getq(queue_t *q); es wird die Nachricht abgeholt, die nach dem Stopp steht, der ElementzĂ€hler wird dekrementiert. Wenn es in der Warteschlange auĂer dem Stopp keine Elemente mehr gibt, wird 0 zurĂŒckgegeben.
EinfĂŒgen einer Nachricht in die Warteschlange:
void insq(queue_t *q, mblk_t *emp, mblk_t *mp); Das Element mp es wird vor dem Element empuseString emp=0, dann wird die Nachricht am Ende der Warteschlange hinzugefĂŒgt.
Entziehen einer Nachricht vom Kopf der Warteschlange:
void remq(queue_t *q, mblk_t *mp); Der ElementzÀhler wird dekrementiert.
Lesen des Zeigers auf das erste Element in der Warteschlange:
mblk_t * peekq(queue_t *q); Entfernen aller Elemente aus der Warteschlange mit Entfernen der eigentlichen Elemente:
void flushq(queue_t *q, int how);Das Argument wie wird nicht verwendet. Der ZĂ€hler fĂŒr die Elemente in der Warteschlange wird auf null gesetzt.
Makro zum Lesen des Zeigers auf das letzte Element der Warteschlange:
mblk_t * qlast(queue_t *q);Beim Arbeiten mit Nachrichtenwarteschlangen sollte beachtet werden, dass bei einem Aufruf von ms_queue_put(q, m) mit einem null-Zeiger auf die Nachricht, die Funktion in eine Endlosschleife gerÀt. Ihr Programm wird hÀngen bleiben. Dasselbe Verhalten zeigt sich bei ms_queue_next(q, m).
Filterverbindung
Die oben beschriebene Warteschlange wird verwendet, um Nachrichten von einem Filter zu einem anderen oder von einem zu mehreren Filtern zu ĂŒbertragen. Filter und ihre Verbindungen bilden einen gerichteten Graph. Den Eingang oder Ausgang eines Filters nennen wir allgemein "Pin". Zur Beschreibung der Verbindungsreihenfolge zwischen den Filtern wird im Media-Streamer das Konzept des "Signalpunkts" verwendet. Ein Signalpunkt ist eine Struktur _MSCPoint, die einen Zeiger auf den Filter und die Nummer eines seiner Pins enthĂ€lt; damit beschreibt sie die Verbindung eines der EingĂ€nge oder AusgĂ€nge des Filters.
Signalpunkt des Datenverarbeitungsgrafen
typedef struct _MSCPoint{
struct _MSFilter *filter; // Zeiger auf den Media-Streamer-Filter.
int pin; // Nummer eines der EingÀnge oder AusgÀnge des Filters, d.h. Pin.
} MSCPoint;
Die Pins der Filter sind nummeriert, beginnend bei null.
Die Verbindung zweier Pins ĂŒber eine Nachrichtenwarteschlange wird durch die Struktur _MSQueue, die eine Nachrichtenwarteschlange und Zeiger auf zwei Signalenpunkte enthĂ€lt, die sie verbindet:
typedef struct _MSQueue
{
queue_t q;
MSCPoint prev;
MSCPoint next;
}MSQueue;
Wir nennen diese Struktur Signallink. Jeder Medienstream-Filter enthĂ€lt eine Tabelle der Eingangslinks und eine Tabelle der Ausgangslinks (MSQueue). Die GröĂe der Tabellen wird bei der Erstellung des Filters festgelegt, dies haben wir bereits mit der exportierten Variablen vom Typ MSFilterDesc, als wir unseren eigenen Filter entwickelt haben. Im Folgenden wird die Struktur gezeigt, die jeden Filter im Medienstream beschreibt, MSFilter:
struct _MSFilter{
MSFilterDesc *desc; /* Zeiger auf die Filterbeschreibung. */
/* GeschĂŒtzte Attribute, die dĂŒrfen nicht verschoben oder entfernt werden, andernfalls funktioniert die Interaktion mit Plugins nicht mehr. */
ms_mutex_t lock; /* Semaphore. */
MSQueue **inputs; /* Eingangslinktabelle. */
MSQueue **outputs; /* Ausgangslinktabelle. */
struct _MSFactory *factory; /* Zeiger auf die Fabrik, die diese Filterinstanz erstellt hat. */
void *padding; /* Wird nicht verwendet und wird aktiviert, wenn geschĂŒtzte Felder hinzugefĂŒgt werden. */
void *data; /* Zeiger auf eine beliebige Struktur zur Speicherung der internen Zustandsdaten des Filters und fĂŒr zwischengerechnete Berechnungen. */
struct _MSTicker *ticker; /* Zeiger auf das Ticker-Objekt, das nicht null sein darf, wenn die Funktion process() aufgerufen wird. */
/*private attributes, they can be moved and changed at any time*/
MSList *notify_callbacks; /* Liste von RĂŒckruffunktionen, die zur Verarbeitung von Filterereignissen verwendet werden. */
uint32_t last_tick; /* Nummer des letzten Taktes, in dem die Funktion process() aufgerufen wurde. */
MSFilterStats *stats; /* Statistiken zur Filterverarbeitung. */
int postponed_task; /* Anzahl der verschobenen Aufgaben. Einige Filter können die Datenverarbeitung (Aufruf von process()) um mehrere Takte verzögern. */
bool_t seen; /* Flag, das der Ticker verwendet, um zu kennzeichnen, dass diese Filterinstanz in diesem Takt bereits bedient wurde. */
};
typedef struct _MSFilter MSFilter;
Nachdem wir die Filter in unserem C-Programm gemÀà unserem Konzept verbunden haben (aber den Ticker nicht angeschlossen haben), haben wir damit einen gerichteten Graphen geschaffen, dessen Knoten Instanzen der Struktur sind. MSFilter, wÀhrend die Kanten Instanzen der Links sind. MSQueue.
HintergrundaktivitÀten des Tickers
Als ich Ihnen sagte, dass der Ticker ein Filter fĂŒr Taktsignale ist, war das nicht die ganze Wahrheit. Der Ticker ist ein Objekt, das Funktionen gemÀà einem Zeitgeber aktiviert. process() fĂŒr alle Filter des Schemas (Graphen), an das er angeschlossen ist. Wenn wir im C-Programm den Ticker mit einem Filter des Graphen verbinden, zeigen wir dem Ticker den Graphen, den er von nun an verwalten wird, bis wir ihn wieder trennen. Nach dem Anschluss beginnt der Ticker, den ihm anvertrauten Graphen zu durchforsten und eine Liste der Filter zu erstellen, in die er eintritt. Um nicht denselben Filter zweimal zu "zĂ€hlen", kennzeichnet er die entdeckten Filter, indem er ein HĂ€kchen setzt. seen. Die Suche erfolgt anhand der Linktabellen, die jeder Filter hat.
WĂ€hrend seiner EinfĂŒhrungsrunde prĂŒft der Ticker, ob unter den Filtern mindestens einer als Datenblockquelle fungiert. Wenn dies nicht der Fall ist, gilt der Graph als ungĂŒltig und der Ticker wird sofort beendet.
Wenn der Graph "gĂŒltig" ist, wird fĂŒr jeden gefundenen Filter zur Initialisierung die Funktion preprocess()aufgerufen. Sobald der Zeitpunkt fĂŒr den nĂ€chsten Verarbeitungstakt erreicht ist (standardmĂ€Ăig alle 10 Millisekunden), ruft der Ticker die Funktion process() fĂŒr alle zuvor gefundenen Quellfilter und anschlieĂend fĂŒr die restlichen Filter in der Liste auf. Wenn ein Filter Eingangslinks hat, wird die Funktion process() so lange wiederholt, bis die Warteschlangen der Eingangslinks leer sind. Danach geht er zum nĂ€chsten Filter in der Liste ĂŒber und "scrollt" ihn bis die Eingangslinks von Nachrichten befreit sind. Der Ticker wechselt von Filter zu Filter, bis die Liste abgearbeitet ist. Damit endet die Verarbeitung des Taktes.
Nun kehren wir zu den Tuples zurĂŒck und besprechen, aus welchem Grund eine solche EntitĂ€t im Medienstreamer hinzugefĂŒgt wurde. Generell stimmt das Volumen der Daten, das der Algorithmus innerhalb des Filters benötigt, nicht mit der GröĂe der Datenpuffer ĂŒberein, die am Eingang ankommen, und ist nicht unbedingt ein Vielfaches davon. Nehmen wir an, wir schreiben einen Filter, der eine schnelle Fourier-Transformation durchfĂŒhrt, die definitionsgemÀà nur Datenblöcke verarbeiten kann, deren GröĂe einer Potenz von zwei entspricht. Setzen wir dies auf 512 Abtastwerte. Wenn die Daten ĂŒber einen Telefonkanal generiert werden, wird der Datenpuffer jeder Nachricht bei Eingang 160 Abtastwerte des Signals liefern. Es besteht die Versuchung, die Daten am Eingang nicht abzurufen, bis die erforderliche Menge an Daten vorhanden ist. In diesem Fall wĂŒrde jedoch eine Kollision mit dem Ticker entstehen, der vergeblich versucht, den Filter zu leeren, bis der Eingangslink aufgebraucht ist. FrĂŒher haben wir diese Regel als das dritte Prinzip der Filterfunktion bezeichnet. GemÀà diesem Prinzip sollte die Funktion process() des Filters alle Daten aus den Eingangswarteschlangen abholen.
DarĂŒber hinaus können von der Eingangsseite nur 512 Messungen nicht abgerufen werden, da diese nur in ganzen Blöcken abgerufen werden können. Das bedeutet, dass der Filter 640 Messungen abholen muss und von diesen 512 verwendet werden, wĂ€hrend der Rest bis zur Ansammlung neuer Datenpools gespeichert wird. Unser Filter muss neben seiner Hauptaufgabe auch unterstĂŒtzende MaĂnahmen zur Zwischenlagerung der Eingangsdaten gewĂ€hrleisten. Die Entwickler des Media-Streamers haben fĂŒr diese gemeinsame Herausforderung ein spezielles Objekt entwickelt â den MSBufferizer (Puffer), der diese Aufgabe mithilfe von Tupeln löst.
Puffer (MSBufferizer)
Dieses Objekt wird die Eingangsdaten innerhalb des Filters sammeln und beginnt, sie zur Verarbeitung freizugeben, sobald die Informationsmenge ausreicht, um den Filteralgorithmus auszufĂŒhren. Solange der Puffer Daten speichert, arbeitet der Filter im Leerlaufmodus, ohne Rechenleistung des Prozessors zu verbrauchen. Doch sobald die Leseoperation vom Puffer ein Ergebnis ungleich null zurĂŒckgibt, beginnt die Funktion process() des Filters, Daten in passenden Portionen aus dem Puffer abzurufen und zu verarbeiten, bis diese erschöpft sind.
Nicht verwendete Daten bleiben im Puffer als erstes Element eines Tupels, an das die nachfolgenden Eingabedaten angehÀngt werden.
Die Struktur, die den Puffer beschreibt:
struct _MSBufferizer{
queue_t q; /* Nachrichtenwarteschlange. */
int size; /* GesamtgröĂe der derzeit im Puffer befindlichen Daten. */
};
typedef struct _MSBufferizer MSBufferizer;Funktionen zur Verarbeitung mit MSBufferizer
Erstellen eines neuen Pufferexemplars:
MSBufferizer * ms_bufferizer_new(void);Speicher wird reserviert, initiiert in ms_bufferizer_init() und gibt einen Zeiger zurĂŒck.
Initialisierungsfunktion:
void ms_bufferizer_init(MSBufferizer *obj); Die Warteschlange q, Feld GröĂe wird auf null gesetzt.
Nachricht hinzufĂŒgen:
void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); Die Nachricht m wird in die Warteschlange eingefĂŒgt. Die berechnete GröĂe der Datenblöcke wird zu GröĂe.
Ăbertragung aller Nachrichten aus der Datenwarteschlange in den Puffer q:
void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q); Nachrichtentransfer vom Link q in den Puffer erfolgt mit der Funktion ms_bufferizer_put().
Lesen aus dem Puffer:
int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); Wenn die GröĂe der im Puffer angesammelten Daten kleiner ist als angefordert (datalen), die Funktion gibt null zurĂŒck, die Daten werden nicht in den Datenbereich kopiert. Andernfalls erfolgt eine sequenzielle Kopie der Daten aus den im Puffer befindlichen Tupeln. Nach der Kopie wird das Tupel gelöscht und der Speicher freigegeben. Der Kopiervorgang endet, wenn datalen Bytes kopiert wurden. Wenn der Speicherplatz wĂ€hrend eines Datenblocks ausgeht, wird in dieser Nachricht der Datenblock auf den verbleibenden nicht kopierten Teil verkĂŒrzt. Beim nĂ€chsten Aufruf wird die Kopie an dieser Stelle fortgesetzt.
Lesen der Menge an Daten, die derzeit im Puffer verfĂŒgbar sind:
int ms_bufferizer_get_avail(MSBufferizer *obj); Gibt das Feld zurĂŒck GröĂe desPuffers zurĂŒck.
Ăbertragung eines Teils der im Puffer befindlichen Daten:
void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);Die angegebene Anzahl von Bytes wird extrahiert und verworfen. Die Àltesten Daten werden verworfen.
Löschen aller Nachrichten im Puffer:
void ms_bufferizer_flush(MSBufferizer *obj); Der DatenzĂ€hler wird auf null zurĂŒckgesetzt.
Löschen aller Nachrichten im Puffer:
void ms_bufferizer_uninit(MSBufferizer *obj); Der ZĂ€hler wird nicht zurĂŒckgesetzt.
Löschen des Puffers und Freigabe des Speichers:
void ms_bufferizer_destroy(MSBufferizer *obj); Beispiele fĂŒr die Verwendung des Puffers finden Sie im Quellcode mehrerer Media-Streamer-Filter. Zum Beispiel im Filter MS_L16_ENC, der die Byte-Reihenfolge von Netzwerk- in Hostreihenfolge umstellt:
Im nĂ€chsten Artikel werden wir die Lastbewertung auf dem Ticker und Möglichkeiten zur BekĂ€mpfung einer ĂŒbermĂ€Ăigen Rechenlast im Mediastreamer behandeln.
Quelle: habr.com
