{"id":79540,"date":"2020-04-28T07:42:01","date_gmt":"2020-04-28T05:42:01","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11"},"modified":"2020-04-28T07:42:01","modified_gmt":"2020-04-28T05:42:01","slug":"izuchaem-voip-dvizhok-mediastreamer2-chast-11","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","title":{"rendered":"Wir erforschen die VoIP-Engine Mediastreamer2, Teil 11","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Der Inhalt dieses Artikels stammt von meinem <noindex><a rel=\"nofollow\" href=\"https:\/\/zen.yandex.ru\/profile\/editor\/id\/5e3f8e0751f5346faab4fc7a\">Zen-Kanal<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wir erforschen die VoIP-Engine Mediastreamer2, Teil 11\" src=\"\/wp-content\/uploads\/2020\/04\/b66fab43eb45c626c50f0383dcfa7cca.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Datenverschiebungsmechanismus<\/p>\n<p><\/p>\n<ul>\n<li>Datenblock dblk_t<\/li>\n<li>Nachricht mblk_t<\/li>\n<li>Funktionen zur Verarbeitung von Nachrichten mblk_t<\/li>\n<li>Warteschlange queue_t<\/li>\n<li>Funktionen zur Verarbeitung von Warteschlangen queue_t<\/li>\n<li>Filterverbindung<\/li>\n<li>Signalpunkt des Datenverarbeitungsgrafen<\/li>\n<li>Hintergrundaktivit\u00e4ten des Tickers<\/li>\n<li>Puffer (MSBufferizer)<\/li>\n<li>Funktionen zur Verarbeitung mit MSBufferizer<\/li>\n<\/ul>\n<p><\/p>\n<p>Im letzten <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/497932\/\">Artikel<\/a><\/noindex> 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\u00f6glichen, raffinierte Filter mit geringerem Aufwand zu schreiben.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2 id=\"mehanizm-peremescheniya-dannyh\">Datenverschiebungsmechanismus<\/h2>\n<p><\/p>\n<p>Die Datenverschiebung im Medienstreamer erfolgt \u00fcber Warteschlangen, die durch die Struktur <em>queue_t<\/em>beschrieben werden. \u00dcber die Warteschlangen werden Sequenzen von Nachrichten des Typs <em>mblk_t<\/em>verschoben, die selbst keine Signaldaten enthalten, sondern lediglich Verweise auf die vorherige, n\u00e4chste Nachricht und auf den Datenblock. Dar\u00fcber hinaus m\u00f6chte ich besonders betonen, dass es auch ein Feld f\u00fcr einen Verweis auf eine Nachricht desselben Typs gibt, das es erm\u00f6glicht, 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. <em>mblk_t<\/em>, und vielleicht auch als Nachrichten-Tuple. <em>mblk_t<\/em>. Jedes Nachrichtentuple kann sein eigenes Datenblock haben. Warum Tuples ben\u00f6tigt werden, werden wir etwas sp\u00e4ter besprechen.<\/p>\n<p><\/p>\n<p>Wie bereits erw\u00e4hnt, enth\u00e4lt die Nachricht selbst keinen Datenblock, sondern nur einen Zeiger auf den Speicherbereich, in dem der Block gespeichert ist. In diesem Teil erinnert das Gesamtbild der Funktionsweise des Medienstreamers an das Lager voller T\u00fcren im Cartoon \"Die Monster AG\", wo die T\u00fcren (Datenlinks \u2013 R\u00e4ume) mit irrsinniger Geschwindigkeit \u00fcber F\u00f6rderb\u00e4nder bewegt werden, w\u00e4hrend die R\u00e4ume selbst stillstehen. <\/p>\n<p><\/p>\n<p>Nun gehen wir die Hierarchie von unten nach oben durch und betrachten die genannten Entit\u00e4ten des Daten\u00fcbertragungsmechanismus im Media Streamer im Detail.<\/p>\n<p><\/p>\n<h3 id=\"blok--dannyh-dblk_t\">Datenblock <em>dblk_t<\/em><\/h3>\n<p><\/p>\n<p>Ein Datenblock besteht aus einem Header und einem Datenpuffer. Der Header wird durch die folgende Struktur beschrieben,<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct datab\n{\nunsigned char *db_base; \/\/ Zeiger auf den Beginn des Datenpuffers.\nunsigned char *db_lim;  \/\/ Zeiger auf das Ende des Datenpuffers.\nvoid (*db_freefn)(void*); \/\/ Funktion zur Freigabe des Speichers bei L\u00f6schung des Blocks.\nint db_ref; \/\/ Z\u00e4hler f\u00fcr Verweise.\n} dblk_t;<\/code><\/pre>\n<p><\/p>\n<p>Die Felder der Struktur enthalten Verweise auf den Anfang des Puffers, das Ende des Puffers und die Funktion zum L\u00f6schen des Datenpuffers. Das letzte Element im Header <em>db_ref<\/em> \u2013 ein Referenzz\u00e4hler, der, wenn er null erreicht, ein Signal zum L\u00f6schen dieses Blocks aus dem Speicher gibt. Wenn der Datenblock durch die Funktion <em>datab_alloc() <\/em>erstellt wurde, wird der Datenpuffer direkt hinter dem Header im Speicher platziert. In allen anderen F\u00e4llen kann der Puffer an einem anderen Ort liegen. Im Datenpuffer werden die Signalwerte oder andere Daten gespeichert, die wir durch Filter bearbeiten m\u00f6chten.<\/p>\n<p><\/p>\n<p>Ein neuer Datenblock wird durch die Funktion erstellt:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">dblk_t *datab_alloc(int size);<\/code><\/pre>\n<p><\/p>\n<p>Der Eingabeparameter ist die Gr\u00f6\u00dfe der Daten, die der Block speichern wird. Es wird zus\u00e4tzlicher Speicher zugewiesen, um am Anfang des zugewiesenen Speichers den Header \u2013 die Struktur <em>datab<\/em>\u2013 unterzubringen. Aber bei der Verwendung anderer Funktionen kommt das nicht immer vor; in einigen F\u00e4llen kann der Datenpuffer vom Header des Datenblocks getrennt sein. Die Felder der Struktur werden beim Erstellen so konfiguriert, dass das Feld <em>db_base<\/em> auf den Anfang des Datenbereichs verweist, und <em>db_lim<\/em> auf dessen Ende. Der Referenzz\u00e4hler <em>db_ref<\/em> wird in die Einheit gesetzt. Der Zeiger f\u00fcr die Datenbereinigung wird auf Null gesetzt.<\/p>\n<p><\/p>\n<h3 id=\"soobschenie-mblk_t\">Nachricht <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Wie bereits erw\u00e4hnt, haben die Warteschlangenelemente den Typ <em>mblk_t,<\/em> er ist wie folgt definiert:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct msgb\n{\n  struct msgb *b_prev;   \/\/ Zeiger auf das vorherige Listenelement.\n  struct msgb *b_next;   \/\/ Zeiger auf das n\u00e4chste Listenelement.\n  struct msgb *b_cont;   \/\/ Zeiger zum Anh\u00e4ngen anderer Nachrichten an die Nachricht, um ein Nachrichten-Tuple zu erstellen.\n  struct datab *b_datap; \/\/ Zeiger auf die Datenblockstruktur.\n  unsigned char *b_rptr; \/\/ Zeiger auf den Beginn des Datenbereichs zur Lesung von Daten aus dem Puffer b_datap.\n  unsigned char *b_wptr; \/\/ Zeiger auf den Beginn des Datenbereichs zur Schreibung von Daten in den Puffer b_datap.\n  uint32_t reserved1;    \/\/ Reserviertes Feld 1, der Medienstremer platziert dort Verwaltungsinformationen.\n  uint32_t reserved2;    \/\/ Reserviertes Feld 2, der Medienstremer platziert dort Verwaltungsinformationen.\n  #if defined(ORTP_TIMESTAMP)\n  struct timeval timestamp;\n  #endif\n  ortp_recv_addr_t recv_addr;\n} mblk_t;<\/code><\/pre>\n<p><\/p>\n<p>Struktur <em>mblk_t<\/em> enth\u00e4lt am Anfang die Zeiger <em>b_prev<\/em>, <em>b_next<\/em>, die erforderlich sind, um eine doppelt verkettete Liste (die Warteschlange ist) zu organisieren. <em>queue_t<\/em>). <\/p>\n<p><\/p>\n<p>Dann folgt der Zeiger <em>b_cont<\/em>, der nur verwendet wird, wenn die Nachricht in das Tuple eintritt. F\u00fcr die letzte Nachricht im Tuple bleibt dieser Zeiger null\u3002 <\/p>\n<p><\/p>\n<p>Als n\u00e4chstes sehen wir einen Zeiger auf einen Datenblock <em>b_datap<\/em>, der das zugrunde liegende Nachrichtenobjekt repr\u00e4sentiert. Danach folgen Zeiger auf den Bereich innerhalb des Datenpuffers des Blocks. Das Feld <em>b_rptr<\/em> weist auf die Stelle hin, von der aus Daten aus dem Puffer gelesen werden. Das Feld <em>b_wptr<\/em> bezeichnet den Ort, an dem Daten in den Puffer geschrieben werden. <\/p>\n<p><\/p>\n<p>Die verbleibenden Felder sind administrativen Charakters und betreffen nicht den Betrieb des Daten\u00fcbertragungsmechanismus.<\/p>\n<p><\/p>\n<p>Unten ist eine einzelne Nachricht mit dem Namen <em>m1<\/em> und einem Datenblock dargestellt <em>d1<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Wir erforschen die VoIP-Engine Mediastreamer2, Teil 11\" src=\"\/wp-content\/uploads\/2020\/04\/e6a726b13491832258c1228f03bde802.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIm n\u00e4chsten Bild sehen wir ein Tuple aus drei Nachrichten <em>m1<\/em>, <em>m1_1<\/em>, <em>m1_2<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Wir erforschen die VoIP-Engine Mediastreamer2, Teil 11\" src=\"\/wp-content\/uploads\/2020\/04\/9613feefe9684b9551cd98115faa9a08.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-soobscheniyami--mblk_t\">Die Funktionen zur Verarbeitung von Nachrichten <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Eine neue Nachricht <em>mblk_t<\/em> wird erstellt durch die Funktion:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *allocb(int size, int pri); <\/code><\/pre>\n<p><\/p>\n<p>dies reserviert im Speicher eine neue Nachricht <em>mblk_t<\/em> mit einem Datenblock der angegebenen Gr\u00f6\u00dfe <em>Gr\u00f6\u00dfe<\/em>, das zweite Argument \u2014 <em>pri<\/em> wird in dieser Version der Bibliothek nicht verwendet. Es sollte null bleiben. W\u00e4hrend der Ausf\u00fchrung der Funktion wird Speicher f\u00fcr die Struktur der neuen Nachricht reserviert und die Funktion <em>mblk_init()<\/em>, die alle Felder des erstellten Strukturbeispiels zur\u00fccksetzt und dann mit Hilfe des oben genannten <em>datab_alloc()<\/em>, einen Datenpuffer erstellt. Danach werden die Felder in der Struktur konfiguriert: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mp-&gt;b_datap=datab;\nmp-&gt;b_rptr=mp-&gt;b_wptr=datab-&gt;db_base;\nmp-&gt;b_next=mp-&gt;b_prev=mp-&gt;b_cont=NULL;<\/code><\/pre>\n<p><\/p>\n<p>Am Ende erhalten wir eine neue Nachricht mit initialisierten Feldern und einem leeren Datenpuffer. Um Daten zur Nachricht hinzuzuf\u00fcgen, m\u00fcssen diese in den Datenblockpuffer kopiert werden:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">memcpy(msg-&gt;b_rptr, data, size);<\/code><\/pre>\n<p><\/p>\n<p>wobei <em>data<\/em> \u2014 der Zeiger auf die Datenquelle, und <em>Gr\u00f6\u00dfe<\/em> \u2014 deren Gr\u00f6\u00dfe.<br \/>\ndanach muss der Schreibzeiger aktualisiert werden, sodass er wieder auf den Anfang des freien Bereichs im Puffer zeigt:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">msg-&gt;b_wptr = msg-&gt;b_wptr + size<\/code><\/pre>\n<p><\/p>\n<p>Soll eine Nachricht aus einem bereits vorhandenen Puffer erstellt werden, ohne Kopieren, wird daf\u00fcr die Funktion verwendet:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *esballoc(uint8_t *buf, int size, int pri, void (*freefn)(void*)); <\/code><\/pre>\n<p><\/p>\n<p>Die Funktion wird nach der Erstellung der Nachricht und der Struktur des Datenblocks die Zeiger auf die Daten an der Adresse <em>buf<\/em>. Das hei\u00dft, 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. <em>datab_alloc()<\/em>. Der an die Funktion \u00fcbergebene Datenpuffer bleibt an seiner Stelle, wird jedoch mit Hilfe von Zeigern auf den neu erstellten Datenblockheader gelenkt, der wiederum auf die Nachricht zeigt.<\/p>\n<p><\/p>\n<p>Zu einer Nachricht <em>mblk_t<\/em> k\u00f6nnen mehrere Datenbl\u00f6cke nacheinander angeh\u00e4ngt werden. Dies geschieht durch die Funktion:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * appendb(mblk_t *mp, const char *data, int size, bool_t pad); <\/code><\/pre>\n<p><\/p>\n<p><em>mp<\/em> \u2013 die Nachricht, zu der ein weiterer Datenblock hinzugef\u00fcgt wird;<br \/>\n<em>data<\/em> \u2013 ein Zeiger auf den Block, dessen Kopie in die Nachricht eingef\u00fcgt wird;<br \/>\n<em>Gr\u00f6\u00dfe<\/em> \u2013 die Gr\u00f6\u00dfe der Daten;<br \/>\n<em>pad<\/em> \u2013 ein Flag, das angibt, dass die Gr\u00f6\u00dfe des reservierten Speichers auf 4 Bytes ausgerichtet werden soll (die Auff\u00fcllung erfolgt mit Nullen). <\/p>\n<p><\/p>\n<p>Wenn im vorhandenen Datenpuffer der Nachricht ausreichend Platz vorhanden ist, werden die neuen Daten hinter den bereits dort befindlichen Daten eingef\u00fcgt. Ist der freie Platz im Datenpuffer der Nachricht jedoch geringer als <em>Gr\u00f6\u00dfe<\/em>, dann wird eine neue Nachricht mit ausreichender Pufferspeichergr\u00f6\u00dfe erstellt und die Daten werden in ihren Puffer kopiert. Diese neue Nachricht wird \u00fcber einen Zeiger <em>b_cont<\/em>an die urspr\u00fcngliche angeh\u00e4ngt. In diesem Fall wird die Nachricht zu einem Tuple. <\/p>\n<p><\/p>\n<p>Wenn ein weiterer Datenblock zum Tuple hinzugef\u00fcgt werden soll, muss die Funktion verwendet werden:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgappend(mblk_t *mp, const char *data, int size, bool_t pad);<\/code><\/pre>\n<p><\/p>\n<p>es wird die letzte Nachricht im Tuple gefunden (die ist <em>b_cont<\/em> null) und daf\u00fcr wird die Funktion <em>appendb() aufgerufen.<\/em>.<\/p>\n<p><\/p>\n<p>Um die Gr\u00f6\u00dfe der Daten in einer Nachricht oder Tuple zu ermitteln, kann die Funktion verwendet werden:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Diese durchl\u00e4uft alle Nachrichten des Tuples und gibt die Gesamtdatenmenge in den Datenpuffern dieser Nachrichten zur\u00fcck. F\u00fcr jede Nachricht wird die Datenmenge wie folgt berechnet:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\"> mp-&gt;b_wptr - mp-&gt;b_rptr<\/code><\/pre>\n<p><\/p>\n<p>Um zwei Tuples zu verbinden, wird die Funktion verwendet:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *concatb(mblk_t *mp, mblk_t *newm);<\/code><\/pre>\n<p><\/p>\n<p>Diese f\u00fcgt das Tuple <em>newm<\/em> ans Ende des Tuples an <em>mp<\/em> und gibt einen Zeiger auf die letzte Nachricht des resultierenden Tuples zur\u00fcck.<\/p>\n<p><\/p>\n<p>Falls erforderlich, kann das Tuple in eine einzige Nachricht mit einem einzigen Datenblock umgewandelt werden, dies geschieht durch die Funktion:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,int len);<\/code><\/pre>\n<p><\/p>\n<p>wenn das Argument <em>len<\/em> gleich -1 ist, wird die Gr\u00f6\u00dfe des reservierten Puffers automatisch bestimmt. Wenn <em>len<\/em> Wenn es sich um eine positive Zahl handelt, wird ein Puffer dieser Gr\u00f6\u00dfe 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\u00e4lt einen neuen Puffer mit den kopierten Daten. Die \u00fcbrigen Nachrichten werden gel\u00f6scht und der Speicher wird wieder in den Heap zur\u00fcckgegeben.<\/p>\n<p><\/p>\n<p>Beim L\u00f6schen der Struktur <em>mblk_t<\/em> wird der Referenzz\u00e4hler des Datenblocks ber\u00fccksichtigt. Wenn beim Aufruf von <em>freeb()<\/em> dieser gleich null ist, wird der Datenpuffer zusammen mit dem Exemplar gel\u00f6scht, <em>mblk_t<\/em>auf das verwiesen wird.<\/p>\n<p><\/p>\n<p>Initialisierung der Felder der neuen Nachricht:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void mblk_init(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Hinzuf\u00fcgen eines weiteren Datenabschnitts zur Nachricht: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * appendb(mblk_t *mp, const char *data, size_t size, bool_t pad);<\/code><\/pre>\n<p><\/p>\n<p>Wenn die neuen Daten nicht in den freien Speicher des Datenpuffers der Nachricht passen, wird der Nachricht ein separat erstelltes Nachricht mit dem ben\u00f6tigten Puffer angeh\u00e4ngt (im ersten Nachricht wird ein Zeiger auf die hinzugef\u00fcgte Nachricht gesetzt), die Nachricht wird zu einem Tuple.<\/p>\n<p><\/p>\n<p>Hinzuf\u00fcgen eines Datenabschnitts zum Tuple:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgappend(mblk_t *mp, const char *data, size_t size, bool_t pad); <\/code><\/pre>\n<p><\/p>\n<p>Die Funktion ruft appendb() in einer Schleife auf.<\/p>\n<p><\/p>\n<p>Zusammenf\u00fchren von zwei Tuples zu einem:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *concatb(mblk_t *mp, mblk_t *newm);<\/code><\/pre>\n<p><\/p>\n<p>Nachricht <em>newm<\/em> wird angeh\u00e4ngt an <em>mp<\/em>.<\/p>\n<p><\/p>\n<p>Erstellen einer Kopie eines einzelnen Nachrichtenblocks:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copyb(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Vollst\u00e4ndige Kopie eines Tupels mit allen Datenbl\u00f6cken:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copymsg(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Die Elemente des Tupels werden durch die Funktion <em>copyb()<\/em>.<\/p>\n<p><\/p>\n<p>Erstellen einer leichten Kopie der Nachricht <em>mblk_t<\/em>. Dabei wird der Datenblock nicht kopiert, sondern nur der Z\u00e4hler seiner Verweise erh\u00f6ht. <em>db_ref<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *dupb(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Erstellen einer leichten Kopie des Tupels. Die Datenbl\u00f6cke werden nicht kopiert, nur ihre Z\u00e4hler f\u00fcr Verweise erh\u00f6ht. <em>db_ref<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *dupmsg(mblk_t* m);<\/code><\/pre>\n<p><\/p>\n<p>Zusammenf\u00fcgen aller Nachrichten des Tupels zu einer einzigen Nachricht:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp, size_t len);<\/code><\/pre>\n<p><\/p>\n<p>Wenn das Argument <em>len<\/em> gleich -1 ist, wird die Gr\u00f6\u00dfe des reservierten Puffers automatisch bestimmt.<\/p>\n<p><\/p>\n<p>L\u00f6schen einer Nachricht oder eines Tupels:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void freemsg(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Der Z\u00e4hler der Verweise des Datenblocks wird um eins verringert. Erreicht dieser dabei null, wird der Datenblock ebenfalls gel\u00f6scht.<\/p>\n<p><\/p>\n<p>Berechnung der Gesamtgr\u00f6\u00dfe der Daten in einer Nachricht oder einem Tupel.<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">size_t msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Abrufen einer Nachricht aus dem Ende der Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *ms_queue_peek_last (q);<\/code><\/pre>\n<p><\/p>\n<p>Kopieren des Inhalts der reservierten Felder einer Nachricht in eine andere Nachricht (tats\u00e4chlich befinden sich in diesen Feldern Flags, die vom Mediastreamer verwendet werden):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_meta_copy(const mblk_t *source, mblk *dest);<\/code><\/pre>\n<p><\/p>\n<h3 id=\"ochered-queue_t\">Queue <em>queue_t<\/em><\/h3>\n<p><\/p>\n<p>Die Nachrichtenwarteschlange im Media-Streamer ist als zirkul\u00e4re doppelt verkettete Liste implementiert. Jeder Listeneintrag enth\u00e4lt einen Zeiger auf einen Datenblock mit Signalwerten. Das bedeutet, dass nur die Zeiger auf die Datenbl\u00f6cke bewegt werden, w\u00e4hrend die tats\u00e4chlichen Daten unver\u00e4ndert bleiben. Mit anderen Worten, es werden nur die Referenzen auf die Daten verschoben.<br \/>\nDie Struktur, die die Warteschlange beschreibt <em>queue_t<\/em>, ist unten dargestellt:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _queue\n{\n   mblk_t _q_stopper; \/* \"Leeres\" Element der Warteschlange, zeigt nicht auf Daten, wird nur zur Steuerung der Warteschlange verwendet. Bei der Initialisierung der Warteschlange (qinit()) werden seine Zeiger so eingerichtet, dass sie auf sich selbst zeigen. *\/\n   int q_mcount;        \/\/ Anzahl der Elemente in der Warteschlange.\n} queue_t;<\/code><\/pre>\n<p><\/p>\n<p>Die Struktur enth\u00e4lt ein Feld \u2013 einen Zeiger <em>_q_stopper<\/em> vom Typ *mblk_t, der auf das erste Element (Nachricht) in der Warteschlange zeigt. Das zweite Feld der Struktur ist ein Z\u00e4hler f\u00fcr die in der Warteschlange befindlichen Nachrichten.<br \/>\nIm folgenden Bild ist die Warteschlange mit dem Namen q1 zu sehen, die 4 Nachrichten m1, m2, m3, m4 enth\u00e4lt.<br \/>\n<img decoding=\"async\" alt=\"Wir erforschen die VoIP-Engine Mediastreamer2, Teil 11\" src=\"\/wp-content\/uploads\/2020\/04\/07fc043861edf15e5eb40e49c8ec5e3e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIm n\u00e4chsten Bild ist die Warteschlange mit dem Namen q1 zu sehen, die 4 Nachrichten m1, m2, m3, m4 enth\u00e4lt. Die Nachricht m2 ist der Kopf eines Tupels, zu dem noch zwei Nachrichten m2_1 und m2_2 f\u00fchren.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wir erforschen die VoIP-Engine Mediastreamer2, Teil 11\" src=\"\/wp-content\/uploads\/2020\/04\/8fa7667f3690674a2ee321b4a6c39e1d.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-ocheredyami-queue_t\">Funktionen zur Verarbeitung von Warteschlangen queue_t<\/h3>\n<p><\/p>\n<p>Initialisierung der Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void qinit(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>Feld <em>_q_stopper<\/em> (weiterhin \"Stopper\" genannt) wird durch eine Funktion initialisiert <em>mblk_init()<\/em>, sein Zeiger auf das vorherige und das n\u00e4chste Element wird so eingestellt, dass sie auf ihn selbst zeigen. Der Elementz\u00e4hler der Warteschlange wird auf null zur\u00fcckgesetzt.<\/p>\n<p><\/p>\n<p>Hinzuf\u00fcgen eines neuen Elements (Nachricht):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void putq(queue_t *q, mblk_t *m);<\/code><\/pre>\n<p><\/p>\n<p>Das neue Element <em>m<\/em> wird am Ende der Liste hinzugef\u00fcgt, die Zeiger des Elements werden so eingestellt, dass der Stopp das n\u00e4chste Element f\u00fcr es wird, und es wird f\u00fcr den Stopp das vorherige. Der Z\u00e4hler der Elemente der Warteschlange wird inkrementiert.<\/p>\n<p><\/p>\n<p>Entziehen eines Elements aus der Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * getq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>es wird die Nachricht abgeholt, die nach dem Stopp steht, der Elementz\u00e4hler wird dekrementiert. Wenn es in der Warteschlange au\u00dfer dem Stopp keine Elemente mehr gibt, wird 0 zur\u00fcckgegeben.<\/p>\n<p><\/p>\n<p>Einf\u00fcgen einer Nachricht in die Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void insq(queue_t *q, mblk_t *emp, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>Das Element <em>mp<\/em> es wird vor dem Element <em>emp<\/em>useString <em>emp<\/em>=0, dann wird die Nachricht am Ende der Warteschlange hinzugef\u00fcgt.<\/p>\n<p><\/p>\n<p>Entziehen einer Nachricht vom Kopf der Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void remq(queue_t *q, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>Der Elementz\u00e4hler wird dekrementiert.<\/p>\n<p><\/p>\n<p>Lesen des Zeigers auf das erste Element in der Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * peekq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>Entfernen aller Elemente aus der Warteschlange mit Entfernen der eigentlichen Elemente:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void flushq(queue_t *q, int how);<\/code><\/pre>\n<p><\/p>\n<p>Das Argument <em>wie<\/em> wird nicht verwendet. Der Z\u00e4hler f\u00fcr die Elemente in der Warteschlange wird auf null gesetzt.<\/p>\n<p><\/p>\n<p>Makro zum Lesen des Zeigers auf das letzte Element der Warteschlange:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * qlast(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>Beim Arbeiten mit Nachrichtenwarteschlangen sollte beachtet werden, dass bei einem Aufruf von <em>ms_queue_put(q, m)<\/em> mit einem null-Zeiger auf die Nachricht, die Funktion in eine Endlosschleife ger\u00e4t. Ihr Programm wird h\u00e4ngen bleiben. Dasselbe Verhalten zeigt sich bei <em>ms_queue_next(q, m)<\/em>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-filtrov\">Filterverbindung<\/h3>\n<p><\/p>\n<p>Die oben beschriebene Warteschlange wird verwendet, um Nachrichten von einem Filter zu einem anderen oder von einem zu mehreren Filtern zu \u00fcbertragen. Die Filter und ihre Verbindungen bilden einen gerichteten Graph. Der Eingang oder Ausgang eines Filters wird mit dem allgemeinen Begriff \"Pin\" bezeichnet. Zur Beschreibung der Reihenfolge, in der die Filter miteinander verbunden sind, wird im Mediastr\u00f6mer der Begriff \"Signalpunkt\" verwendet. Ein Signalpunkt ist eine Struktur <em>_MSCPoint<\/em>, die einen Zeiger auf den Filter und die Nummer eines seiner Pins enth\u00e4lt; damit beschreibt sie die Verbindung eines der Eing\u00e4nge oder Ausg\u00e4nge des Filters. <\/p>\n<p><\/p>\n<h4 id=\"signalnaya-tochka-grafa-obrabotki-dannyh\">Signalpunkt des Datenverarbeitungsgrafen<\/h4>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSCPoint{\nstruct _MSFilter *filter; \/\/ Zeiger auf den Media-Streamer-Filter.\nint pin;                        \/\/ Nummer eines der Eing\u00e4nge oder Ausg\u00e4nge des Filters, d.h. Pin.\n} MSCPoint;\n<\/code><\/pre>\n<p><\/p>\n<p>Die Pins der Filter sind nummeriert, beginnend bei null. <\/p>\n<p><\/p>\n<p>Die Verbindung zweier Pins \u00fcber eine Nachrichtenwarteschlange wird durch die Struktur <em>_MSQueue<\/em>, die eine Nachrichtenwarteschlange und Zeiger auf zwei Signalenpunkte enth\u00e4lt, die sie verbindet:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSQueue\n{\nqueue_t q;\nMSCPoint prev;\nMSCPoint next;\n}MSQueue;\n<\/code><\/pre>\n<p><\/p>\n<p>Wir nennen diese Struktur Signallink. Jeder Medienstream-Filter enth\u00e4lt eine Tabelle der Eingangslinks und eine Tabelle der Ausgangslinks (<em>MSQueue<\/em>). Die Gr\u00f6\u00dfe der Tabellen wird bei der Erstellung des Filters festgelegt, dies haben wir bereits mit der exportierten Variablen vom Typ <em>MSFilterDesc<\/em>, als wir unseren eigenen Filter entwickelt haben. Im Folgenden wird die Struktur gezeigt, die jeden Filter im Medienstream beschreibt, <em>MSFilter<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">\nstruct _MSFilter{\n    MSFilterDesc *desc;    \/* Zeiger auf die Filterbeschreibung. *\/\n    \/* Gesch\u00fctzte Attribute, die d\u00fcrfen nicht verschoben oder entfernt werden, andernfalls funktioniert die Interaktion mit Plugins nicht mehr. *\/\n    ms_mutex_t lock;      \/* Semaphore. *\/\n    MSQueue **inputs;     \/* Eingangslinktabelle. *\/\n    MSQueue **outputs;    \/* Ausgangslinktabelle. *\/\n    struct _MSFactory *factory; \/* Zeiger auf die Fabrik, die diese Filterinstanz erstellt hat. *\/\n    void *padding;              \/* Wird nicht verwendet und wird aktiviert, wenn gesch\u00fctzte Felder hinzugef\u00fcgt werden. *\/\n    void *data;                 \/* Zeiger auf eine beliebige Struktur zur Speicherung der internen Zustandsdaten des Filters und f\u00fcr zwischengerechnete Berechnungen. *\/\n    struct _MSTicker *ticker;   \/* Zeiger auf das Ticker-Objekt, das nicht null sein darf, wenn die Funktion process() aufgerufen wird. *\/\n    \/*private attributes, they can be moved and changed at any time*\/\n    MSList *notify_callbacks; \/* Liste von R\u00fcckruffunktionen, die zur Verarbeitung von Filterereignissen verwendet werden. *\/\n    uint32_t last_tick;       \/* Nummer des letzten Taktes, in dem die Funktion process() aufgerufen wurde. *\/ \n    MSFilterStats *stats;     \/* Statistiken zur Filterverarbeitung. *\/\n    int postponed_task; \/* Anzahl der verschobenen Aufgaben. Einige Filter k\u00f6nnen die Datenverarbeitung (Aufruf von process()) um mehrere Takte verz\u00f6gern. *\/\n    bool_t seen;  \/* Flag, das der Ticker verwendet, um zu kennzeichnen, dass diese Filterinstanz in diesem Takt bereits bedient wurde. *\/\n};\ntypedef struct _MSFilter MSFilter;\n<\/code><\/pre>\n<p><\/p>\n<p>Nachdem wir die Filter in unserem C-Programm gem\u00e4\u00df unserem Konzept verbunden haben (aber den Ticker nicht angeschlossen haben), haben wir damit einen gerichteten Graphen geschaffen, dessen Knoten Instanzen der Struktur sind. <em>MSFilter<\/em>, w\u00e4hrend die Kanten Instanzen der Links sind. <em>MSQueue<\/em>. <\/p>\n<p><\/p>\n<h3 id=\"zakulisnaya-deyatelnost-tikera\">Hintergrundaktivit\u00e4ten des Tickers<\/h3>\n<p><\/p>\n<p>Als ich Ihnen sagte, dass der Ticker ein Filter f\u00fcr Taktsignale ist, war das nicht die ganze Wahrheit. Der Ticker ist ein Objekt, das Funktionen gem\u00e4\u00df einem Zeitgeber aktiviert. <em>process()<\/em> aller Filter des Schemas (des Graphen), an die er angeschlossen ist. Wenn wir in einem C-Programm einen Tick an einen Filter des Graphen anschlie\u00dfen, zeigen wir dem Tick den Graphen, den er von diesem Zeitpunkt an verwalten wird, bis wir ihn wieder trennen. Nach dem Anschluss beginnt der Tick, den ihm anvertrauten Graph zu \u00fcberwachen und erstellt eine Liste der Filter, in die er integriert ist. Um nicht denselben Filter zweimal zu \"z\u00e4hlen\", markiert er die entdeckten Filter, indem er ein H\u00e4kchen in ihnen setzt <em>seen<\/em>. Die Suche erfolgt anhand der Linktabellen, die jeder Filter hat. <\/p>\n<p><\/p>\n<p>W\u00e4hrend seiner Einf\u00fchrungsrunde pr\u00fcft der Ticker, ob unter den Filtern mindestens einer als Datenblockquelle fungiert. Wenn dies nicht der Fall ist, gilt der Graph als ung\u00fcltig und der Ticker wird sofort beendet.<\/p>\n<p><\/p>\n<p>Wenn der Graph \"korrekt\" ist, wird f\u00fcr jeden gefundenen Filter zur Initialisierung eine Funktion aufgerufen <em>preprocess()<\/em>aufgerufen. Sobald der Zeitpunkt f\u00fcr den n\u00e4chsten Verarbeitungstakt erreicht ist (standardm\u00e4\u00dfig alle 10 Millisekunden), ruft der Ticker die Funktion <em>process()<\/em> f\u00fcr alle zuvor gefundenen Quellfilter und anschlie\u00dfend f\u00fcr die restlichen Filter in der Liste auf. Wenn ein Filter Eingangslinks hat, wird die Funktion <em>process()<\/em> wird wiederholt, bis die Warteschlangen der eingehenden Links leer sind. Danach wechselt er zum n\u00e4chsten Filter in der Liste und \"scrollt\" ihn, bis die eingehenden Links von den Nachrichten befreit sind. Der Ticker wechselt von Filter zu Filter, bis die Liste abgeschlossen ist. Damit endet die Taktverarbeitung. <\/p>\n<p><\/p>\n<p>Nun kehren wir zu den Tuples zur\u00fcck und besprechen, aus welchem Grund eine solche Entit\u00e4t im Medienstreamer hinzugef\u00fcgt wurde. Generell stimmt das Volumen der Daten, das der Algorithmus innerhalb des Filters ben\u00f6tigt, nicht mit der Gr\u00f6\u00dfe der Datenpuffer \u00fcberein, die am Eingang ankommen, und ist nicht unbedingt ein Vielfaches davon. Nehmen wir an, wir schreiben einen Filter, der eine schnelle Fourier-Transformation durchf\u00fchrt, die definitionsgem\u00e4\u00df nur Datenbl\u00f6cke verarbeiten kann, deren Gr\u00f6\u00dfe einer Potenz von zwei entspricht. Setzen wir dies auf 512 Abtastwerte. Wenn die Daten \u00fcber 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\u00fcrde jedoch eine Kollision mit dem Ticker entstehen, der vergeblich versucht, den Filter zu leeren, bis der Eingangslink aufgebraucht ist. Fr\u00fcher haben wir diese Regel als das dritte Prinzip der Filterfunktion bezeichnet. Gem\u00e4\u00df diesem Prinzip sollte die Funktion process() des Filters alle Daten aus den Eingangswarteschlangen abholen.<\/p>\n<p><\/p>\n<p>Dar\u00fcber hinaus k\u00f6nnen von der Eingangsseite nur 512 Messungen nicht abgerufen werden, da diese nur in ganzen Bl\u00f6cken abgerufen werden k\u00f6nnen. Das bedeutet, dass der Filter 640 Messungen abholen muss und von diesen 512 verwendet werden, w\u00e4hrend der Rest bis zur Ansammlung neuer Datenpools gespeichert wird. Unser Filter muss neben seiner Hauptaufgabe auch unterst\u00fctzende Ma\u00dfnahmen zur Zwischenlagerung der Eingangsdaten gew\u00e4hrleisten. Die Entwickler des Media-Streamers haben f\u00fcr diese gemeinsame Herausforderung ein spezielles Objekt entwickelt \u2013 den MSBufferizer (Puffer), der diese Aufgabe mithilfe von Tupeln l\u00f6st. <\/p>\n<p><\/p>\n<h3 id=\"buferizator-msbufferizer\">Puffer (MSBufferizer)<\/h3>\n<p><\/p>\n<p>Dieses Objekt wird die Eingangsdaten innerhalb des Filters sammeln und beginnt, sie zur Verarbeitung freizugeben, sobald die Informationsmenge ausreicht, um den Filteralgorithmus auszuf\u00fchren. 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\u00fcckgibt, beginnt die Funktion process() des Filters, Daten in passenden Portionen aus dem Puffer abzurufen und zu verarbeiten, bis diese ersch\u00f6pft sind.<br \/>\nNicht verwendete Daten bleiben im Puffer als erstes Element eines Tupels, an das die nachfolgenden Eingabedaten angeh\u00e4ngt werden. <\/p>\n<p><\/p>\n<p>Die Struktur, die den Puffer beschreibt: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">struct _MSBufferizer{\nqueue_t q; \/* Nachrichtenwarteschlange. *\/\nint size; \/* Gesamtgr\u00f6\u00dfe der derzeit im Puffer befindlichen Daten. *\/\n};\ntypedef struct _MSBufferizer MSBufferizer;<\/code><\/pre>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-msbufferizer\">Funktionen zur Verarbeitung mit MSBufferizer<\/h3>\n<p><\/p>\n<p>Erstellen eines neuen Pufferexemplars:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">MSBufferizer * ms_bufferizer_new(void);<\/code><\/pre>\n<p><\/p>\n<p>Speicher wird reserviert, initiiert in <em>ms_bufferizer_init()<\/em> und gibt einen Zeiger zur\u00fcck.<\/p>\n<p><\/p>\n<p>Initialisierungsfunktion:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_init(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Die Warteschlange <em>q<\/em>, Feld <em>Gr\u00f6\u00dfe<\/em> wird auf null gesetzt.<\/p>\n<p><\/p>\n<p>Nachricht hinzuf\u00fcgen:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_put(MSBufferizer *obj, mblk_t *m); <\/code><\/pre>\n<p><\/p>\n<p>Die Nachricht m wird in die Warteschlange eingef\u00fcgt. Die berechnete Gr\u00f6\u00dfe der Datenbl\u00f6cke wird zu <em>Gr\u00f6\u00dfe<\/em>.<\/p>\n<p><\/p>\n<p>\u00dcbertragung aller Nachrichten aus der Datenwarteschlange in den Puffer <em>q<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_put_from_queue(MSBufferizer *obj, MSQueue *q);   <\/code><\/pre>\n<p><\/p>\n<p>Nachrichtentransfer vom Link <em>q<\/em> in den Puffer erfolgt mit der Funktion <em>ms_bufferizer_put()<\/em>.<\/p>\n<p><\/p>\n<p>Lesen aus dem Puffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_read(MSBufferizer *obj, uint8_t *data, int datalen); <\/code><\/pre>\n<p><\/p>\n<p>Wenn die Gr\u00f6\u00dfe der im Puffer angesammelten Daten kleiner ist als angefordert (<em>datalen<\/em>), die Funktion gibt null zur\u00fcck, 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\u00f6scht und der Speicher freigegeben. Der Kopiervorgang endet, wenn datalen Bytes kopiert wurden. Wenn der Speicherplatz w\u00e4hrend eines Datenblocks ausgeht, wird in dieser Nachricht der Datenblock auf den verbleibenden nicht kopierten Teil verk\u00fcrzt. Beim n\u00e4chsten Aufruf wird die Kopie an dieser Stelle fortgesetzt.<\/p>\n<p><\/p>\n<p>Lesen der Menge an Daten, die derzeit im Puffer verf\u00fcgbar sind:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_get_avail(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Gibt das Feld zur\u00fcck <em>Gr\u00f6\u00dfe<\/em> desPuffers zur\u00fcck.<\/p>\n<p><\/p>\n<p>\u00dcbertragung eines Teils der im Puffer befindlichen Daten:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);<\/code><\/pre>\n<p><\/p>\n<p>Die angegebene Anzahl von Bytes wird extrahiert und verworfen. Die \u00e4ltesten Daten werden verworfen.<\/p>\n<p><\/p>\n<p>L\u00f6schen aller Nachrichten im Puffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_flush(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Der Datenz\u00e4hler wird auf null zur\u00fcckgesetzt.<\/p>\n<p><\/p>\n<p>L\u00f6schen aller Nachrichten im Puffer:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_uninit(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Der Z\u00e4hler wird nicht zur\u00fcckgesetzt.<\/p>\n<p><\/p>\n<p>L\u00f6schen des Puffers und Freigabe des Speichers:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_destroy(MSBufferizer *obj);  <\/code><\/pre>\n<p><\/p>\n<p>Beispiele f\u00fcr 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:<noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/BelledonneCommunications\/mediastreamer2\/blob\/1a9e8dec369a65ed10db975f5b77b92dc999b096\/src\/audiofilters\/l16.c\"> l16.c<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Im n\u00e4chsten Artikel werden wir die Lastbewertung auf dem Ticker und M\u00f6glichkeiten zur Bek\u00e4mpfung einer \u00fcberm\u00e4\u00dfigen Rechenlast im Mediastreamer behandeln.<\/p>\n<p>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/499010\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0411\u043b\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 dblk_t \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 mblk_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 mblk_t \u041e\u0447\u0435\u0440\u0435\u0434\u044c queue_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 queue_t \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0421\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0433\u0440\u0430\u0444\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0417\u0430\u043a\u0443\u043b\u0438\u0441\u043d\u0430\u044f \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0442\u0438\u043a\u0435\u0440\u0430 \u0411\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0442\u043e\u0440 (MSBufferizer) \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MSBufferizer \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440. \u042d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u043c\u044b \u043f\u043e\u0441\u0432\u0435\u0442\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79541,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79540","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0411\u043b\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 dblk_t \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 mblk_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 mblk_t \u041e\u0447\u0435\u0440\u0435\u0434\u044c queue_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 queue_t \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0421\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0433\u0440\u0430\u0444\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0417\u0430\u043a\u0443\u043b\u0438\u0441\u043d\u0430\u044f \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0442\u0438\u043a\u0435\u0440\u0430 \u0411\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0442\u043e\u0440 (MSBufferizer) \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MSBufferizer \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440. \u042d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u043c\u044b \u043f\u043e\u0441\u0432\u0435\u0442\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0418\u0437\u0443\u0447\u0430\u0435\u043c VoIP-\u0434\u0432\u0438\u0436\u043e\u043a Mediastreamer2. \u0427\u0430\u0441\u0442\u044c 11 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0411\u043b\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 dblk_t \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 mblk_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 mblk_t \u041e\u0447\u0435\u0440\u0435\u0434\u044c queue_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 queue_t \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0421\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0433\u0440\u0430\u0444\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0417\u0430\u043a\u0443\u043b\u0438\u0441\u043d\u0430\u044f \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0442\u0438\u043a\u0435\u0440\u0430 \u0411\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0442\u043e\u0440 (MSBufferizer) \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MSBufferizer \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440. \u042d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u043c\u044b \u043f\u043e\u0441\u0432\u0435\u0442\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-04-28T05:42:01+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-04-28T05:42:01+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Wir lernen die VoIP-Engine Mediastreamer2 kennen. Teil 11 | ProHoster","description":"Das Material des Artikels stammt von meinem Dzen-Kanal. Datenbewegungsmechanismus Block dblk_t Nachrichten mblk_t Funktionen zum Arbeiten mit Nachrichten mblk_t Warteschlange queue_t Funktionen zum Arbeiten mit Warteschlangen queue_t Verbindung von Filtern Signalpunkt des Datenverarbeitungsdiagramms Hintergrundaktivit\u00e4t des Tickers Puffer (MSBufferizer) Funktionen zum Arbeiten mit MSBufferizer In unserem letzten Artikel haben wir unseren eigenen Filter entwickelt. In diesem Artikel widmen wir uns der Struktur.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0418\u0437\u0443\u0447\u0430\u0435\u043c VoIP-\u0434\u0432\u0438\u0436\u043e\u043a Mediastreamer2. \u0427\u0430\u0441\u0442\u044c 11 | ProHoster","og:description":"\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0437\u044f\u0442 \u0441 \u043c\u043e\u0435\u0433\u043e \u0434\u0437\u0435\u043d-\u043a\u0430\u043d\u0430\u043b\u0430. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0411\u043b\u043e\u043a \u0434\u0430\u043d\u043d\u044b\u0445 dblk_t \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 mblk_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 mblk_t \u041e\u0447\u0435\u0440\u0435\u0434\u044c queue_t \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 queue_t \u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0421\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0433\u0440\u0430\u0444\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0417\u0430\u043a\u0443\u043b\u0438\u0441\u043d\u0430\u044f \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0442\u0438\u043a\u0435\u0440\u0430 \u0411\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0442\u043e\u0440 (MSBufferizer) \u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MSBufferizer \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440. \u042d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e \u043c\u044b \u043f\u043e\u0441\u0432\u0435\u0442\u0438\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-04-28T05:42:01+00:00","article:modified_time":"2020-04-28T05:42:01+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79540","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 12:40:33","updated":"2022-10-06 16:26:59"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/79540","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=79540"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/79540\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/79541"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=79540"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=79540"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=79540"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}