{"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\/pl\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","title":{"rendered":"Badamy silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 11","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Materia\u0142 artyku\u0142u pochodzi z mojego <noindex><a rel=\"nofollow\" href=\"https:\/\/zen.yandex.ru\/profile\/editor\/id\/5e3f8e0751f5346faab4fc7a\">kana\u0142u Zen<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Badamy silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 11\" src=\"\/wp-content\/uploads\/2020\/04\/b66fab43eb45c626c50f0383dcfa7cca.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mechanizm przesy\u0142ania danych<\/p>\n<p><\/p>\n<ul>\n<li>Blok danych dblk_t<\/li>\n<li>Wiadomo\u015b\u0107 mblk_t<\/li>\n<li>Funkcje pracy z wiadomo\u015bciami mblk_t<\/li>\n<li>Kolejka queue_t<\/li>\n<li>Funkcje pracy z kolejkami queue_t<\/li>\n<li>Po\u0142\u0105czenie filtr\u00f3w<\/li>\n<li>Punkt sygna\u0142owy grafu przetwarzania danych<\/li>\n<li>Kulisy dzia\u0142ania tickera<\/li>\n<li>Bufor (MSBufferizer)<\/li>\n<li>Funkcje pracy z MSBufferizer<\/li>\n<\/ul>\n<p><\/p>\n<p>W poprzedniej <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/497932\/\">artyku\u0142<\/a><\/noindex> opracowali\u015bmy nasz w\u0142asny filtr. Ten artyku\u0142 po\u015bwi\u0119cimy konstrukcji wewn\u0119trznego mechanizmu przesy\u0142ania danych mi\u0119dzy filtrami mediastreamera. Pozwoli to w przysz\u0142o\u015bci pisa\u0107 bardziej skomplikowane filtry przy mniejszych nak\u0142adach.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2 id=\"mehanizm-peremescheniya-dannyh\">Mechanizm przesy\u0142ania danych<\/h2>\n<p><\/p>\n<p>Przesy\u0142anie danych w mediastreamerze odbywa si\u0119 za pomoc\u0105 kolejki opisanej przez struktury <em>queue_t<\/em>. Przez kolejki przesuwaj\u0105 si\u0119 sekwencje wiadomo\u015bci typu <em>mblk_t<\/em>, kt\u00f3re same z siebie nie zawieraj\u0105 danych sygna\u0142u, a jedynie odniesienia do poprzedniej, nast\u0119pnej wiadomo\u015bci oraz bloku danych. Ponadto, chc\u0119 szczeg\u00f3lnie podkre\u015bli\u0107, \u017ce istnieje pole do odniesienia do wiadomo\u015bci tego samego typu, kt\u00f3re pozwala zorganizowa\u0107 jednokierunkow\u0105 list\u0119 wiadomo\u015bci. Grup\u0119 wiadomo\u015bci po\u0142\u0105czonych tak\u0105 list\u0105 b\u0119dziemy nazywa\u0107 krotk\u0105. W ten spos\u00f3b, ka\u017cdy element kolejki mo\u017ce by\u0107 pojedyncz\u0105 wiadomo\u015bci\u0105 <em>mblk_t<\/em>, a mo\u017ce by\u0107 tak\u017ce g\u0142ow\u0105 krotki wiadomo\u015bci <em>mblk_t<\/em>. Ka\u017cda wiadomo\u015b\u0107 w krotce mo\u017ce mie\u0107 sw\u00f3j przypisany blok danych. Dlaczego potrzebne s\u0105 krotki, om\u00f3wimy troch\u0119 p\u00f3\u017aniej.<\/p>\n<p><\/p>\n<p>Jak wspomniano wcze\u015bniej, samo w sobie wiadomo\u015b\u0107 nie zawiera bloku danych, a zamiast tego zawiera tylko wska\u017anik na obszar pami\u0119ci, w kt\u00f3rym przechowywany jest blok. W tej cz\u0119\u015bci og\u00f3lny obraz dzia\u0142ania medi\u00f3w strumieniowych przypomina magazyn drzwi w multfilmie \"Potwory w pracy\", gdzie drzwi (linki do danych - pomieszczenia) poruszaj\u0105 si\u0119 z osza\u0142amiaj\u0105c\u0105 pr\u0119dko\u015bci\u0105 po podwieszonych ta\u015bmach, podczas gdy same pomieszczenia pozostaj\u0105 nieruchome. <\/p>\n<p><\/p>\n<p>Teraz, przechodz\u0105c przez hierarchi\u0119 od do\u0142u do g\u00f3ry, przyjrzyjmy si\u0119 dok\u0142adniej wymienionym bytom mechanizmu przesy\u0142ania danych w mediastreamerze.<\/p>\n<p><\/p>\n<h3 id=\"blok--dannyh-dblk_t\">Blok danych <em>dblk_t<\/em><\/h3>\n<p><\/p>\n<p>Blok danych sk\u0142ada si\u0119 z nag\u0142\u00f3wka i bufora danych. Nag\u0142\u00f3wek opisany jest nast\u0119puj\u0105c\u0105 struktur\u0105,<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct datab\n{\nunsigned char *db_base; \/\/ Wska\u017anik na pocz\u0105tek bufora danych.\nunsigned char *db_lim;  \/\/ Wska\u017anik na koniec bufora danych.\nvoid (*db_freefn)(void*); \/\/ Funkcja zwalniaj\u0105ca pami\u0119\u0107 przy usuni\u0119ciu bloku.\nint db_ref; \/\/ Licznik referencji.\n} dblk_t;<\/code><\/pre>\n<p><\/p>\n<p>Pola struktury zawieraj\u0105 wska\u017aniki na pocz\u0105tek bufora, koniec bufora oraz funkcj\u0119 usuwania bufora danych. Ostatni element w nag\u0142\u00f3wku <em>db_ref<\/em> \u2014 licznik referencji, gdy osi\u0105gnie zero, wskazuje to na potrzeb\u0119 usuni\u0119cia bloku z pami\u0119ci. Je\u015bli blok danych zosta\u0142 utworzony przez funkcj\u0119 <em>datab_alloc() <\/em>, to bufor danych b\u0119dzie umieszczony w pami\u0119ci tu\u017c po nag\u0142\u00f3wku. W przeciwnym razie bufor mo\u017ce znajdowa\u0107 si\u0119 gdzie indziej. W buforze danych b\u0119d\u0105 przechowywane pr\u00f3bki sygna\u0142u lub inne dane, kt\u00f3re chcemy przetwarza\u0107 filtrami.<\/p>\n<p><\/p>\n<p>Nowa instancja bloku danych jest tworzona za pomoc\u0105 funkcji:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">dblk_t *datab_alloc(int size);<\/code><\/pre>\n<p><\/p>\n<p>Jako parametr wej\u015bciowy przekazywany jest rozmiar danych, kt\u00f3re ma przechowywa\u0107 blok. Przydzielana jest wi\u0119ksza ilo\u015b\u0107 pami\u0119ci, aby na pocz\u0105tku przydzielonej pami\u0119ci umie\u015bci\u0107 nag\u0142\u00f3wek \u2014 struktur\u0119 <em>datab<\/em>. Ale przy u\u017cyciu innych funkcji zdarza si\u0119, \u017ce bufor danych mo\u017ce znajdowa\u0107 si\u0119 niezale\u017cnie od nag\u0142\u00f3wka bloku danych. Pola struktury przy tworzeniu s\u0105 konfigurowane w ten spos\u00f3b, aby pole <em>db_base<\/em> wskazywa\u0142o na pocz\u0105tek obszaru danych, a <em>db_lim<\/em> na jego koniec. Licznik referencji <em>db_ref<\/em> ustawiany jest na jeden. Wska\u017anik funkcji czyszczenia danych ustawiany jest na zero.<\/p>\n<p><\/p>\n<h3 id=\"soobschenie-mblk_t\">Komunikat <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Jak ju\u017c wspomniano, elementy kolejki maj\u0105 typ <em>mblk_t,<\/em> zdefiniowany jest w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct msgb\n{\n  struct msgb *b_prev;   \/\/ Wska\u017anik na poprzedni element listy.\n  struct msgb *b_next;   \/\/ Wska\u017anik na nast\u0119pny element listy.\n  struct msgb *b_cont;   \/\/ Wska\u017anik do do\u0142\u0105czania innych wiadomo\u015bci do wiadomo\u015bci, aby utworzy\u0107 krotk\u0119 wiadomo\u015bci.\n  struct datab *b_datap; \/\/ Wska\u017anik na struktur\u0119 bloku danych.\n  unsigned char *b_rptr; \/\/ Wska\u017anik na pocz\u0105tek obszaru danych do odczytu danych z bufora b_datap.\n  unsigned char *b_wptr; \/\/ Wska\u017anik na pocz\u0105tek obszaru danych do zapisu danych z bufora b_datap.\n  uint32_t reserved1;    \/\/ Zarezerwowane pole1, mediastreamer umieszcza tam informacje serwisowe.\n  uint32_t reserved2;    \/\/ Zarezerwowane pole2, mediastreamer umieszcza tam informacje serwisowe.\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>Struktura <em>mblk_t<\/em> na pocz\u0105tku zawiera wska\u017aniki <em>b_prev<\/em>, <em>b_next<\/em>, kt\u00f3re s\u0105 niezb\u0119dne do organizacji dwukierunkowej listy (kt\u00f3rej jest kolejka <em>queue_t<\/em>). <\/p>\n<p><\/p>\n<p>Nast\u0119pnie znajduje si\u0119 wska\u017anik <em>b_cont<\/em>, kt\u00f3ry jest u\u017cywany tylko wtedy, gdy wiadomo\u015b\u0107 wchodzi do krotki. Dla ostatniej wiadomo\u015bci w krotce ten wska\u017anik pozostaje zerowy. <\/p>\n<p><\/p>\n<p>Nast\u0119pnie widzimy wska\u017anik na blok danych <em>b_datap<\/em>, dla kt\u00f3rego istnieje wiadomo\u015b\u0107. Nast\u0119pne s\u0105 wska\u017aniki na obszar w obr\u0119bie bufora danych bloku. Pole <em>b_rptr<\/em> okre\u015bla miejsce, z kt\u00f3rego b\u0119d\u0105 odczytywane dane z bufora. Pole <em>b_wptr<\/em> okre\u015bla miejsce, z kt\u00f3rego b\u0119d\u0105 zapisywane dane do bufora. <\/p>\n<p><\/p>\n<p>Pozosta\u0142e pola maj\u0105 charakter operacyjny i nie dotycz\u0105 dzia\u0142ania mechanizmu przesy\u0142ania danych.<\/p>\n<p><\/p>\n<p>Poni\u017cej przedstawiono pojedyncz\u0105 wiadomo\u015b\u0107 o nazwie <em>m1<\/em> i bloku danych <em>d1<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Badamy silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 11\" src=\"\/wp-content\/uploads\/2020\/04\/e6a726b13491832258c1228f03bde802.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNa nast\u0119pnym rysunku przedstawiono krotk\u0119 z trzema wiadomo\u015bciami <em>m1<\/em>, <em>m1_1<\/em>, <em>m1_2<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Badamy silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 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\">Funkcje pracy z wiadomo\u015bciami <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Nowa wiadomo\u015b\u0107 <em>mblk_t<\/em> jest tworzona przez funkcj\u0119:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *allocb(int size, int pri); <\/code><\/pre>\n<p><\/p>\n<p>Tworzy nowe wiadomo\u015bci w pami\u0119ci <em>mblk_t<\/em> z blokiem danych o okre\u015blonym rozmiarze <em>rozmiar<\/em>, drugi argument \u2014 <em>pri<\/em> nie jest u\u017cywany w omawianej wersji biblioteki. Powinien pozosta\u0107 zerowy. W trakcie dzia\u0142ania funkcji pami\u0119\u0107 b\u0119dzie przydzielana pod struktur\u0119 nowej wiadomo\u015bci, a funkcja <em>mblk_init()<\/em>, kt\u00f3ra wyzeruje wszystkie pola nowo utworzonej struktury, a nast\u0119pnie, za pomoc\u0105 wspomnianego wcze\u015bniej, <em>datab_alloc()<\/em>, utworzy bufor danych. Nast\u0119pnie zostan\u0105 ustawione pola w strukturze: <\/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>Na wyj\u015bciu otrzymujemy now\u0105 wiadomo\u015b\u0107 z zainicjowanymi polami i pustym buforem danych. Aby doda\u0107 do wiadomo\u015bci dane, nale\u017cy skopiowa\u0107 je do bufora bloku danych:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">memcpy(msg-&gt;b_rptr, data, size);<\/code><\/pre>\n<p><\/p>\n<p>gdzie <em>data<\/em> \u2014 wska\u017anik na \u017ar\u00f3d\u0142o danych, a <em>rozmiar<\/em> \u2014 ich rozmiar.<br \/>\nNast\u0119pnie nale\u017cy zaktualizowa\u0107 wska\u017anik do miejsca zapisu, aby ponownie wskazywa\u0142 na pocz\u0105tek wolnego obszaru w buforze:<\/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>Je\u015bli wymagana jest utworzenie wiadomo\u015bci z ju\u017c istniej\u0105cego bufora, bez kopiowania, u\u017cywa si\u0119 do tego funkcji:<\/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>Funkcja po utworzeniu wiadomo\u015bci i struktury bloku danych ustawi jej wska\u017aniki na dane pod adresem <em>buf<\/em>. Tzn. w tym przypadku bufor danych nie znajduje si\u0119 tu\u017c za polami nag\u0142\u00f3wka bloku danych, jak mia\u0142o to miejsce przy tworzeniu bloku danych przez funkcj\u0119. <em>datab_alloc()<\/em>Przekazany do funkcji bufor danych pozostanie w swojej pierwotnej lokalizacji, ale dzi\u0119ki wska\u017anikom zostanie skierowany do w\u0142a\u015bnie utworzonego nag\u0142\u00f3wka bloku danych, a ten z kolei do wiadomo\u015bci.<\/p>\n<p><\/p>\n<p>Do jednej wiadomo\u015bci <em>mblk_t<\/em> mo\u017ce by\u0107 do\u0142\u0105czonych kilka blok\u00f3w danych. Robi si\u0119 to za pomoc\u0105 funkcji:<\/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> \u2014 wiadomo\u015b\u0107, do kt\u00f3rej zostanie dodany kolejny blok danych;<br \/>\n<em>data<\/em> \u2014 wska\u017anik na blok, kopia kt\u00f3rego zostanie dodana do wiadomo\u015bci;<br \/>\n<em>rozmiar<\/em> \u2014 rozmiar danych;<br \/>\n<em>pad<\/em> \u2014 flaga wskazuj\u0105ca, czy rozmiar przydzielanej pami\u0119ci ma by\u0107 wyr\u00f3wnany do granicy 4 bajt\u00f3w (uzupe\u0142nienie b\u0119dzie wykonane zerami). <\/p>\n<p><\/p>\n<p>Je\u017celi w istniej\u0105cym buforze danych wiadomo\u015bci jest wystarczaj\u0105co miejsca, nowe dane zostan\u0105 do\u0142\u0105czone za danymi, kt\u00f3re ju\u017c si\u0119 tam znajduj\u0105. Je\u015bli w buforze danych wiadomo\u015bci zwolnionego miejsca jest mniej ni\u017c <em>rozmiar<\/em>, tworzona jest nowa wiadomo\u015b\u0107 z wystarczaj\u0105cym rozmiarem bufora, a dane s\u0105 kopiowane do jej bufora. Ta nowa wiadomo\u015b\u0107 jest do\u0142\u0105czana do oryginalnej za pomoc\u0105 wska\u017anika <em>b_cont<\/em>. W takim przypadku wiadomo\u015b\u0107 przekszta\u0142ca si\u0119 w krotk\u0119. <\/p>\n<p><\/p>\n<p>Je\u017celi do krotki wymaga si\u0119 do\u0142\u0105czenia kolejnego bloku danych, nale\u017cy u\u017cy\u0107 funkcji:<\/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>ona odszuka ostatni\u0105 wiadomo\u015b\u0107 w krotce (ma ona <em>b_cont<\/em> b\u0119dzie zerowa) i wywo\u0142a dla tej wiadomo\u015bci funkcj\u0119 <em>appendb()<\/em>.<\/p>\n<p><\/p>\n<p>Aby pozna\u0107 rozmiar danych w wiadomo\u015bci lub krotce, mo\u017cna u\u017cy\u0107 funkcji:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>ona przejdzie przez wszystkie wiadomo\u015bci krotki i zwr\u00f3ci ca\u0142kowit\u0105 ilo\u015b\u0107 danych w buforach danych tych wiadomo\u015bci. Dla ka\u017cdej wiadomo\u015bci liczba danych jest obliczana w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\"> mp-&gt;b_wptr - mp-&gt;b_rptr<\/code><\/pre>\n<p><\/p>\n<p>Aby po\u0142\u0105czy\u0107 dwie krotki, stosuje si\u0119 funkcj\u0119:<\/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>ona do\u0142\u0105cza krotk\u0119 <em>newm<\/em> na ko\u0144cu krotki <em>mp<\/em> i zwraca wska\u017anik do ostatniej wiadomo\u015bci otrzymanej krotki.<\/p>\n<p><\/p>\n<p>W razie potrzeby, krotk\u0119 mo\u017cna przekszta\u0142ci\u0107 w jedn\u0105 wiadomo\u015b\u0107 z pojedynczym blokiem danych, robi si\u0119 to za pomoc\u0105 funkcji:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,int len);<\/code><\/pre>\n<p><\/p>\n<p>je\u017celi argument <em>len<\/em> jest r\u00f3wny -1, to rozmiar przydzielanego bufora okre\u015blany jest automatycznie. Je\u015bli <em>len<\/em> Je\u015bli jest to liczba dodatnia, utworzony zostanie bufor o tym rozmiarze, do kt\u00f3rego skopiowane zostan\u0105 dane wiadomo\u015bci krotki. Je\u015bli bufor si\u0119 zape\u0142ni, kopiowanie zostanie przerwane. Pierwsza wiadomo\u015b\u0107 krotki otrzyma bufor nowego rozmiaru z skopiowanymi danymi. Pozosta\u0142e wiadomo\u015bci zostan\u0105 usuni\u0119te, a pami\u0119\u0107 zwr\u00f3cona do sterty.<\/p>\n<p><\/p>\n<p>Podczas usuwania struktury <em>mblk_t<\/em> uwzgl\u0119dniany jest licznik referencji bloku danych, je\u015bli podczas wywo\u0142ywania <em>freeb()<\/em> wynosi zero, to bufor danych zostaje usuni\u0119ty wraz z instancj\u0105 <em>mblk_t<\/em>, na kt\u00f3r\u0105 wskazuje.<\/p>\n<p><\/p>\n<p>Inicjalizacja p\u00f3l nowej wiadomo\u015bci:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void mblk_init(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Dodanie do wiadomo\u015bci kolejnej porcji danych: <\/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>Je\u015bli nowe dane nie mieszcz\u0105 si\u0119 w wolnym miejscu bufora danych wiadomo\u015bci, do wiadomo\u015bci do\u0142\u0105czana jest osobno utworzona wiadomo\u015b\u0107 z buforem odpowiedniego rozmiaru (w pierwszej wiadomo\u015bci ustawiany jest wska\u017anik na dodan\u0105 wiadomo\u015b\u0107), co przekszta\u0142ca wiadomo\u015b\u0107 w krotk\u0119.<\/p>\n<p><\/p>\n<p>Dodanie porcji danych do krotki:<\/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>Funkcja wywo\u0142uje appendb() w p\u0119tli.<\/p>\n<p><\/p>\n<p>Scalanie dw\u00f3ch krotek w jedn\u0105:<\/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>Komunikat <em>newm<\/em> zostanie do\u0142\u0105czone do <em>mp<\/em>.<\/p>\n<p><\/p>\n<p>Utworzenie kopii pojedynczej wiadomo\u015bci:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copyb(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Pe\u0142ne skopiowanie krotki ze wszystkimi blokami danych:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copymsg(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Elementy krotki s\u0105 kopiowane przez funkcj\u0119 <em>copyb()<\/em>.<\/p>\n<p><\/p>\n<p>Utworzenie lekkiej kopii wiadomo\u015bci <em>mblk_t<\/em>. Przy tym blok danych nie jest kopiowany, lecz zwi\u0119ksza si\u0119 licznik jego referencji <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>Utworzenie lekkiej kopii krotki. Bloki danych nie s\u0105 kopiowane, tylko zwi\u0119kszaj\u0105 si\u0119 ich liczniki referencji <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>Scalenie wszystkich wiadomo\u015bci krotki w jedn\u0105 wiadomo\u015b\u0107:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,size_t len);<\/code><\/pre>\n<p><\/p>\n<p>Je\u015bli argument <em>len<\/em> wynosi -1, to rozmiar wydzielanego bufora okre\u015blany jest automatycznie.<\/p>\n<p><\/p>\n<p>Usuni\u0119cie wiadomo\u015bci, krotki:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void freemsg(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Licznik referencji bloku danych zmniejsza si\u0119 o jeden. Je\u015bli osi\u0105gnie zero, blok danych r\u00f3wnie\u017c zostaje usuni\u0119ty.<\/p>\n<p><\/p>\n<p>Policzenie ca\u0142kowitej obj\u0119to\u015bci danych w wiadomo\u015bci lub krotce.<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">size_t msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Wyci\u0105gni\u0119cie wiadomo\u015bci z ko\u0144ca kolejki:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *ms_queue_peek_last (q);<\/code><\/pre>\n<p><\/p>\n<p>Kopiowanie zawarto\u015bci zarezerwowanych p\u00f3l jednej wiadomo\u015bci do innej wiadomo\u015bci (w rzeczywisto\u015bci w tych polach znajduj\u0105 si\u0119 flagi u\u017cywane przez strumie\u0144 medi\u00f3w):<\/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\">Kolejka <em>queue_t<\/em><\/h3>\n<p><\/p>\n<p>Kolejka wiadomo\u015bci w mediastreamerze jest zrealizowana jako okr\u0105g\u0142a lista dwukierunkowa. Ka\u017cdy element listy zawiera wska\u017anik na blok danych z pomiarami sygna\u0142u. Oznacza to, \u017ce w kolejce poruszaj\u0105 si\u0119 jedynie wska\u017aniki do bloku danych, podczas gdy same dane pozostaj\u0105 nieruchome. Tzn. poruszaj\u0105 si\u0119 tylko odno\u015bniki do nich.<br \/>\nStruktura opisuj\u0105ca kolejk\u0119 <em>queue_t<\/em>, pokazana poni\u017cej:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _queue\n{\n   mblk_t _q_stopper; \/* \"Pusty\" element kolejki, nie wskazuje na dane, u\u017cywany tylko do zarz\u0105dzania kolejk\u0105. Podczas inicjalizacji kolejki (qinit()) jego wska\u017aniki s\u0105 ustawiane tak, aby wskazywa\u0142y na niego samego. *\/\n   int q_mcount;        \/\/ Liczba element\u00f3w w kolejce.\n} queue_t;<\/code><\/pre>\n<p><\/p>\n<p>Struktura zawiera pole \u2014 wska\u017anik <em>_q_stopper<\/em> typu *mblk_t, wskazuje na pierwszy element (wiadomo\u015b\u0107) w kolejce. Drugie pole struktury to licznik wiadomo\u015bci znajduj\u0105cych si\u0119 w kolejce.<br \/>\nNa ilustracji poni\u017cej przedstawiona jest kolejka o nazwie q1, zawieraj\u0105ca 4 wiadomo\u015bci m1, m2, m3, m4.<br \/>\n<img decoding=\"async\" alt=\"Badamy silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 11\" src=\"\/wp-content\/uploads\/2020\/04\/07fc043861edf15e5eb40e49c8ec5e3e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNa nast\u0119pnej ilustracji przedstawiona jest kolejka o nazwie q1, zawieraj\u0105ca 4 wiadomo\u015bci m1, m2, m3, m4. Wiadomo\u015b\u0107 m2 jest g\u0142ow\u0105 krotki, do kt\u00f3rej prowadz\u0105 jeszcze dwie wiadomo\u015bci m2_1 i m2_2.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Badamy silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 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\">Funkcje pracy z kolejkami queue_t<\/h3>\n<p><\/p>\n<p>Inicjalizacja kolejki:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void qinit(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>Pole <em>_q_stopper<\/em> (dalej nazywany \"stoperem\") jest inicjowany przez funkcj\u0119 <em>mblk_init()<\/em>, jego wska\u017aniki do poprzedniego i nast\u0119pnego elementu s\u0105 ustawiane tak, aby wskazywa\u0142y na niego samego. Licznik element\u00f3w w kolejce jest zerowany.<\/p>\n<p><\/p>\n<p>Dodanie nowego elementu (wiadomo\u015bci):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void putq(queue_t *q, mblk_t *m);<\/code><\/pre>\n<p><\/p>\n<p>Nowy element <em>m<\/em> jest dodawany na koniec listy, wska\u017aniki elementu s\u0105 ustawiane tak, aby stopor stawa\u0142 si\u0119 jego nast\u0119pnym elementem, a on dla stopora poprzednim. Licznik element\u00f3w w kolejce jest inkrementowany.<\/p>\n<p><\/p>\n<p>Wyci\u0105ganie elementu z kolejki:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * getq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>wyci\u0105gane jest to wiadomo\u015b\u0107, kt\u00f3ra znajduje si\u0119 po stoporze, licznik element\u00f3w jest dekrementowany. Je\u015bli w kolejce, poza stoporem, nie ma \u017cadnych element\u00f3w, zwracane jest 0.<\/p>\n<p><\/p>\n<p>Wstawienie wiadomo\u015bci do kolejki:<\/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>Element <em>mp<\/em> jest wstawiany przed elementem <em>emp<\/em>. Je\u015bli <em>emp<\/em>=0, to wiadomo\u015b\u0107 jest dodawana do ko\u0144ca kolejki.<\/p>\n<p><\/p>\n<p>Wyci\u0105ganie wiadomo\u015bci z g\u0142owy kolejki:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void remq(queue_t *q, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>Licznik element\u00f3w jest dekrementowany.<\/p>\n<p><\/p>\n<p>Odczyt wska\u017anika na pierwszy element w kolejce:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * peekq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>Usuni\u0119cie wszystkich element\u00f3w z kolejki z usuni\u0119ciem samych element\u00f3w:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void flushq(queue_t *q, int how);<\/code><\/pre>\n<p><\/p>\n<p>Argument <em>how<\/em> nie jest u\u017cywane. Licznik element\u00f3w w kolejce zostaje ustawiony na zero.<\/p>\n<p><\/p>\n<p>Makro odczytu wska\u017anika na ostatni element kolejki:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * qlast(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>Przy pracy z kolejkami wiadomo\u015bci nale\u017cy pami\u0119ta\u0107, \u017ce wywo\u0142anie <em>ms_queue_put(q, m)<\/em> z zerowym wska\u017anikiem na wiadomo\u015b\u0107 powoduje zap\u0119tlenie. Twoja aplikacja zawiesi si\u0119. Podobne zachowanie ma <em>ms_queue_next(q, m)<\/em>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-filtrov\">Po\u0142\u0105czenie filtr\u00f3w<\/h3>\n<p><\/p>\n<p>Opisana powy\u017cej kolejka jest u\u017cywana do przesy\u0142ania wiadomo\u015bci od jednego filtra do drugiego lub od jednego do kilku filtr\u00f3w. Filtry i ich po\u0142\u0105czenia tworz\u0105 skierowany graf. Wej\u015bcie lub wyj\u015bcie filtra nazywamy og\u00f3lnym terminem \"pin\". Do opisu kolejno\u015bci po\u0142\u0105cze\u0144 filtr\u00f3w u\u017cywa si\u0119 w media streamerze poj\u0119cia \"punktu sygna\u0142owego\". Punkt sygna\u0142owy to struktura <em>_MSCPoint<\/em>, kt\u00f3ra zawiera wska\u017anik do filtra i numer jednego z jego pin\u00f3w, w zwi\u0105zku z tym opisuje po\u0142\u0105czenie jednego z wej\u015b\u0107 lub wyj\u015b\u0107 filtra. <\/p>\n<p><\/p>\n<h4 id=\"signalnaya-tochka-grafa-obrabotki-dannyh\">Punkt sygna\u0142owy grafu przetwarzania danych<\/h4>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSCPoint{\nstruct _MSFilter *filter; \/\/ Wska\u017anik do filtra mediastreamera.\nint pin; \/\/ Numer jednego z wej\u015b\u0107 lub wyj\u015b\u0107 filtra, tzn. pin.\n} MSCPoint;\n<\/code><\/pre>\n<p><\/p>\n<p>Piny filtr\u00f3w numerowane s\u0105 od zera. <\/p>\n<p><\/p>\n<p>Po\u0142\u0105czenie dw\u00f3ch pin\u00f3w za pomoc\u0105 kolejki wiadomo\u015bci opisuje struktura <em>_MSQueue<\/em>, kt\u00f3ra zawiera kolejk\u0119 wiadomo\u015bci oraz wska\u017aniki do dw\u00f3ch punkt\u00f3w sygna\u0142owych, kt\u00f3re \u0142\u0105czy:<\/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>Nazwijmy t\u0119 struktur\u0119 po\u0142\u0105czeniem sygna\u0142owym. Ka\u017cdy filtr mediastreamera zawiera tabel\u0119 po\u0142\u0105cze\u0144 wej\u015bciowych oraz tabel\u0119 po\u0142\u0105cze\u0144 wyj\u015bciowych (<em>MSQueue<\/em>). Rozmiar tabel jest ustalany podczas tworzenia filtra, co ju\u017c zrobili\u015bmy przy pomocy eksportowanej zmiennej typu <em>MSFilterDesc<\/em>, gdy opracowywali\u015bmy nasz w\u0142asny filtr. Poni\u017cej przedstawiona jest struktura opisuj\u0105ca ka\u017cdy filtr w mediastreamerze, <em>MSFilter<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">\nstruct _MSFilter{\n    MSFilterDesc *desc;    \n    \/* Wska\u017anik na deskryptor filtra. *\/\n    \/* Zabezpieczone atrybuty, ich nie mo\u017cna przesuwa\u0107 ani usuwa\u0107, w przeciwnym razie praca z wtyczkami b\u0119dzie naruszona. *\/\n    ms_mutex_t lock;      \/* Semafor. *\/\n    MSQueue **inputs;     \/* Tabela wej\u015bciowych \u0142\u0105czy. *\/\n    MSQueue **outputs;    \/* Tabela wyj\u015bciowych \u0142\u0105czy. *\/\n    struct _MSFactory *factory; \/* Wska\u017anik na fabryk\u0119, kt\u00f3ra stworzy\u0142a dany egzemplarz filtra. *\/\n    void *padding;              \/* Nie u\u017cywane, zostanie u\u017cyte, je\u015bli zostan\u0105 dodane zabezpieczone pola. *\/\n    void *data;                 \/* Wska\u017anik na dowoln\u0105 struktur\u0119 do przechowywania danych stanu wewn\u0119trznego filtra i oblicze\u0144 po\u015brednich. *\/\n    struct _MSTicker *ticker;   \/* Wska\u017anik na obiekt tickera, kt\u00f3ry nie powinien by\u0107 zerowy, gdy wywo\u0142ywana jest funkcja process(). *\/\n    \/*prywatne atrybuty, mog\u0105 by\u0107 przenoszone i zmieniane w ka\u017cdej chwili*\/\n    MSList *notify_callbacks; \/* Lista callback\u00f3w u\u017cywanych do przetwarzania zdarze\u0144 filtra. *\/\n    uint32_t last_tick;       \/* Numer ostatniego taktu, gdy wywo\u0142ywana by\u0142a funkcja process(). *\/\n    MSFilterStats *stats;     \/* Statystyka pracy filtra.*\/\n    int postponed_task; \/*Liczba od\u0142o\u017conych zada\u0144. Niekt\u00f3re filtry mog\u0105 odk\u0142ada\u0107 przetwarzanie danych (wywo\u0142anie process()) na kilka takt\u00f3w.*\/\n    bool_t seen;  \/* Flaga, kt\u00f3r\u0105 u\u017cywa ticker, aby oznaczy\u0107, \u017ce ten egzemplarz filtra zosta\u0142 ju\u017c obs\u0142u\u017cony w danym takcie.*\/\n};\ntypedef struct _MSFilter MSFilter;\n<\/code><\/pre>\n<p><\/p>\n<p>Po po\u0142\u0105czeniu filtr\u00f3w w programie C zgodnie z naszym zamys\u0142em (ale bez pod\u0142\u0105czenia tickera), stworzyli\u015bmy w ten spos\u00f3b graf skierowany, kt\u00f3rego w\u0119z\u0142y to egzemplarze struktury <em>MSFilter<\/em>, a kraw\u0119dzie to egzemplarze \u0142\u0105czy <em>MSQueue<\/em>. <\/p>\n<p><\/p>\n<h3 id=\"zakulisnaya-deyatelnost-tikera\">Kulisy dzia\u0142ania tickera<\/h3>\n<p><\/p>\n<p>Kiedy m\u00f3wi\u0142em, \u017ce ticker to filtr \u017ar\u00f3d\u0142owy takt\u00f3w, nie by\u0142a to ca\u0142a prawda o nim. Ticker to obiekt, kt\u00f3ry regularnie uruchamia funkcje <em>process()<\/em> wszystkich filtr\u00f3w schematu (grafu), do kt\u00f3rego jest pod\u0142\u0105czony. Kiedy w programie w C pod\u0142\u0105czamy tykacz do filtra grafu, pokazujemy tykaczowi graf, kt\u00f3rym od tego momentu b\u0119dzie zarz\u0105dza\u0107, dop\u00f3ki go nie od\u0142\u0105czymy. Po pod\u0142\u0105czeniu, tykacz zaczyna przeszukiwa\u0107 powierzony mu graf, sporz\u0105dzaj\u0105c list\u0119 filtr\u00f3w, kt\u00f3re wchodz\u0105 w jego sk\u0142ad. Aby nie \"policzy\u0107\" tego samego filtra dwa razy, oznacza on znalezione filtry, ustawiaj\u0105c w nich flag\u0119 <em>seen<\/em>. Przeszukiwanie odbywa si\u0119 po tabelach \u0142\u0105czy, kt\u00f3re ma ka\u017cdy filtr. <\/p>\n<p><\/p>\n<p>Podczas swojej wst\u0119pnej wizyty w grafie, ticker sprawdza, czy w\u015br\u00f3d filtr\u00f3w znajduje si\u0119 przynajmniej jeden, kt\u00f3ry pe\u0142ni rol\u0119 \u017ar\u00f3d\u0142a blok\u00f3w danych. Je\u015bli taki nie zostanie znaleziony, graf uznaje si\u0119 za niew\u0142a\u015bciwy, a ticker ko\u0144czy dzia\u0142anie awaryjnie.<\/p>\n<p><\/p>\n<p>Je\u015bli graf okaza\u0142 si\u0119 \"prawid\u0142owy\", dla ka\u017cdego znalezionego filtra, aby go zainicjowa\u0107, wywo\u0142ywana jest funkcja <em>preprocess()<\/em>. Gdy nadejdzie czas na kolejny krok przetwarzania (domy\u015blnie co 10 milisekund), ticker wywo\u0142uje funkcj\u0119 <em>process()<\/em> dla wszystkich wcze\u015bniej znalezionych filtr\u00f3w \u017ar\u00f3d\u0142owych, a nast\u0119pnie dla pozosta\u0142ych filtr\u00f3w z listy. Je\u015bli filtr ma wej\u015bciowe \u0142\u0105cza, uruchomienie funkcji <em>process()<\/em> powtarza si\u0119, dop\u00f3ki kolejki wej\u015bciowych link\u00f3w nie s\u0105 puste. Po tym przechodzi do nast\u0119pnego filtra na li\u015bcie i \"przewija\" go, a\u017c wej\u015bciowe linki b\u0119d\u0105 wolne od wiadomo\u015bci. Tykacz przechodzi od filtra do filtra, a\u017c lista si\u0119 sko\u0144czy. Na tym ko\u0144czy si\u0119 przetwarzanie taktu. <\/p>\n<p><\/p>\n<p>Teraz wr\u00f3cimy do krotek i porozmawiamy o tym, po co w mediastreamerze dodano taki byt. W og\u00f3lnym przypadku, obj\u0119to\u015b\u0107 danych, potrzebna algorytmowi dzia\u0142aj\u0105cemu w filtrze, nie zgadza si\u0119 i nie jest wielokrotno\u015bci\u0105 rozmiaru bufor\u00f3w danych wchodz\u0105cych. Na przyk\u0142ad piszemy filtr, kt\u00f3ry wykonuje szybkie przekszta\u0142cenie Fouriera, kt\u00f3re z definicji mo\u017ce przetwarza\u0107 tylko bloki danych, kt\u00f3rych rozmiar jest pot\u0119g\u0105 liczby dwa. Przyjmijmy, \u017ce to 512 pr\u00f3bek. Je\u015bli dane s\u0105 generowane przez kana\u0142 telefoniczny, to bufor danych ka\u017cdej wiadomo\u015bci na wej\u015bciu przynosi nam po 160 pr\u00f3bek sygna\u0142u. Jest pokusa, by nie zabiera\u0107 danych z wej\u015bcia, dop\u00f3ki nie uzbiera si\u0119 wymagana liczba danych. Ale w takim przypadku wyst\u0105pi kolizja z tickerem, kt\u00f3ry bezskutecznie b\u0119dzie pr\u00f3bowa\u0142 przewin\u0105\u0107 filtr do opr\u00f3\u017cnienia wej\u015bciowego \u0142\u0105cza. Wcze\u015bniej oznaczyli\u015bmy t\u0119 zasad\u0119 jako trzeci\u0105 zasad\u0119 dzia\u0142ania filtra. Zgodnie z t\u0105 zasad\u0105 funkcja process() filtra powinna pobra\u0107 wszystkie dane z kolejek wej\u015bciowych.<\/p>\n<p><\/p>\n<p>Opr\u00f3cz tego z wej\u015bcia nie mo\u017cna b\u0119dzie pobra\u0107 jedynie 512 pr\u00f3bek, poniewa\u017c mo\u017cna je pobiera\u0107 tylko w ca\u0142o\u015bci, tzn. filtr musi pobra\u0107 640 pr\u00f3bek, a wykorzystuj\u0105c 512 z nich, pozosta\u0142o\u015b\u0107 do zgromadzenia nowej porcji danych. W ten spos\u00f3b nasz filtr, opr\u00f3cz swojej g\u0142\u00f3wnej pracy, musi zapewni\u0107 dodatkowe dzia\u0142ania zwi\u0105zane z tymczasowym przechowywaniem danych wej\u015bciowych. Tw\u00f3rcy mediastreamera do rozwi\u0105zania tego wsp\u00f3lnego problemu opracowali specjalny obiekt \u2014 MSBufferizer (bufor), kt\u00f3ry rozwi\u0105zuje t\u0119 kwesti\u0119 za pomoc\u0105 krotek. <\/p>\n<p><\/p>\n<h3 id=\"buferizator-msbufferizer\">Bufor (MSBufferizer)<\/h3>\n<p><\/p>\n<p>Jest to obiekt, kt\u00f3ry b\u0119dzie gromadzi\u0142 dane wej\u015bciowe wewn\u0105trz filtra i zacznie je wydawa\u0107 do przetwarzania, gdy tylko ilo\u015b\u0107 informacji oka\u017ce si\u0119 wystarczaj\u0105ca do przeprowadzenia algorytmu filtra. Podczas gdy bufor gromadzi dane, filtr b\u0119dzie dzia\u0142a\u0142 w trybie ja\u0142owym, nie zu\u017cywaj\u0105c mocy obliczeniowej procesora. Ale gdy tylko funkcja odczytu z bufora zwr\u00f3ci warto\u015b\u0107 r\u00f3\u017cn\u0105 od zera, funkcja process() filtra zaczyna pobiera\u0107 i przetwarza\u0107 dane z bufora w porcjach odpowiedniego rozmiaru, a\u017c do ich wyczerpania.<br \/>\nNiepotrzebne na razie dane pozostaj\u0105 w buforze jako pierwszy element krotki, do kt\u00f3rej do\u0142\u0105czaj\u0105 nast\u0119pne bloki danych wej\u015bciowych. <\/p>\n<p><\/p>\n<p>Struktura opisuj\u0105ca bufor: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">struct _MSBufferizer{\nqueue_t q; \/* Kolejka komunikat\u00f3w. *\/\nint size; \/* \u0141\u0105czny rozmiar danych znajduj\u0105cych si\u0119 w buforze w danym momencie. *\/\n};\ntypedef struct _MSBufferizer MSBufferizer;<\/code><\/pre>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-msbufferizer\">Funkcje pracy z MSBufferizer<\/h3>\n<p><\/p>\n<p>Tworzenie nowej instancji bufora:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">MSBufferizer * ms_bufferizer_new(void);<\/code><\/pre>\n<p><\/p>\n<p>Przydzielana jest pami\u0119\u0107, inicjalizowany w <em>ms_bufferizer_init()<\/em> i zwracany jest wska\u017anik.<\/p>\n<p><\/p>\n<p>Funkcja inicjalizacji:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_init(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Inicjalizowana jest kolejka <em>q<\/em>, pole <em>rozmiar<\/em> ustawiane jest na zero.<\/p>\n<p><\/p>\n<p>Dodawanie komunikatu:<\/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>Komunikat m jest dodawany do kolejki. Obliczony rozmiar blok\u00f3w danych jest dodawany do <em>rozmiar<\/em>.<\/p>\n<p><\/p>\n<p>Zrzut wszystkich komunikat\u00f3w z kolejki danych \u0142\u0105cza do bufora <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>Przenoszenie komunikat\u00f3w z \u0142\u0105cza <em>q<\/em> do bufora odbywa si\u0119 za pomoc\u0105 funkcji <em>ms_bufferizer_put()<\/em>.<\/p>\n<p><\/p>\n<p>Odczyt z bufora:<\/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>Je\u015bli rozmiar zgromadzonych w buforze danych oka\u017ce si\u0119 mniejszy ni\u017c \u017c\u0105dany (<em>datalen<\/em>), funkcja zwraca zero, a kopiowanie danych do data nie jest wykonywane. W przeciwnym razie nast\u0119puje sekwencyjne kopiowanie danych z krotek znajduj\u0105cych si\u0119 w buforze. Po skopiowaniu krotka jest usuwana, a pami\u0119\u0107 zwalniana. Kopiowanie ko\u0144czy si\u0119 w momencie skopiowania datalen bajt\u00f3w. Je\u015bli miejsce ko\u0144czy si\u0119 w po\u0142owie bloku danych, w tym komunikacie blok danych jest skracany do pozosta\u0142ej, nie skopiowanej cz\u0119\u015bci. Przy nast\u0119pnym wywo\u0142aniu, kopiowanie b\u0119dzie kontynuowane od tego miejsca.<\/p>\n<p><\/p>\n<p>Odczyt liczby danych, kt\u00f3re s\u0105 dost\u0119pne w danym momencie w buforze:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_get_avail(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Zwraca pole <em>rozmiar<\/em> bufora.<\/p>\n<p><\/p>\n<p>Odrzucanie cz\u0119\u015bci danych znajduj\u0105cych si\u0119 w buforze:<\/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>Okre\u015blona liczba bajt\u00f3w danych jest ekstrakowana i odrzucana. Odrzucane s\u0105 najstarsze dane.<\/p>\n<p><\/p>\n<p>Usuni\u0119cie wszystkich wiadomo\u015bci znajduj\u0105cych si\u0119 w buforze:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_flush(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Licznik danych jest resetowany do zera.<\/p>\n<p><\/p>\n<p>Usuni\u0119cie wszystkich wiadomo\u015bci znajduj\u0105cych si\u0119 w buforze:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_uninit(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Resetowanie licznika nie jest wykonywane.<\/p>\n<p><\/p>\n<p>Usuni\u0119cie buforu i zwolnienie pami\u0119ci:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_destroy(MSBufferizer *obj);  <\/code><\/pre>\n<p><\/p>\n<p>Przyk\u0142ady u\u017cycia buforu mo\u017cna znale\u017a\u0107 w kodzie \u017ar\u00f3d\u0142owym kilku filtr\u00f3w mediastreamera. Na przyk\u0142ad w filtrze MS_L16_ENC, kt\u00f3ry wykonuje przestawienie bajt\u00f3w w pomiarach z porz\u0105dku sieciowego do porz\u0105dku hosta:<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>W nast\u0119pnym artykule om\u00f3wimy kwesti\u0119 oceny obci\u0105\u017cenia na tickerze i sposoby radzenia sobie z nadmiernym obci\u0105\u017ceniem obliczeniowym w mediastreamerze.<\/p>\n<p>\u0179r\u00f3d\u0142o: <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 5.0.1.1 - 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.\" \/>\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\/pl\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\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.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/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\udd47Zag\u0142\u0119biamy si\u0119 w silnik VoIP Mediastreamer2. Cz\u0119\u015b\u0107 11 | ProHoster","description":"Materia\u0142 artyku\u0142u pochodzi z mojego kana\u0142u Dzen.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","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.","og:url":"https:\/\/prohoster.info\/pl\/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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/79540","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=79540"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/79540\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/79541"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=79540"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=79540"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=79540"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}