Poznawanie silnika Mediastreamer2 VoIP. Część 11

Materiał artykułu pochodzi z mojego kanał zen.

Poznawanie silnika Mediastreamer2 VoIP. Część 11

Mechanizm przenoszenia danych

  • Blok danych dblk_t
  • Wiadomość mblk_t
  • Funkcje do pracy z wiadomościami mblk_t
  • Kolejka kolejka_t
  • Funkcje do pracy z kolejkami kolejka_t
  • Podłączanie filtrów
  • Punkt sygnałowy wykresu przetwarzania danych
  • Za kulisami działalności tickera
  • Bufor (MSBufferizer)
  • Funkcje do pracy z MSBufferizerem

W przeszłości Artykuł opracowaliśmy własny filtr. W tym artykule skupimy się na wewnętrznym mechanizmie przenoszenia danych pomiędzy filtrami streamerów multimediów. Umożliwi to w przyszłości pisanie zaawansowanych filtrów przy mniejszym wysiłku.

Mechanizm przenoszenia danych

Przenoszenie danych w streamerze multimediów odbywa się za pomocą kolejek opisanych strukturą kolejka_t. Ciągi wiadomości typu mblk_t, które same w sobie nie zawierają danych sygnałowych, a jedynie linki do poprzedniego, następnego komunikatu i do bloku danych. Ponadto chcę szczególnie podkreślić, że istnieje również pole na link do wiadomości tego samego typu, co pozwala na zorganizowanie pojedynczo połączonej listy wiadomości. Grupę wiadomości połączoną taką listą nazwiemy krotką. Zatem dowolny element kolejki może być pojedynczą wiadomością mblk_ti być może nagłówek krotki wiadomości mblk_t. Każda wiadomość krotki może mieć swój własny blok danych. Nieco później omówimy, dlaczego krotki są potrzebne.

Jak wspomniano powyżej, sama wiadomość nie zawiera bloku danych, zamiast tego zawiera jedynie wskaźnik do obszaru pamięci, w którym przechowywany jest blok. W tej części ogólny obraz pracy streamera medialnego przypomina magazyn drzwi z kreskówki „Monsters, Inc.”, gdzie drzwi (linki do danych - pokoi) poruszają się z szaloną prędkością po podwieszonych przenośnikach, podczas gdy same pomieszczenia pozostać bez ruchu.

Teraz, poruszając się po hierarchii od dołu do góry, przyjrzyjmy się szczegółowo wymienionym podmiotom mechanizmu transmisji danych w streamerze multimediów.

Blok danych dblk_t

Blok danych składa się z nagłówka i bufora danych. Nagłówek jest opisany następującą strukturą,

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

Pola struktury zawierają wskaźniki na początek bufora, koniec bufora oraz funkcję usuwania bufora danych. Ostatni element w nagłówku db_ref — licznik referencyjny, jeżeli osiągnie zero, jest to sygnał do usunięcia tego bloku z pamięci. Jeśli blok danych został utworzony przez funkcję datab_alloc() , wówczas bufor danych zostanie umieszczony w pamięci zaraz po nagłówku. We wszystkich innych przypadkach bufor może być umieszczony gdzieś osobno. Bufor danych będzie zawierał próbki sygnałów lub inne dane, które chcemy przetworzyć za pomocą filtrów.

Nowa instancja bloku danych tworzona jest za pomocą funkcji:

dblk_t *datab_alloc(int size);

Jako parametr wejściowy podawany jest rozmiar danych, które będą przechowywane w bloku. Przydzielana jest większa ilość pamięci, aby umieścić nagłówek – strukturę – na początku przydzielonej pamięci baza danych. Jednak w przypadku innych funkcji nie zawsze tak się dzieje; w niektórych przypadkach bufor danych może znajdować się oddzielnie od nagłówka bloku danych. Podczas tworzenia struktury pola są konfigurowane tak, aby jej pole baza_bazy wskazał początek obszaru danych i db_lim do końca. Liczba linków db_ref jest ustawiony na jeden. Wskaźnik funkcji kasowania danych jest ustawiony na zero.

