{"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\/cs\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","title":{"rendered":"Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 11","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Materi\u00e1l \u010dl\u00e1nku je p\u0159evzat z m\u00e9ho <noindex><a rel=\"nofollow\" href=\"https:\/\/zen.yandex.ru\/profile\/editor\/id\/5e3f8e0751f5346faab4fc7a\">zenov\u00e9ho kan\u00e1lu<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 11\" src=\"\/wp-content\/uploads\/2020\/04\/b66fab43eb45c626c50f0383dcfa7cca.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mechanismus p\u0159enosu dat<\/p>\n<p><\/p>\n<ul>\n<li>Datov\u00fd blok dblk_t<\/li>\n<li>Zpr\u00e1va mblk_t<\/li>\n<li>Funkce pro pr\u00e1ci se zpr\u00e1vami mblk_t<\/li>\n<li>Fronta queue_t<\/li>\n<li>Funkce pro pr\u00e1ci s frontami queue_t<\/li>\n<li>Spojen\u00ed filtr\u016f<\/li>\n<li>Sign\u00e1ln\u00ed bod grafu zpracov\u00e1n\u00ed dat<\/li>\n<li>Pozad\u00ed \u010dinnosti tickeru<\/li>\n<li>Bufferiz\u00e1tor (MSBufferizer)<\/li>\n<li>Funkce pro pr\u00e1ci s MSBufferizer<\/li>\n<\/ul>\n<p><\/p>\n<p>V minulosti <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/497932\/\">\u010dl\u00e1nku<\/a><\/noindex> Vytvo\u0159ili jsme si vlastn\u00ed filtr. Tento \u010dl\u00e1nek v\u011bnujeme uspo\u0159\u00e1d\u00e1n\u00ed vnit\u0159n\u00edho mechanismu p\u0159enosu dat mezi filtry mediastreameru. To umo\u017en\u00ed psan\u00ed sofistikovan\u00fdch filtr\u016f s men\u0161\u00edmi n\u00e1roky.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2 id=\"mehanizm-peremescheniya-dannyh\">Mechanismus p\u0159enosu dat<\/h2>\n<p><\/p>\n<p>P\u0159en\u00e1\u0161en\u00ed dat v mediastreameru se prov\u00e1d\u00ed pomoc\u00ed front popsan\u00fdch strukturou <em>queue_t<\/em>. P\u0159es fronty se p\u0159en\u00e1\u0161ej\u00ed s\u00e9rie zpr\u00e1v typu <em>mblk_t<\/em>, kter\u00e9 samy o sob\u011b neobsahuj\u00ed data sign\u00e1lu, ale pouze odkazy na p\u0159edchoz\u00ed, n\u00e1sleduj\u00edc\u00ed zpr\u00e1vu a na datov\u00fd blok. Krom\u011b toho bych r\u00e1d zd\u016fraznil, \u017ee existuje tak\u00e9 pole pro odkaz na zpr\u00e1vu stejn\u00e9ho typu, kter\u00e9 umo\u017e\u0148uje organizovat jednosm\u011brn\u00fd seznam zpr\u00e1v. Skupinu zpr\u00e1v, spojen\u00fdch takov\u00fdm seznamem, budeme naz\u00fdvat tuple. Tak\u017ee jak\u00fdkoliv prvek fronty m\u016f\u017ee b\u00fdt jednotlivou zpr\u00e1vou <em>mblk_t<\/em>, nebo m\u016f\u017ee b\u00fdt hlavou skupiny zpr\u00e1v <em>mblk_t<\/em>. Ka\u017ed\u00e1 zpr\u00e1va tuple m\u016f\u017ee m\u00edt sv\u016fj vlastn\u00ed datov\u00fd blok. O d\u016fvodech existence tuple budeme hovo\u0159it pozd\u011bji.<\/p>\n<p><\/p>\n<p>Jak bylo \u0159e\u010deno v\u00fd\u0161e, samotn\u00e1 zpr\u00e1va neobsahuje blok dat, m\u00edsto toho obsahuje pouze ukazatel na oblast pam\u011bti, kde je blok ulo\u017een. V t\u00e9to \u010d\u00e1sti celkov\u00fd obraz pr\u00e1ce mediastreameru p\u0159ipom\u00edn\u00e1 sklad dve\u0159\u00ed z animovan\u00e9ho filmu \"Jak vycvi\u010dit draka\", kde se dve\u0159e (odkazy na data \u2014 m\u00edstnosti) \u0161\u00edlenou rychlost\u00ed pohybuj\u00ed po visut\u00fdch p\u00e1sech, p\u0159i\u010dem\u017e samotn\u00e9 m\u00edstnosti z\u016fst\u00e1vaj\u00ed nehybn\u00e9. <\/p>\n<p><\/p>\n<p>Nyn\u00ed, pohybuj\u00edc\u00ed se hierarchi\u00ed zdola nahoru, pod\u00edvejme se podrobn\u011b na uveden\u00e9 entity mechanismu p\u0159enosu dat v mediastreameru.<\/p>\n<p><\/p>\n<h3 id=\"blok--dannyh-dblk_t\">Datov\u00fd blok <em>dblk_t<\/em><\/h3>\n<p><\/p>\n<p>Datov\u00fd blok se skl\u00e1d\u00e1 z hlavi\u010dky a datov\u00e9ho bufferu. Hlavi\u010dka je pops\u00e1na n\u00e1sleduj\u00edc\u00ed strukturou,<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct datab\n{\nunsigned char *db_base; \/\/ Ukazatel na za\u010d\u00e1tek datov\u00e9ho bufferu.\nunsigned char *db_lim;  \/\/ Ukazatel na konec datov\u00e9ho bufferu.\nvoid (*db_freefn)(void*); \/\/ Funkce pro uvoln\u011bn\u00ed pam\u011bti p\u0159i maz\u00e1n\u00ed bloku.\nint db_ref; \/\/ Po\u010d\u00edtadlo odkaz\u016f.\n} dblk_t;<\/code><\/pre>\n<p><\/p>\n<p>Pole struktury obsahuj\u00ed ukazatele na za\u010d\u00e1tek vyrovn\u00e1vac\u00ed pam\u011bti, konec vyrovn\u00e1vac\u00ed pam\u011bti a funkci pro odstran\u011bn\u00ed datov\u00e9 vyrovn\u00e1vac\u00ed pam\u011bti. Posledn\u00ed prvek v hlavi\u010dce <em>db_ref<\/em> \u2014 po\u010det odkaz\u016f, pokud dos\u00e1hne nuly, slou\u017e\u00ed jako sign\u00e1l k odstran\u011bn\u00ed tohoto bloku z pam\u011bti. Pokud byl blok dat vytvo\u0159en funkc\u00ed <em>datab_alloc() <\/em>, pak bude datov\u00e1 vyrovn\u00e1vac\u00ed pam\u011b\u0165 um\u00edst\u011bna v pam\u011bti p\u0159\u00edmo za hlavi\u010dkou. Ve v\u0161ech ostatn\u00edch p\u0159\u00edpadech m\u016f\u017ee b\u00fdt vyrovn\u00e1vac\u00ed pam\u011b\u0165 um\u00edst\u011bna n\u011bkde jinde. Ve vyrovn\u00e1vac\u00ed pam\u011bti budou um\u00edst\u011bny sign\u00e1ln\u00ed vzorky nebo jin\u00e1 data, kter\u00e1 chceme zpracov\u00e1vat pomoc\u00ed filtr\u016f.<\/p>\n<p><\/p>\n<p>Nov\u00fd blok dat je vytvo\u0159en pomoc\u00ed funkce:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">dblk_t *datab_alloc(int size);<\/code><\/pre>\n<p><\/p>\n<p>Jako vstupn\u00ed parametr je j\u00ed p\u0159ed\u00e1v\u00e1n velikost dat, kter\u00e1 bude blok uchov\u00e1vat. Pam\u011b\u0165 je alokov\u00e1na v\u011bt\u0161\u00ed, aby na za\u010d\u00e1tku alokovan\u00e9 pam\u011bti bylo mo\u017en\u00e9 um\u00edstit hlavi\u010dku \u2014 strukturu <em>datab<\/em>. P\u0159i pou\u017eit\u00ed jin\u00fdch funkc\u00ed to v\u0161ak tak v\u017edy nefunguje, v n\u011bkter\u00fdch p\u0159\u00edpadech m\u016f\u017ee b\u00fdt datov\u00e1 vyrovn\u00e1vac\u00ed pam\u011b\u0165 um\u00edst\u011bna odd\u011blen\u011b od hlavi\u010dky bloku dat. Pole struktury jsou p\u0159i vytv\u00e1\u0159en\u00ed nastavena tak, aby jej\u00ed pole <em>db_base<\/em> ukazovalo na za\u010d\u00e1tek oblasti dat, a <em>db_lim<\/em> na jej\u00ed konec. Po\u010d\u00edtadlo odkaz\u016f <em>db_ref<\/em> se nastavuje na jednotku. Ukazatel na funkci pro vy\u010di\u0161t\u011bn\u00ed dat se nastavuje na nulu.<\/p>\n<p><\/p>\n<h3 id=\"soobschenie-mblk_t\">Zpr\u00e1va <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Jak bylo \u0159e\u010deno, prvky fronty maj\u00ed typ <em>mblk_t,<\/em> je definov\u00e1n n\u00e1sledovn\u011b:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct msgb\n{\n  struct msgb *b_prev;   \/\/ Ukazatel na p\u0159edchoz\u00ed prvek seznamu.\n  struct msgb *b_next;   \/\/ Ukazatel na n\u00e1sleduj\u00edc\u00ed prvek seznamu.\n  struct msgb *b_cont;   \/\/ Ukazatel pro podlepov\u00e1n\u00ed k zpr\u00e1v\u011b jin\u00fdch zpr\u00e1v, za \u00fa\u010delem vytvo\u0159en\u00ed krou\u017eku zpr\u00e1v.\n  struct datab *b_datap; \/\/ Ukazatel na strukturu bloku dat.\n  unsigned char *b_rptr; \/\/ Ukazatel na za\u010d\u00e1tek oblasti dat pro \u010dten\u00ed dat vyrovn\u00e1vac\u00ed pam\u011bti b_datap.\n  unsigned char *b_wptr; \/\/ Ukazatel na za\u010d\u00e1tek oblasti dat pro z\u00e1pis dat vyrovn\u00e1vac\u00ed pam\u011bti b_datap.\n  uint32_t reserved1;    \/\/ Rezervovan\u00e9 pole1, mediastreamer ukl\u00e1d\u00e1 informace o slu\u017eb\u011b tam. \n  uint32_t reserved2;    \/\/ Rezervovan\u00e9 pole2, mediastreamer ukl\u00e1d\u00e1 informace o slu\u017eb\u011b tam.\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 za\u010d\u00e1tku obsahuje ukazatele <em>b_prev<\/em>, <em>b_next<\/em>, kter\u00e9 jsou nezbytn\u00e9 pro organizaci obousm\u011brn\u00e9ho seznamu (kter\u00fdm je fronta <em>queue_t<\/em>). <\/p>\n<p><\/p>\n<p>N\u00e1sleduje ukazatel <em>b_cont<\/em>, kter\u00fd se pou\u017e\u00edv\u00e1 pouze tehdy, kdy\u017e zpr\u00e1va vstupuje do tuple. Pro posledn\u00ed zpr\u00e1vu v tuplu tento ukazatel z\u016fst\u00e1v\u00e1 nulov\u00fd. <\/p>\n<p><\/p>\n<p>D\u00e1le vid\u00edme ukazatel na datov\u00fd blok <em>b_datap<\/em>, pro kter\u00fd existuje zpr\u00e1va. N\u00e1sleduj\u00ed ukazatele na oblast uvnit\u0159 datov\u00e9ho bufferu bloku. Pole <em>b_rptr<\/em> ukazuje m\u00edsto, odkud se budou \u010d\u00edst data z bufferu. Pole <em>b_wptr<\/em> ukazuje m\u00edsto, odkud se budou zapisovat data do bufferu. <\/p>\n<p><\/p>\n<p>Zbyl\u00e1 pole maj\u00ed servisn\u00ed charakter a nejsou relevantn\u00ed pro fungov\u00e1n\u00ed mechanismu p\u0159enosu dat.<\/p>\n<p><\/p>\n<p>N\u00ed\u017ee je zn\u00e1zorn\u011bna jednotliv\u00e1 zpr\u00e1va s n\u00e1zvem <em>m1<\/em> a datov\u00fdm blokem <em>d1<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 11\" src=\"\/wp-content\/uploads\/2020\/04\/e6a726b13491832258c1228f03bde802.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNa n\u00e1sleduj\u00edc\u00edm obr\u00e1zku je zobrazen tuple ze t\u0159\u00ed zpr\u00e1v <em>m1<\/em>, <em>m1_1<\/em>, <em>m1_2<\/em>.<br \/>\n<img decoding=\"async\" alt=\"Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 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\">Funkce pro pr\u00e1ci se zpr\u00e1vami <em>mblk_t<\/em><\/h3>\n<p><\/p>\n<p>Nov\u00e1 zpr\u00e1va <em>mblk_t<\/em> je vytv\u00e1\u0159ena funkc\u00ed:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *allocb(int size, int pri); <\/code><\/pre>\n<p><\/p>\n<p>ta alokuje v pam\u011bti novou zpr\u00e1vu <em>mblk_t<\/em> s datov\u00fdm blokem dan\u00e9 velikosti <em>size<\/em>, druh\u00fd argument \u2014 <em>pri<\/em> se ve zva\u017eovan\u00e9 verzi knihovny nepou\u017e\u00edv\u00e1. M\u011bl by z\u016fstat nulov\u00fd. B\u011bhem fungov\u00e1n\u00ed funkce bude alokov\u00e1na pam\u011b\u0165 pro strukturu nov\u00e9 zpr\u00e1vy a bude vyvol\u00e1na funkce <em>mblk_init()<\/em>, kter\u00e1 nulov\u00e1 v\u0161echna pole vytvo\u0159en\u00e9ho instance struktury a pot\u00e9, s pomoc\u00ed v\u00fd\u0161e zm\u00edn\u011bn\u00e9ho, <em>datab_alloc()<\/em>vytvo\u0159\u00ed datov\u00fd buffer. Pot\u00e9 dojde k nastaven\u00ed pol\u00ed ve struktu\u0159e: <\/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 v\u00fdstupu z\u00edsk\u00e1me novou zpr\u00e1vu s inicializovan\u00fdmi poli a pr\u00e1zdn\u00fdm datov\u00fdm bufferem. Chcete-li do zpr\u00e1vy p\u0159idat data, je nutn\u00e9 prov\u00e9st jejich kopii do bufferu datov\u00e9ho bloku:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">memcpy(msg-&gt;b_rptr, data, size);<\/code><\/pre>\n<p><\/p>\n<p>kde <em>data<\/em> \u2014 ukazatel na zdroj dat, a <em>size<\/em> \u2014 jejich velikost.<br \/>\nPot\u00e9 je nutn\u00e9 aktualizovat ukazatel na m\u00edsto z\u00e1pisu, aby op\u011bt ukazoval na za\u010d\u00e1tek voln\u00e9ho m\u00edsta v bufferu:<\/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>Pokud je pot\u0159eba vytvo\u0159it zpr\u00e1vu z ji\u017e existuj\u00edc\u00edho bufferu, bez kop\u00edrov\u00e1n\u00ed, pak se k tomu pou\u017e\u00edv\u00e1 funkce:<\/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>Funkce po vytvo\u0159en\u00ed zpr\u00e1vy a struktury datov\u00e9ho bloku nastav\u00ed jej\u00ed ukazatele na \u00fadaje na adrese <em>buf<\/em>. T.j. v tomto p\u0159\u00edpad\u011b se datov\u00fd buffer nenach\u00e1z\u00ed za poli hlavi\u010dky datov\u00e9ho bloku, jak tomu bylo p\u0159i vytv\u00e1\u0159en\u00ed datov\u00e9ho bloku funkc\u00ed <em>datab_alloc()<\/em>P\u0159edan\u00e1 funkci buffer s daty z\u016fstane tam, kde byl, ale pomoc\u00ed ukazatel\u016f bude posunut k pr\u00e1v\u011b vytvo\u0159en\u00e9 hlavi\u010dce bloku dat, a ta samoz\u0159ejm\u011b k zpr\u00e1v\u011b.<\/p>\n<p><\/p>\n<p>K jedn\u00e9 zpr\u00e1v\u011b <em>mblk_t<\/em> mohou b\u00fdt postupn\u011b p\u0159ipojeny n\u011bkolik blok\u016f dat. To se prov\u00e1d\u00ed funkc\u00ed:<\/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 zpr\u00e1va, k kter\u00e9 bude p\u0159id\u00e1n dal\u0161\u00ed blok dat;<br \/>\n<em>data<\/em> \u2014 ukazatel na blok, jeho\u017e kopie bude p\u0159id\u00e1na do zpr\u00e1vy;<br \/>\n<em>size<\/em> \u2014 velikost dat;<br \/>\n<em>pad<\/em> \u2014 p\u0159\u00edznak, \u017ee velikost alokovan\u00e9 pam\u011bti by m\u011bla b\u00fdt zarovn\u00e1na na hranici 4 bajt\u016f (dopln\u011bn\u00ed bude provedeno nulami). <\/p>\n<p><\/p>\n<p>Pokud v existuj\u00edc\u00edm bufferu dat zpr\u00e1vy je dostatek m\u00edsta, nov\u00e9 \u00fadaje budou p\u0159ilepeny za ji\u017e existuj\u00edc\u00edmi tam daty. Pokud bude voln\u00e9ho m\u00edsta v bufferu dat zpr\u00e1vy m\u00e9n\u011b ne\u017e <em>size<\/em>, vytvo\u0159\u00ed se nov\u00e1 zpr\u00e1va s dostate\u010dnou velikost\u00ed bufferu a data se zkop\u00edruj\u00ed do jej\u00edho bufferu. Tato nov\u00e1 zpr\u00e1va se p\u0159ipoj\u00ed k p\u016fvodn\u00ed pomoc\u00ed ukazatele <em>b_cont<\/em>. V tomto p\u0159\u00edpad\u011b se zpr\u00e1va prom\u011bn\u00ed v tuple. <\/p>\n<p><\/p>\n<p>Pokud do tuple je t\u0159eba p\u0159idat dal\u0161\u00ed blok dat, je t\u0159eba pou\u017e\u00edt funkci:<\/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>ta najde posledn\u00ed zpr\u00e1vu v tuple (ta <em>b_cont<\/em> bude nulov\u00e1) a zavol\u00e1 pro tuto zpr\u00e1vu funkci <em>appendb()<\/em>.<\/p>\n<p><\/p>\n<p>Zjistit velikost dat ve zpr\u00e1v\u011b nebo tuple lze pomoc\u00ed funkce:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>ta projde v\u0161emi zpr\u00e1vami tuple a vr\u00e1t\u00ed celkov\u00fd po\u010det dat v buferch dat t\u011bchto zpr\u00e1v. Pro ka\u017edou zpr\u00e1vu se mno\u017estv\u00ed dat vypo\u010d\u00edt\u00e1v\u00e1 takto:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\"> mp-&gt;b_wptr - mp-&gt;b_rptr<\/code><\/pre>\n<p><\/p>\n<p>K sjednocen\u00ed dvou tuple se pou\u017e\u00edv\u00e1 funkce:<\/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>ta p\u0159ipoj\u00ed tuple <em>newm<\/em> na konec tuple <em>mp<\/em> a vr\u00e1t\u00ed ukazatel na posledn\u00ed zpr\u00e1vu vznikl\u00e9 tupl\u00e9.<\/p>\n<p><\/p>\n<p>Pokud je to nutn\u00e9, m\u016f\u017eeme tuple p\u0159ev\u00e9st na jednu zpr\u00e1vu s jednotn\u00fdm blokem dat, to se prov\u00e1d\u00ed funkc\u00ed:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,int len);<\/code><\/pre>\n<p><\/p>\n<p>pokud je argument <em>len<\/em> rovn\u00fd -1, pak je velikost vy\u010dlen\u011bn\u00e9ho bufferu ur\u010dena automaticky. Pokud <em>len<\/em> Pokud je \u010d\u00edslo kladn\u00e9, bude vytvo\u0159en vyrovn\u00e1vac\u00ed pam\u011b\u0165 t\u00e9to velikosti a do n\u011bj budou zkop\u00edrov\u00e1na data zpr\u00e1v tupl\u00edru. Pokud pam\u011b\u0165 skon\u010d\u00ed, kop\u00edrov\u00e1n\u00ed se zastav\u00ed. Prvn\u00ed zpr\u00e1va tupl\u00edru obdr\u017e\u00ed vyrovn\u00e1vac\u00ed pam\u011b\u0165 nov\u00e9 velikosti se zkop\u00edrovan\u00fdmi daty. Ostatn\u00ed zpr\u00e1vy budou odstran\u011bny a pam\u011b\u0165 vr\u00e1cena do haldy.<\/p>\n<p><\/p>\n<p>P\u0159i odstra\u0148ov\u00e1n\u00ed struktury <em>mblk_t<\/em> se zohled\u0148uje \u010d\u00edta\u010d referenc\u00ed bloku dat, pokud p\u0159i vyvol\u00e1n\u00ed <em>freeb()<\/em> bude rovno nule, pak bude datov\u00e1 pam\u011b\u0165 odstran\u011bna spolu s instanc\u00ed <em>mblk_t<\/em>, na kterou ukazuje.<\/p>\n<p><\/p>\n<p>Inicializace pol\u00ed nov\u00e9 zpr\u00e1vy:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void mblk_init(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>P\u0159id\u00e1n\u00ed dal\u0161\u00ed d\u00e1vky dat do zpr\u00e1vy: <\/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>Pokud nov\u00e1 data nezapadnou do voln\u00e9ho m\u00edsta vyrovn\u00e1vac\u00ed pam\u011bti zpr\u00e1vy, je k zpr\u00e1v\u011b p\u0159ipojena samostatn\u011b vytvo\u0159en\u00e1 zpr\u00e1va s vyrovn\u00e1vac\u00ed pam\u011bt\u00ed pot\u0159ebn\u00e9 velikosti (v prvn\u00ed zpr\u00e1v\u011b je nastavena ukazatel na p\u0159idanou zpr\u00e1vu) zpr\u00e1va se prom\u011bn\u00ed na tupl\u00edr.<\/p>\n<p><\/p>\n<p>P\u0159id\u00e1n\u00ed d\u00e1vky dat do tupl\u00edru:<\/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>Funkce vol\u00e1 appendb() v cyklu.<\/p>\n<p><\/p>\n<p>Slu\u010dov\u00e1n\u00ed dvou tupl\u00edr\u016f do jednoho:<\/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>Zpr\u00e1va <em>newm<\/em> bude p\u0159ipojeno k <em>mp<\/em>.<\/p>\n<p><\/p>\n<p>Vytvo\u0159en\u00ed kopie jednotliv\u00e9 zpr\u00e1vy:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copyb(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>\u00dapln\u00e9 kop\u00edrov\u00e1n\u00ed tupl\u00edru se v\u0161emi bloky dat:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *copymsg(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Prvky tupl\u00edru jsou p\u0159i tom kop\u00edrov\u00e1ny funkc\u00ed <em>copyb()<\/em>.<\/p>\n<p><\/p>\n<p>Vytvo\u0159en\u00ed lehk\u00e9 kopie zpr\u00e1vy <em>mblk_t<\/em>. P\u0159i tom nen\u00ed blok dat kop\u00edrov\u00e1n, ale zvy\u0161uje se \u010d\u00edta\u010d jeho referenc\u00ed <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>Vytvo\u0159en\u00ed lehk\u00e9 kopie tupl\u00edru. Bloky dat nejsou kop\u00edrov\u00e1ny, pouze se zvy\u0161uj\u00ed jejich \u010d\u00edta\u010de referenc\u00ed <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>Slu\u010den\u00ed v\u0161ech zpr\u00e1v tupl\u00edru do jedn\u00e9 zpr\u00e1vy:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void msgpullup(mblk_t *mp,size_t len);<\/code><\/pre>\n<p><\/p>\n<p>Pokud je argument <em>len<\/em> rovn\u00e1n -1, pak je velikost vyhrazen\u00e9 vyrovn\u00e1vac\u00ed pam\u011bti ur\u010dena automaticky.<\/p>\n<p><\/p>\n<p>Odstran\u011bn\u00ed zpr\u00e1vy, tupl\u00edru:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void freemsg(mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>\u010c\u00edta\u010d referenc\u00ed bloku dat se sni\u017euje o jednu. Pokud p\u0159itom dos\u00e1hne nuly, bloku dat je tak\u00e9 odstran\u011bn.<\/p>\n<p><\/p>\n<p>Po\u010d\u00edt\u00e1n\u00ed celkov\u00e9ho objemu dat ve zpr\u00e1v\u011b nebo tupl\u00edru.<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">size_t msgdsize(const mblk_t *mp);<\/code><\/pre>\n<p><\/p>\n<p>Extrakce zpr\u00e1vy z konce fronty:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t *ms_queue_peek_last (q);<\/code><\/pre>\n<p><\/p>\n<p>Kop\u00edrov\u00e1n\u00ed obsahu rezervovan\u00fdch pol\u00ed jedn\u00e9 zpr\u00e1vy do jin\u00e9 zpr\u00e1vy (ve skute\u010dnosti se v t\u011bchto pol\u00edch nach\u00e1zej\u00ed flagy, kter\u00e9 pou\u017e\u00edv\u00e1 mediastreamer):<\/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\">Fronta <em>queue_t<\/em><\/h3>\n<p><\/p>\n<p>Fronta zpr\u00e1v ve streamovac\u00edm m\u00e9diu je realizov\u00e1na jako kruhov\u00fd obousm\u011brn\u00fd seznam. Ka\u017ed\u00fd prvek seznamu obsahuje ukazatel na blok dat se z\u00e1znamy sign\u00e1lu. To znamen\u00e1, \u017ee se po front\u011b pohybuj\u00ed pouze ukazatele na blok dat, zat\u00edmco samotn\u00e1 data z\u016fst\u00e1vaj\u00ed na sv\u00e9m m\u00edst\u011b. Tedy se pohybuj\u00ed pouze odkazy na n\u011b.<br \/>\nStruktura popisuj\u00edc\u00ed frontu <em>queue_t<\/em>, je zn\u00e1zorn\u011bna n\u00ed\u017ee:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _queue\n{\n   mblk_t _q_stopper; \/* \"Pr\u00e1zdn\u00fd\" prvek fronty, neukazuje na data, pou\u017e\u00edv\u00e1 se pouze k \u0159\u00edzen\u00ed fronty. P\u0159i inicializaci fronty (qinit()) jsou jeho ukazatele nastaveny tak, aby ukazovaly na n\u011bj samotn\u00e9ho. *\/\n   int q_mcount;        \/\/ Po\u010det prvk\u016f ve front\u011b.\n} queue_t;<\/code><\/pre>\n<p><\/p>\n<p>Struktura obsahuje pole \u2013 ukazatel <em>_q_stopper<\/em> typu *mblk_t, ukazuje na prvn\u00ed prvek (zpr\u00e1vu) ve front\u011b. Druh\u00e9 pole struktury je \u010d\u00edta\u010d zpr\u00e1v, kter\u00e9 se nach\u00e1zej\u00ed ve front\u011b.<br \/>\nNa obr\u00e1zku n\u00ed\u017ee je zn\u00e1zorn\u011bna fronta s n\u00e1zvem q1, obsahuj\u00edc\u00ed 4 zpr\u00e1vy m1,m2, m3, m4.<br \/>\n<img decoding=\"async\" alt=\"Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 11\" src=\"\/wp-content\/uploads\/2020\/04\/07fc043861edf15e5eb40e49c8ec5e3e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNa n\u00e1sleduj\u00edc\u00edm obr\u00e1zku je zn\u00e1zorn\u011bna fronta s n\u00e1zvem q1, obsahuj\u00edc\u00ed 4 zpr\u00e1vy m1,m2, m3, m4. Zpr\u00e1va m2 je hlavou n-tice, do n\u00ed\u017e vedou je\u0161t\u011b dv\u011b zpr\u00e1vy m2_1 a m2_2.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 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\">Funkce pro pr\u00e1ci s frontami queue_t<\/h3>\n<p><\/p>\n<p>Inicializace fronty:<\/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> (d\u00e1le ho budeme naz\u00fdvat \"stopper\") je inicializov\u00e1n funkc\u00ed <em>mblk_init()<\/em>, jeho ukazatel p\u0159edchoz\u00edho prvku a n\u00e1sleduj\u00edc\u00edho prvku jsou nastaveny tak, aby ukazovaly na n\u011bj samotn\u00e9ho. \u010c\u00edta\u010d prvk\u016f fronty se nastavuje na nulu.<\/p>\n<p><\/p>\n<p>P\u0159id\u00e1n\u00ed nov\u00e9ho prvku (zpr\u00e1vy):<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void putq(queue_t *q, mblk_t *m);<\/code><\/pre>\n<p><\/p>\n<p>Nov\u00fd prvek <em>m<\/em> je p\u0159id\u00e1n na konec seznamu, ukazatele prvku se nastavuj\u00ed tak, aby se stopor stal pro n\u011bj dal\u0161\u00edm prvkem, a on pro stopor p\u0159edchoz\u00edm. \u010c\u00edta\u010d prvk\u016f fronty se inkrementuje.<\/p>\n<p><\/p>\n<p>Vyjmut\u00ed prvku z fronty:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * getq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>vyj\u00edm\u00e1 se ta zpr\u00e1va, kter\u00e1 stoj\u00ed za stoporem, \u010d\u00edta\u010d prvk\u016f se dekrementuje. Pokud ve front\u011b krom\u011b stoporu nejsou \u017e\u00e1dn\u00e9 prvky, vrac\u00ed se 0.<\/p>\n<p><\/p>\n<p>Vlo\u017een\u00ed zpr\u00e1vy do fronty:<\/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>Prvek <em>mp<\/em> je vlo\u017een p\u0159ed prvek <em>emp<\/em>. Pokud <em>emp<\/em>=0, tak\u017ee se zpr\u00e1va p\u0159id\u00e1 na konec fronty.<\/p>\n<p><\/p>\n<p>Vyjmut\u00ed zpr\u00e1vy z hlavy fronty:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void remq(queue_t *q, mblk_t *mp); <\/code><\/pre>\n<p><\/p>\n<p>\u010c\u00edta\u010d prvk\u016f se dekrementuje.<\/p>\n<p><\/p>\n<p>\u010cten\u00ed ukazatele na prvn\u00ed prvek ve front\u011b:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * peekq(queue_t *q); <\/code><\/pre>\n<p><\/p>\n<p>Odstran\u011bn\u00ed v\u0161ech prvk\u016f z fronty se odstran\u011bn\u00edm samotn\u00fdch prvk\u016f:<\/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>jak<\/em> nen\u00ed pou\u017e\u00edv\u00e1n. Po\u010d\u00edtadlo prvk\u016f fronty se nastav\u00ed na nulu.<\/p>\n<p><\/p>\n<p>Makro pro \u010dten\u00ed ukazatele na posledn\u00ed prvek fronty:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">mblk_t * qlast(queue_t *q);<\/code><\/pre>\n<p><\/p>\n<p>P\u0159i pr\u00e1ci s frontami zpr\u00e1v je t\u0159eba m\u00edt na pam\u011bti, \u017ee p\u0159i vol\u00e1n\u00ed <em>ms_queue_put(q, m)<\/em> s nulov\u00fdm ukazatelem na zpr\u00e1vu se funkce zacykl\u00ed. Va\u0161e aplikace se zasekne. Podobn\u011b se chov\u00e1 <em>ms_queue_next(q, m)<\/em>.<\/p>\n<p><\/p>\n<h3 id=\"soedinenie-filtrov\">Spojen\u00ed filtr\u016f<\/h3>\n<p><\/p>\n<p>Popisovan\u00e1 fronta se pou\u017e\u00edv\u00e1 k p\u0159en\u00e1\u0161en\u00ed zpr\u00e1v z jednoho filtru na druh\u00fd nebo z jednoho do n\u011bkolika filtr\u016f. Filtry a jejich spojen\u00ed vytv\u00e1\u0159ej\u00ed orientovan\u00fd graf. Vstup nebo v\u00fdstup filtru budeme naz\u00fdvat obecn\u00fdm term\u00ednem \"pin\". K popisu po\u0159ad\u00ed spojen\u00ed filtr\u016f mezi sebou se v mediastreameru pou\u017e\u00edv\u00e1 pojem \"sign\u00e1ln\u00ed bod\". Sign\u00e1ln\u00ed bod je struktura <em>_MSCPoint<\/em>, kter\u00e1 obsahuje ukazatel na filtr a \u010d\u00edslo jednoho z jeho pin\u016f, popisuje tedy propojen\u00ed jednoho z vchod\u016f nebo v\u00fdstup\u016f filtru. <\/p>\n<p><\/p>\n<h4 id=\"signalnaya-tochka-grafa-obrabotki-dannyh\">Sign\u00e1ln\u00ed bod grafu zpracov\u00e1n\u00ed dat<\/h4>\n<p><\/p>\n<pre><code class=\"cpp\">typedef struct _MSCPoint{\nstruct _MSFilter *filter; \/\/ Ukazatel na filtr mediastreameru.\nint pin;                        \/\/ \u010c\u00edslo jednoho z vchod\u016f nebo v\u00fdstup\u016f filtru, tj. pin.\n} MSCPoint;\n<\/code><\/pre>\n<p><\/p>\n<p>Piny filtr\u016f jsou \u010d\u00edslov\u00e1ny od nuly. <\/p>\n<p><\/p>\n<p>Propojen\u00ed dvou pin\u016f frontou zpr\u00e1v je pops\u00e1no strukturou <em>_MSQueue<\/em>, kter\u00e1 obsahuje frontu zpr\u00e1v a ukazatele na dva sign\u00e1ln\u00ed body, kter\u00e9 propojuje:<\/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>Budeme tuto strukturu naz\u00fdvat sign\u00e1ln\u00edm linkem. Ka\u017ed\u00fd filtr mediastreameru obsahuje tabulku vstupn\u00edch odkaz\u016f a tabulku v\u00fdstupn\u00edch odkaz\u016f (<em>MSQueue<\/em>). Velikost tabulek je nastavena p\u0159i vytv\u00e1\u0159en\u00ed filtru, co\u017e jsme ji\u017e ud\u011blali pomoc\u00ed exportovan\u00e9 prom\u011bnn\u00e9 typu <em>MSFilterDesc<\/em>, kdy\u017e jsme vyv\u00edjeli n\u00e1\u0161 vlastn\u00ed filtr. N\u00e1sleduje struktura popisuj\u00edc\u00ed jak\u00fdkoli filtr v mediastreameru, <em>MSFilter<\/em>:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">\nstruct _MSFilter{\n    MSFilterDesc *desc;    \n    \/* Ukazatel na popisova\u010d filtru. *\/\n    \/* Ochrann\u00e9 atributy, kter\u00e9 nelze p\u0159esunout nebo odstranit, jinak dojde k poru\u0161en\u00ed pr\u00e1ce s pluginy. *\/\n    ms_mutex_t lock;      \/* Semafor. *\/\n    MSQueue **inputs;     \/* Tabulka vstupn\u00edch odkaz\u016f. *\/\n    MSQueue **outputs;    \/* Tabulka v\u00fdstupn\u00edch odkaz\u016f. *\/\n    struct _MSFactory *factory; \/* Ukazatel na tov\u00e1rnu, kter\u00e1 vytvo\u0159ila tuto instanci filtru. *\/\n    void *padding;              \/* Nepou\u017e\u00edv\u00e1 se, bude vyu\u017eito, pokud budou p\u0159id\u00e1na chr\u00e1n\u011bn\u00e1 pole. *\/\n    void *data;                 \/* Ukazatel na libovolnou strukturu pro uchov\u00e1n\u00ed dat intern\u00edho stavu filtru a meziv\u00fdpo\u010dt\u016f. *\/\n    struct _MSTicker *ticker;   \/* Ukazatel na objekt tikera, kter\u00fd nesm\u00ed b\u00fdt nulov\u00fd, kdy\u017e je vol\u00e1na funkce process(). *\/\n    \/* soukrom\u00e9 atributy, mohou b\u00fdt kdykoli p\u0159esunuty a zm\u011bn\u011bny *\/\n    MSList *notify_callbacks; \/* Seznam zp\u011btn\u00fdch vol\u00e1n\u00ed, kter\u00e9 se pou\u017e\u00edvaj\u00ed k zpracov\u00e1n\u00ed ud\u00e1lost\u00ed filtru. *\/\n    uint32_t last_tick;       \/* \u010c\u00edslo posledn\u00edho cyklu, kdy byla vol\u00e1na funkce process(). *\/\n    MSFilterStats *stats;     \/* Statistiky pr\u00e1ce filtru. *\/\n    int postponed_task; \/* Po\u010det odlo\u017een\u00fdch \u00faloh. N\u011bkter\u00e9 filtry mohou odlo\u017eit zpracov\u00e1n\u00ed dat (vol\u00e1n\u00ed process()) na n\u011bkolik cykl\u016f. *\/\n    bool_t seen;  \/* Flag, kter\u00fd pou\u017e\u00edv\u00e1 tikker k ozna\u010den\u00ed, \u017ee tuto instanci filtru ji\u017e obslou\u017eil v tomto cyklu. *\/\n};\ntypedef struct _MSFilter MSFilter;\n<\/code><\/pre>\n<p><\/p>\n<p>Pot\u00e9, co jsme ve sv\u00e9 C-programu spojili filtry podle na\u0161eho z\u00e1m\u011bru (ale nep\u0159ipojili jsme tikker), jsme t\u00edm vytvo\u0159ili orientovanou grafovou strukturu, jej\u00ed\u017e uzly tvo\u0159\u00ed instance struktury <em>MSFilter<\/em>, a hrany to jsou instance odkaz\u016f <em>MSQueue<\/em>. <\/p>\n<p><\/p>\n<h3 id=\"zakulisnaya-deyatelnost-tikera\">Pozad\u00ed \u010dinnosti tickeru<\/h3>\n<p><\/p>\n<p>Kdy\u017e jsem v\u00e1m \u0159\u00edkal, \u017ee tikker je zdrojov\u00fd filtr taktu, nebyla to cel\u00e1 pravda o n\u011bm. Tikker je objekt, kter\u00fd pravideln\u011b spou\u0161t\u00ed funkce <em>process()<\/em> v\u0161ech filtr\u016f sch\u00e9matu (grafu), ke kter\u00e9mu je p\u0159ipojen. Kdy\u017e v programu v jazyce C p\u0159ipojujeme ticker k filtru grafu, ukazujeme tickeru graf, kter\u00fdm od tohoto okam\u017eiku bude spravovat, dokud ho neodpoj\u00edme. Po p\u0159ipojen\u00ed ticker za\u010dne zkoumat sv\u011b\u0159en\u00fd graf a sestavuje seznam filtr\u016f, do kter\u00fdch spad\u00e1. Aby \"nes\u010d\u00edtal\" stejn\u00fd filtr dvakr\u00e1t, ozna\u010duje nalezen\u00e9 filtry a nastavuje v nich p\u0159\u00edznak <em>seen<\/em>. Vyhled\u00e1v\u00e1n\u00ed se prov\u00e1d\u00ed v tabulk\u00e1ch odkaz\u016f, kter\u00e9 m\u00e1 ka\u017ed\u00fd filtr. <\/p>\n<p><\/p>\n<p>B\u011bhem sv\u00e9 \u00favodn\u00ed prohl\u00eddky grafu ticker kontroluje, zda je mezi filtry alespo\u0148 jeden, kter\u00fd funguje jako zdroj datov\u00fdch blok\u016f. Pokud takov\u00fd filtr nenajde, je graf pova\u017eov\u00e1n za neplatn\u00fd a ticker se neo\u010dek\u00e1van\u011b ukon\u010d\u00ed.<\/p>\n<p><\/p>\n<p>Pokud se uk\u00e1\u017ee, \u017ee graf je \"spr\u00e1vn\u00fd\", pro ka\u017ed\u00fd nalezen\u00fd filtr se pro inicializaci vol\u00e1 funkce <em>preprocess()<\/em>. Jakmile nastane \u010das pro dal\u0161\u00ed zpracovatelsk\u00fd cyklus (v\u00fdchoz\u00ed interval je ka\u017ed\u00fdch 10 milisekund), ticker vyvol\u00e1 funkci <em>process()<\/em> pro v\u0161echny d\u0159\u00edve nalezen\u00e9 zdrojov\u00e9 filtry a pot\u00e9 i pro ostatn\u00ed filtry seznamu. Pokud filtr m\u00e1 vstupn\u00ed linky, vyvol\u00e1n\u00ed funkce <em>process()<\/em> pokra\u010duje, dokud nejsou fronty vstupn\u00edch odkaz\u016f pr\u00e1zdn\u00e9. Pot\u00e9 p\u0159ech\u00e1z\u00ed k dal\u0161\u00edmu filtru v seznamu a \"proch\u00e1z\u00ed\" jej, dokud se vstupn\u00ed odkazy nezbav\u00ed zpr\u00e1v. Ticker p\u0159ech\u00e1z\u00ed z filtru na filtr, dokud se seznam neskon\u010d\u00ed. T\u00edmto kon\u010d\u00ed zpracov\u00e1n\u00ed cyklu. <\/p>\n<p><\/p>\n<p>Nyn\u00ed se vr\u00e1t\u00edme k n-tic\u00edm a probereme, pro\u010d byla do mediastreameru p\u0159id\u00e1na takov\u00e1 entita. Obecn\u011b plat\u00ed, \u017ee objem dat pot\u0159ebn\u00fd algoritmu, kter\u00fd pracuje uvnit\u0159 filtru, se neshoduje a nen\u00ed n\u00e1sobkem objemu dat p\u0159ich\u00e1zej\u00edc\u00edch na vstupu. Nap\u0159\u00edklad p\u00ed\u0161eme filtr, kter\u00fd prov\u00e1d\u00ed rychlou Fourierovu transformaci, je\u017e podle definice m\u016f\u017ee zpracov\u00e1vat pouze datov\u00e9 bloky, jejich\u017e velikost je mocnina dvou. P\u0159edpokl\u00e1dejme, \u017ee je to 512 vzork\u016f. Pokud jsou data generov\u00e1na telefonn\u00edm kan\u00e1lem, pak datov\u00fd buffer ka\u017ed\u00e9 zpr\u00e1vy na vstupu p\u0159in\u00e1\u0161\u00ed 160 vzork\u016f sign\u00e1lu. Existuje l\u00e1kav\u00e1 tendence neodeb\u00edrat data ze vstupu, dokud tam nebude pot\u0159ebn\u00e9 mno\u017estv\u00ed dat. V takov\u00e9m p\u0159\u00edpad\u011b v\u0161ak dojde ke kolizi s tickerem, kter\u00fd se bezv\u00fdsledn\u011b pokou\u0161\u00ed posunout filtr k vypr\u00e1zdn\u011bn\u00ed vstupn\u00edho linku. D\u0159\u00edve jsme toto pravidlo nazvali t\u0159et\u00edm principem fungov\u00e1n\u00ed filtru. Podle tohoto principu mus\u00ed funkce process() filtru odebrat v\u0161echna data z vstupn\u00edch front.<\/p>\n<p><\/p>\n<p>Krom\u011b toho nebude mo\u017en\u00e9 na vstupu z\u00edskat pouze 512 vzork\u016f, proto\u017ee je mo\u017en\u00e9 je br\u00e1t pouze po cel\u00fdch bloc\u00edch, tj. filtr bude muset vz\u00edt 640 vzork\u016f a pou\u017e\u00edt 512 z nich, zbytek a\u017e do na\u010dten\u00ed nov\u00e9 d\u00e1vky dat. T\u00edmto zp\u016fsobem mus\u00ed n\u00e1\u0161 filtr, krom\u011b sv\u00e9 hlavn\u00ed pr\u00e1ce, zajistit tak\u00e9 pomocn\u00e9 akce pro meziskladov\u00e1n\u00ed vstupn\u00edch dat. V\u00fdvoj\u00e1\u0159i mediastreameru pro \u0159e\u0161en\u00ed t\u00e9to obecn\u00e9 \u00falohy vyvinuli speci\u00e1ln\u00ed objekt \u2014 MSBufferizer (bufferiz\u00e1tor), kter\u00fd tuto \u00falohu \u0159e\u0161\u00ed pomoc\u00ed n-tic. <\/p>\n<p><\/p>\n<h3 id=\"buferizator-msbufferizer\">Bufferiz\u00e1tor (MSBufferizer)<\/h3>\n<p><\/p>\n<p>Tento objekt bude hromadit vstupn\u00ed data uvnit\u0159 filtru a za\u010dne je vyd\u00e1vat k zpracov\u00e1n\u00ed, jakmile se mno\u017estv\u00ed informac\u00ed uk\u00e1\u017ee jako dostate\u010dn\u00e9 pro proveden\u00ed algoritmu filtru. Zat\u00edmco bufferiz\u00e1tor shroma\u017e\u010fuje data, filtr bude pracovat v pr\u00e1zdn\u00e9m re\u017eimu, nevynalo\u017euje \u017e\u00e1dn\u00e9 v\u00fdpo\u010detn\u00ed zdroje procesoru. Ale jakmile funkce \u010dten\u00ed z bufferiz\u00e1toru vr\u00e1t\u00ed hodnotu jinou ne\u017e nula, funkce process() filtru za\u010dne br\u00e1t a zpracov\u00e1vat z bufferiz\u00e1toru data po d\u00e1vk\u00e1ch pot\u0159ebn\u00e9 velikosti, a\u017e do jejich vy\u010derp\u00e1n\u00ed.<br \/>\nDosud nevyu\u017eit\u00e1 data z\u016fst\u00e1vaj\u00ed v bufferiz\u00e1toru jako prvn\u00ed prvek n-tice, ke kter\u00e9mu se p\u0159ipojuj\u00ed n\u00e1sledn\u00e9 bloky vstupn\u00edch dat. <\/p>\n<p><\/p>\n<p>Struktura, kter\u00e1 popisuje bufferiz\u00e1tor: <\/p>\n<p><\/p>\n<pre><code class=\"cpp\">struct _MSBufferizer{\nqueue_t q; \/* Fronta zpr\u00e1v. *\/\nint size; \/* Celkov\u00e1 velikost dat, kter\u00e1 se pr\u00e1v\u011b nach\u00e1zej\u00ed v bufferiz\u00e1toru. *\/\n};\ntypedef struct _MSBufferizer MSBufferizer;<\/code><\/pre>\n<p><\/p>\n<h3 id=\"funkcii-raboty-s-msbufferizer\">Funkce pro pr\u00e1ci s MSBufferizer<\/h3>\n<p><\/p>\n<p>Vytvo\u0159en\u00ed nov\u00e9 instance bufferiz\u00e1toru:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">MSBufferizer * ms_bufferizer_new(void);<\/code><\/pre>\n<p><\/p>\n<p>Vyhrazuje se pam\u011b\u0165, inicializuje se v <em>ms_bufferizer_init()<\/em> a vrac\u00ed se ukazatel.<\/p>\n<p><\/p>\n<p>Funkce inicializace:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_init(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Inicializuje se fronta <em>q<\/em>, pole <em>size<\/em> nastavuje se na nulu.<\/p>\n<p><\/p>\n<p>P\u0159id\u00e1n\u00ed zpr\u00e1vy:<\/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>Zpr\u00e1va m se p\u0159id\u00e1 do fronty. Vypo\u010dten\u00e1 velikost blok\u016f dat se p\u0159id\u00e1 k <em>size<\/em>.<\/p>\n<p><\/p>\n<p>P\u0159esun do bufferiz\u00e1toru v\u0161ech zpr\u00e1v fronty datov\u00e9ho linku <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>P\u0159enos zpr\u00e1v z linky <em>q<\/em> do bufferiz\u00e1toru se prov\u00e1d\u00ed pomoc\u00ed funkce <em>ms_bufferizer_put()<\/em>.<\/p>\n<p><\/p>\n<p>\u010cten\u00ed z bufferiz\u00e1toru:<\/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>Pokud velikost akumulovan\u00fdch v bufferiz\u00e1toru dat je men\u0161\u00ed ne\u017e po\u017eadovan\u00e1 (<em>datalen<\/em>), funkce vr\u00e1t\u00ed nula, kop\u00edrov\u00e1n\u00ed dat do data se neprov\u00e1d\u00ed. Jinak se prov\u00e1d\u00ed sekven\u010dn\u00ed kop\u00edrov\u00e1n\u00ed dat z n-tic nach\u00e1zej\u00edc\u00edch se v bufferu. Po zkop\u00edrov\u00e1n\u00ed se n-tice vyma\u017ee a pam\u011b\u0165 se uvoln\u00ed. Kop\u00edrov\u00e1n\u00ed kon\u010d\u00ed v moment\u011b, kdy je zkop\u00edrov\u00e1no datalen bajt\u016f. Pokud m\u00edsto skon\u010d\u00ed uprost\u0159ed bloku dat, tento blok dat bude zkr\u00e1cen na zb\u00fdvaj\u00edc\u00ed zkop\u00edrovanou \u010d\u00e1st. P\u0159i dal\u0161\u00edm vol\u00e1n\u00ed bude kop\u00edrov\u00e1n\u00ed pokra\u010dovat od tohoto m\u00edsta.<\/p>\n<p><\/p>\n<p>\u010cten\u00ed po\u010dtu dat, kter\u00e1 jsou v tuto chv\u00edli dostupn\u00e1 v bufferu:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">int ms_bufferizer_get_avail(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Vr\u00e1t\u00ed pole <em>size<\/em> bufferiz\u00e9ru.<\/p>\n<p><\/p>\n<p>Zahod\u00ed \u010d\u00e1st dat, kter\u00e1 se nach\u00e1zej\u00ed v bufferu:<\/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>Uveden\u00e9 mno\u017estv\u00ed bajt\u016f dat se vyt\u00e1hne a zahod\u00ed. Zahazuje se nejstar\u0161\u00ed data.<\/p>\n<p><\/p>\n<p>Odstran\u011bn\u00ed v\u0161ech zpr\u00e1v nach\u00e1zej\u00edc\u00edch se v bufferu:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_flush(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>\u010c\u00edta\u010d dat se resetuje na nula.<\/p>\n<p><\/p>\n<p>Odstran\u011bn\u00ed v\u0161ech zpr\u00e1v nach\u00e1zej\u00edc\u00edch se v bufferu:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_uninit(MSBufferizer *obj); <\/code><\/pre>\n<p><\/p>\n<p>Resetov\u00e1n\u00ed \u010d\u00edta\u010de se neprov\u00e1d\u00ed.<\/p>\n<p><\/p>\n<p>Odstran\u011bn\u00ed bufferu a uvoln\u011bn\u00ed pam\u011bti:<\/p>\n<p><\/p>\n<pre><code class=\"cpp\">void ms_bufferizer_destroy(MSBufferizer *obj);  <\/code><\/pre>\n<p><\/p>\n<p>P\u0159\u00edklady pou\u017eit\u00ed bufferu lze nal\u00e9zt v zdrojov\u00e9m k\u00f3du n\u011bkolika filtr\u016f mediastreamera. Nap\u0159\u00edklad ve filtru MS_L16_ENC, kter\u00fd prov\u00e1d\u00ed p\u0159eskupen\u00ed bajt\u016f v m\u011b\u0159en\u00ed ze s\u00ed\u0165ov\u00e9ho po\u0159ad\u00ed do po\u0159ad\u00ed hostitele:<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>V n\u00e1sleduj\u00edc\u00edm \u010dl\u00e1nku se budeme zab\u00fdvat ot\u00e1zkou hodnocen\u00ed zat\u00ed\u017een\u00ed tickeru a zp\u016fsoby, jak se vypo\u0159\u00e1dat s nadm\u011brnou v\u00fdpo\u010detn\u00ed z\u00e1t\u011b\u017e\u00ed v mediastreameru.<\/p>\n<p>Zdroj: <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.3 - 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\/cs\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"cs_CZ\" \/>\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\/cs\/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\udd47Studujeme VoIP engine Mediastreamer2. \u010c\u00e1st 11 | ProHoster","description":"Materi\u00e1l \u010dl\u00e1nku poch\u00e1z\u00ed z m\u00e9ho Dzen kan\u00e1lu.","canonical_url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/izuchaem-voip-dvizhok-mediastreamer2-chast-11","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"cs_CZ","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\/cs\/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\/cs\/wp-json\/wp\/v2\/posts\/79540","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/comments?post=79540"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/79540\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media\/79541"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media?parent=79540"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/categories?post=79540"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/tags?post=79540"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}