wiadomość mblk_t

Jak już wspomniano, elementy kolejki są typu mblk_t, definiuje się to następująco:

typedef struct msgb
{
  struct msgb *b_prev;   // Указатель на предыдущий элемент списка.
  struct msgb *b_next;   // Указатель на следующий элемент списка.
  struct msgb *b_cont;   // Указатель для подклейки к сообщению других сообщений, для создания кортежа сообщений.
  struct datab *b_datap; // Указатель на структуру блока данных.
  unsigned char *b_rptr; // Указатель на начало области данных для чтения данных буфера b_datap.
  unsigned char *b_wptr; // Указатель на начало области данных для записи данных буфера b_datap.
  uint32_t reserved1;    // Зарезервированное поле1, медиастример помещает туда служебную информацию. 
  uint32_t reserved2;    // Зарезервированное поле2, медиастример помещает туда служебную информацию.
  #if defined(ORTP_TIMESTAMP)
  struct timeval timestamp;
  #endif
  ortp_recv_addr_t recv_addr;
} mblk_t;

Struktura mblk_t zawiera wskazówki na początku b_poprzedni, b_następny, które są niezbędne do zorganizowania podwójnie połączonej listy (która jest kolejką kolejka_t).

Potem pojawia się wskaźnik b_cd, który jest używany tylko wtedy, gdy wiadomość jest częścią krotki. W przypadku ostatniej wiadomości w krotce ten wskaźnik pozostaje pusty.

Następnie widzimy wskaźnik do bloku danych b_dane, dla którego istnieje komunikat. Po nim następują wskaźniki do obszaru wewnątrz bufora danych blokowych. Pole b_rptr określa lokalizację, z której będą odczytywane dane z bufora. Pole b_wptr wskazuje lokalizację, z której będą wykonywane zapisy do bufora.

Pozostałe pola mają charakter usługowy i nie dotyczą obsługi mechanizmu przesyłania danych.

Poniżej znajduje się pojedyncza wiadomość z nazwą m1 i blok danych d1.
Poznawanie silnika Mediastreamer2 VoIP. Część 11
Poniższy rysunek przedstawia krotkę trzech komunikatów m1, m1_1, m1_2.
Poznawanie silnika Mediastreamer2 VoIP. Część 11

Funkcje przesyłania wiadomości mblk_t

Nowa wiadomość mblk_t utworzone przez funkcję:

mblk_t *allocb(int size, int pri); 

zapisuje w pamięci nową wiadomość mblk_t z blokiem danych o określonej wielkości rozmiar, drugi argument - pri nieużywane w tej wersji biblioteki. Powinno pozostać zerowe. W trakcie działania funkcji zostanie przydzielona pamięć na strukturę nowego komunikatu i zostanie wywołana funkcja mblk_init(), co zresetuje wszystkie pola utworzonej instancji struktury, a następnie korzystając z powyższego datab_alloc(), utworzy bufor danych. Po czym zostaną skonfigurowane pola w strukturze:

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

Na wyjściu otrzymujemy nową wiadomość z zainicjowanymi polami i pustym buforem danych. Aby dodać dane do wiadomości należy skopiować ją do bufora bloku danych:

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

gdzie dane jest wskaźnikiem do źródła danych, oraz rozmiar - ich rozmiar.
następnie musisz zaktualizować wskaźnik do punktu zapisu, aby ponownie wskazywał początek wolnego obszaru w buforze:

msg->b_wptr = msg->b_wptr + size

Jeśli chcesz utworzyć wiadomość z istniejącego bufora, bez kopiowania, użyj funkcji:

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

Funkcja po utworzeniu komunikatu i strukturze bloku danych skonfiguruje swoje wskaźniki do danych pod adresem buf. Te. w tym przypadku bufor danych nie jest umiejscowiony za polami nagłówkowymi bloku danych, jak miało to miejsce przy tworzeniu bloku danych za pomocą funkcji datab_alloc(). Bufor z danymi przekazanymi do funkcji pozostanie tam, gdzie był, jednak za pomocą wskaźników zostanie dołączony do nowo utworzonego nagłówka bloku danych, a tym samym do wiadomości.

Do jednej wiadomości mblk_t Można łączyć kilka bloków danych sekwencyjnie. Robi się to za pomocą funkcji:

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

mp — komunikat, do którego zostanie dodany kolejny blok danych;
dane — wskaźnik do bloku, którego kopia zostanie dodana do wiadomości;
rozmiar — rozmiar danych;
ścieżka — flaga informująca, że ​​rozmiar przydzielonej pamięci musi być wyrównany wzdłuż 4-bajtowej granicy (wypełnienie zostanie wykonane zerami).

Jeśli w istniejącym buforze danych komunikatu jest wystarczająca ilość miejsca, nowe dane zostaną wklejone za danymi już tam znajdującymi się. Jeśli w buforze danych wiadomości jest mniej wolnego miejsca niż rozmiar, wówczas tworzona jest nowa wiadomość z buforem o wystarczającej wielkości i dane są kopiowane do tego bufora. To jest nowa wiadomość, połączona z oryginalną za pomocą wskaźnika b_cd. W tym przypadku wiadomość zamienia się w krotkę.

Jeśli chcesz dodać do krotki kolejny blok danych, musisz skorzystać z funkcji:

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

znajdzie ostatnią wiadomość w krotce (on has b_cd będzie null) i wywoła funkcję dla tej wiadomości dołączb().

Rozmiar danych w wiadomości lub krotce możesz sprawdzić za pomocą funkcji:

int msgdsize(const mblk_t *mp);

przejdzie przez wszystkie wiadomości w krotce i zwróci całkowitą ilość danych w buforach danych tych wiadomości. Dla każdej wiadomości ilość danych jest obliczana w następujący sposób:

 mp->b_wptr - mp->b_rptr

Aby połączyć dwie krotki, użyj funkcji:

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

ona dołącza krotkę nowość do ogona krotki mp i zwraca wskaźnik do ostatniej wiadomości wynikowej krotki.

W razie potrzeby krotkę można zamienić w jedną wiadomość zawierającą pojedynczy blok danych, służy do tego funkcja:

void msgpullup(mblk_t *mp,int len);

jeśli argument len wynosi -1, wówczas rozmiar przydzielonego bufora jest określany automatycznie. Jeśli len jest liczbą dodatnią, zostanie utworzony bufor o tej wielkości i zostaną do niego skopiowane dane wiadomości krotki. Jeśli bufor się wyczerpie, kopiowanie zostanie w tym miejscu zatrzymane. Pierwsza wiadomość krotki otrzyma bufor o nowym rozmiarze ze skopiowanymi danymi. Pozostałe wiadomości zostaną usunięte, a pamięć zwrócona na stertę.

Podczas usuwania struktury mblk_t liczba referencji bloku danych jest brana pod uwagę, jeśli podczas wywoływania darmoweb() okazuje się, że wynosi zero, wówczas bufor danych jest usuwany wraz z instancją mblk_t, co na to wskazuje.

Inicjowanie pól nowej wiadomości:

void mblk_init(mblk_t *mp);

Dodanie kolejnej porcji danych do wiadomości:

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

Jeżeli nowe dane nie mieszczą się w wolnej przestrzeni bufora danych komunikatu, to do wiadomości dołączana jest odrębnie utworzona wiadomość z buforem o wymaganej wielkości (w pierwszym komunikacie ustawiany jest wskaźnik do dodanej wiadomości) i wiadomość zamienia się w krotkę.

Dodanie fragmentu danych do krotki:

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

Funkcja wywołuje appendb() w pętli.

Łączenie dwóch krotek w jedną:

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

wiadomość nowość zostanie dołączony do mp.

Tworzenie kopii pojedynczej wiadomości:

mblk_t *copyb(const mblk_t *mp);

Pełne kopiowanie krotki ze wszystkimi blokami danych:

mblk_t *copymsg(const mblk_t *mp);

Elementy krotki są kopiowane przez funkcję kopiujb().

Utwórz łatwą kopię wiadomości mblk_t. W tym przypadku blok danych nie jest kopiowany, lecz zwiększany jest jego licznik referencyjny db_ref:

mblk_t *dupb(mblk_t *mp);

Tworzenie lekkiej kopii krotki. Bloki danych nie są kopiowane, inkrementowane są jedynie ich liczniki referencyjne db_ref:

mblk_t *dupmsg(mblk_t* m);

Sklejanie wszystkich wiadomości krotki w jedną wiadomość:

void msgpullup(mblk_t *mp,size_t len);

Jeśli argument len wynosi -1, wówczas rozmiar przydzielonego bufora jest określany automatycznie.

Usuwanie wiadomości, krotka:

void freemsg(mblk_t *mp);

Liczba odwołań do bloku danych jest zmniejszana o jeden. Jeśli osiągnie zero, blok danych również zostanie usunięty.

Obliczanie całkowitej ilości danych w wiadomości lub krotce.

size_t msgdsize(const mblk_t *mp);

Pobieranie wiadomości z końca kolejki:

mblk_t *ms_queue_peek_last (q);

Kopiowanie zawartości zarezerwowanych pól jednej wiadomości do innej wiadomości (w rzeczywistości pola te zawierają flagi używane przez streamer multimediów):

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

Kolejka kolejka_t

Kolejka komunikatów w streamerze multimediów jest zaimplementowana w formie cyklicznej, podwójnie połączonej listy. Każdy element listy zawiera wskaźnik do bloku danych z próbkami sygnałów. Okazuje się, że po kolei poruszają się tylko wskaźniki do bloku danych, natomiast same dane pozostają w bezruchu. Te. przenoszone są jedynie linki do nich.
Struktura opisująca kolejkę kolejka_t, pokazane poniżej:

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

Struktura zawiera pole - wskaźnik _q_korek wpisz *mblk_t, wskazuje on pierwszy element (wiadomość) w kolejce. Drugie pole konstrukcji to licznik komunikatów w kolejce.
Poniższy rysunek przedstawia kolejkę o nazwie q1 zawierającą 4 komunikaty m1, m2, m3, m4.
Poznawanie silnika Mediastreamer2 VoIP. Część 11
Poniższy rysunek przedstawia kolejkę o nazwie q1 zawierającą 4 komunikaty m1,m2,m3,m4. Komunikat m2 jest początkiem krotki zawierającej jeszcze dwa komunikaty m2_1 i m2_2.

Poznawanie silnika Mediastreamer2 VoIP. Część 11

Funkcje do pracy z kolejkami kolejka_t

Inicjalizacja kolejki:

void qinit(queue_t *q);

Pole _q_korek (dalej będziemy go nazywać „stopperem”) jest inicjowany przez funkcję mblk_init(), jego poprzedni element i wskaźnik następnego elementu są dostosowywane tak, aby wskazywały na siebie. Licznik elementów kolejki jest resetowany do zera.

Dodanie nowego elementu (wiadomości):

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

Nowy element m zostanie dodany na koniec listy, wskaźniki elementu zostaną dostosowane w taki sposób, że stoper stanie się dla niego kolejnym elementem, a on stanie się poprzednim elementem dla stopera. Licznik elementów kolejki jest zwiększany.

Pobieranie elementu z kolejki:

mblk_t * getq(queue_t *q); 

Komunikat pojawiający się po pobraniu stopera i zmniejszeniu licznika elementów. Jeśli w kolejce nie ma żadnych elementów poza stoperem, zwracane jest 0.

Wstawianie wiadomości do kolejki:

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

Element mp wstawiony przed elementem EMP. Jeśli EMP=0, wówczas wiadomość jest dodawana na końcu kolejki.

Pobieranie wiadomości z początku kolejki:

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

Licznik elementów jest zmniejszany.

Odczyt wskaźnika do pierwszego elementu w kolejce:

mblk_t * peekq(queue_t *q); 

Usuwanie wszystkich elementów z kolejki przy jednoczesnym usuwaniu samych elementów:

void flushq(queue_t *q, int how);

argument w jaki sposób nieużywany. Licznik elementów kolejki jest ustawiony na zero.

Makro odczytujące wskaźnik do ostatniego elementu kolejki:

mblk_t * qlast(queue_t *q);

Pracując z kolejkami wiadomości, pamiętaj o tym, dzwoniąc ms_queue_put(q, m) ze wskaźnikiem zerowym do wiadomości, funkcja wykonuje pętlę. Twój program się zawiesi. zachowuje się podobnie ms_queue_next(q, m).

Podłączanie filtrów

Opisana powyżej kolejka służy do przekazywania wiadomości z jednego filtra do drugiego lub z jednego do kilku filtrów. Filtry i ich połączenia tworzą graf skierowany. Wejście lub wyjście filtra będzie nazywane ogólnym słowem „pin”. Aby opisać kolejność, w jakiej filtry są ze sobą połączone, streamer multimediów wykorzystuje koncepcję „punktu sygnałowego”. Punktem sygnałowym jest struktura _MSCPoint, który zawiera wskaźnik do filtra i numer jednego z jego pinów, czyli opisuje podłączenie jednego z wejść lub wyjść filtra.

Punkt sygnałowy wykresu przetwarzania danych

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

Piny filtra numerowane są od zera.

Połączenie dwóch pinów kolejką komunikatów opisuje struktura _MSKolejka, który zawiera kolejkę komunikatów i wskaźniki do dwóch punktów sygnałowych, które łączy:

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

Nazwiemy tę strukturę łączem sygnałowym. Każdy filtr strumienia multimediów zawiera tabelę łączy wejściowych i tabelę łączy wyjściowych (Kolejka MS). Rozmiar tabel ustalany jest podczas tworzenia filtra, robiliśmy to już za pomocą wyeksportowanej zmiennej typu Opis MSFiltera, kiedy opracowaliśmy własny filtr. Poniżej znajduje się struktura opisująca dowolny filtr w streamerze multimediów, Filtr MS:


struct _MSFilter{
    MSFilterDesc *desc;    /* Указатель на дескриптор фильтра. */
    /* Защищенные атрибуты, их нельзя сдвигать или убирать иначе будет нарушена работа с плагинами. */
    ms_mutex_t lock;      /* Семафор. */
    MSQueue **inputs;     /* Таблица входных линков. */
    MSQueue **outputs;    /* Таблица выходных линков. */
    struct _MSFactory *factory; /* Указатель на фабрику, которая создала данный экземпляр фильтра. */
    void *padding;              /* Не используется, будет задействован если добавятся защищенные поля. */
    void *data;                 /* Указатель на произвольную структуру для хранения данных внутреннего состояния фильтра и промежуточных вычислений. */
    struct _MSTicker *ticker;   /* Указатель на объект тикера, который не должен быть нулевым когда вызывается функция process(). */
    /*private attributes, they can be moved and changed at any time*/
    MSList *notify_callbacks; /* Список обратных вызовов, используемых для обработки событий фильтра. */
    uint32_t last_tick;       /* Номер последнего такта, когда выполнялся вызов process(). */ 
    MSFilterStats *stats;     /* Статистика работы фильтра.*/
    int postponed_task; /*Количество отложенных задач. Некоторые фильтры могут откладывать обработку данных (вызов process()) на несколько тактов.*/
    bool_t seen;  /* Флаг, который использует тикер, чтобы помечать что этот экземпляр фильтра он уже обслужил на данном такте.*/
};
typedef struct _MSFilter MSFilter;

Po tym jak połączyliśmy filtry w programie C zgodnie z naszym planem (ale nie połączyliśmy tickera), stworzyliśmy w ten sposób graf skierowany, którego węzły są instancjami konstrukcji Filtr MS, a krawędzie są wystąpieniami łączy Kolejka MS.

Za kulisami działalności tickera

Kiedy mówiłem, że ticker jest filtrem źródła kleszczy, nie była to cała prawda. Ticker to obiekt uruchamiający funkcje zegara proces() wszystkie filtry obwodu (wykresu), do którego jest podłączony. Kiedy podłączymy ticker do filtru wykresu w programie C, pokażemy tickerowi wykres, którym będzie sterował od tej chwili, dopóki go nie wyłączymy. Po podłączeniu ticker zaczyna przeglądać powierzony mu wykres, tworząc listę filtrów, które go uwzględniają. Aby nie „liczyć” tego samego filtra dwa razy, zaznacza wykryte filtry umieszczając w nich checkbox widziany. Wyszukiwanie odbywa się przy użyciu tabel linków, które posiada każdy filtr.

Podczas wstępnego zwiedzania wykresu ticker sprawdza, czy wśród filtrów znajduje się przynajmniej jeden, który pełni rolę źródła bloków danych. Jeżeli ich nie ma, wykres zostaje uznany za nieprawidłowy i następuje awaria tickera.

Jeśli wykres okaże się „poprawny”, dla każdego znalezionego filtra funkcja wywoływana jest do inicjalizacji przetwarzanie wstępne(). Gdy tylko nadejdzie moment kolejnego cyklu przetwarzania (domyślnie co 10 milisekund), ticker wywołuje funkcję proces() dla wszystkich wcześniej znalezionych filtrów źródłowych, a następnie dla pozostałych filtrów na liście. Jeśli filtr ma łącza wejściowe, to uruchomienie funkcji proces() powtarza się, aż kolejki łączy wejściowych będą puste. Następnie przechodzi do kolejnego filtra na liście i „przewija” go, aż linki wejściowe będą wolne od komunikatów. Pasek przesuwa się od filtra do filtra, aż lista się zakończy. To kończy przetwarzanie cyklu.

Teraz powrócimy do krotek i porozmawiamy o tym, dlaczego taka jednostka została dodana do streamera multimediów. Generalnie ilość danych wymaganych przez algorytm działający wewnątrz filtru nie pokrywa się i nie jest wielokrotnością wielkości buforów danych odbieranych na wejście. Na przykład piszemy filtr wykonujący szybką transformatę Fouriera, która z definicji może przetwarzać tylko bloki danych, których rozmiar jest potęgą dwójki. Niech będzie 512 zliczeń. Jeżeli dane generowane są kanałem telefonicznym, to bufor danych każdego komunikatu na wejściu dostarczy nam 160 próbek sygnału. Kuszące jest, aby nie zbierać danych wejściowych, dopóki nie pojawi się wymagana ilość danych. Ale w tym przypadku nastąpi kolizja z tickerem, który bezskutecznie będzie próbował przewinąć filtr, aż łącze wejściowe będzie puste. Wcześniej określaliśmy tę regułę jako trzecią zasadę filtra. Zgodnie z tą zasadą funkcja Process() filtru musi pobrać wszystkie dane z kolejek wejściowych.

Ponadto nie będzie możliwe pobranie z wejścia tylko 512 próbek, ponieważ można pobrać tylko całe bloki, tj. filtr będzie musiał pobrać 640 próbek i wykorzystać 512 z nich, resztę przed zgromadzeniem nowej porcji danych. Zatem nasz filtr oprócz swojej głównej pracy musi zapewniać działania pomocnicze w celu pośredniego przechowywania danych wejściowych. Twórcy streamera multimediów i rozwiązania tego ogólnego problemu opracowali specjalny obiekt - MSBufferizer (bufor), który rozwiązuje ten problem za pomocą krotek.

Bufor (MSBufferizer)

Jest to obiekt, który będzie gromadził dane wejściowe wewnątrz filtra i rozpoczynał ich przesyłanie do przetwarzania, gdy tylko ilość informacji będzie wystarczająca do uruchomienia algorytmu filtru. W czasie, gdy bufor gromadzi dane, filtr będzie działał w trybie bezczynności, nie zużywając mocy obliczeniowej procesora. Jednak gdy tylko funkcja odczytu z bufora zwróci wartość różną od zera, funkcja Process() filtra zaczyna pobierać i przetwarzać dane z bufora w porcjach o wymaganym rozmiarze, aż do ich wyczerpania.
Dane, które nie są jeszcze wymagane, pozostają w buforze jako pierwszy element krotki, do którego dołączane są kolejne bloki danych wejściowych.

Struktura opisująca bufor:

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

Funkcje do pracy z MSBufferizerem

Tworzenie nowej instancji bufora:

MSBufferizer * ms_bufferizer_new(void);

Pamięć jest przydzielana, inicjowana w ms_bufferizer_init() i zwracany jest wskaźnik.

Funkcja inicjalizacji:

void ms_bufferizer_init(MSBufferizer *obj); 

Trwa inicjowanie kolejki q, pole rozmiar jest ustawiony na zero.

Dodawanie wiadomości:

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

Wiadomość m została dodana do kolejki. Dodawany jest obliczony rozmiar bloków danych rozmiar.

Przesyłanie wszystkich komunikatów z kolejki danych łącza do bufora q:

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

Przesyłanie wiadomości z łącza q w buforze odbywa się za pomocą funkcji ms_bufferizer_put().

Odczyt z bufora:

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

Jeżeli rozmiar danych zgromadzonych w buforze jest mniejszy niż żądany (dane), to funkcja zwraca zero, dane nie są kopiowane do danych. W przeciwnym wypadku następuje sekwencyjne kopiowanie danych z krotek znajdujących się w buforze. Po skopiowaniu krotka jest usuwana, a pamięć zostaje zwolniona. Kopiowanie kończy się w momencie skopiowania bajtów danych. Jeśli w środku bloku danych zabraknie miejsca, w tym komunikacie blok danych zostanie zredukowany do pozostałej, nieskopiowanej części. Gdy następnym razem zadzwonisz, kopiowanie będzie kontynuowane od tego momentu.

Odczyt ilości danych aktualnie dostępnych w buforze:

int ms_bufferizer_get_avail(MSBufferizer *obj); 

Zwraca pole rozmiar bufor.

Odrzucanie części danych w buforze:

void ms_bufferizer_skip_bytes(MSBufferizer *obj, int bytes);

Określona liczba bajtów danych jest pobierana i odrzucana. Najstarsze dane są odrzucane.

Usuwanie wszystkich wiadomości w buforze:

void ms_bufferizer_flush(MSBufferizer *obj); 

Licznik danych zostaje wyzerowany.

Usuwanie wszystkich wiadomości w buforze:

void ms_bufferizer_uninit(MSBufferizer *obj); 

Licznik nie jest resetowany.

Usuwanie bufora i zwalnianie pamięci:

void ms_bufferizer_destroy(MSBufferizer *obj);  

Przykłady użycia bufora można znaleźć w kodzie źródłowym kilku filtrów strumieniujących media. Na przykład w filtrze MS_L16_ENC, który zmienia kolejność bajtów w próbkach z kolejności sieciowej na kolejność hosta: l16.c

W kolejnym artykule przyjrzymy się zagadnieniu szacowania obciążenia tickera oraz temu, jak radzić sobie z nadmiernym obciążeniem obliczeniowym w streamerze multimediów.

Źródło: www.habr.com

Dodaj komentarz