RTP/RTCP – Realtime Transport/Control Protocol

Ponieważ ostatnio musiałem odświeżyc swoją wiedzę na temat RTP ponizej powtórka z przeszlosci. Poniższy post oparty jest bezpośrednio na dokumencie IETF RFC 3550 specyfikujacym bazowy protokol RTP.

RTP/RTCP są to protokóły przeznaczone do transmisji end2end sygnałów cyfrowych o charakterystyce ‘realtime’, takich jak dzwięk czy video. Został zaprojektowany w celu oddzielenia mechanizmów transmiscji danych i kontroli sesji. Z każdym strumieniem danych skojarzony jest oddzielny kanał RTP/RTCP zawierajaca po jednym porcie RTP i RTCP. RTP jest protokołem odpowiedzialnym za transmisje strumieni danych tak zwanych ‘RTP payload’. RTP samo w sobie nie zapewnia mechanizmów kontorli opoźnień czy stratności ale bazuje na wykorzystywanym protokole transportowym ktorym najcześciej jest to UDP. RTCP skolei jest odpowiedzialne za kontrole jakości swiadczonych uslug poprzez RTP (informowanie o ilosci gubionych pakietow, opoznieniach czy parametrach wykorzystywanych kodekow adaptacyjnych). Opcjonalnie dostarcza możliwość kontroli uczestnikow sesji, ale to najcześciej jest realizowane przez skojarzony z RTP protokół sygnalizacyjny tak jak np SIP.

RTP

Struktura pakietu

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           synchronization source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            contributing source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Pierwsze 12 oktetow wystepuje zawsze, opcjonalne pola CSRC wystepuja gdy na drodze danych jest mixer.

  • version (V): 2 bity – wersja obecnie 2
  • padding (P): 1 bit – wskazuje czy koniec pakietu jest uzupelniony zerami, jesli tak ostatni oktet wskazuje na liczbe oktetow do pominiecia
  • extension (X): 1 bit – wskazuje ze do pakietu dolaczony jest naglowek z rozszerzeniem
  • CSRC count (CC): 4 bity – wskazuje na liczbe identyfikatorw CSRC na koncu naglowka
  • marker (M): 1 bit – umozliwia wskazanie ze jest znaczacy pakiet, wykorzystywane przez profile RTP
  • payload type (PT): 7 bitów – określa format danych
  • sequence number: 16 bitów – numer sekwencyjny pakietu RTP, zwiekszany o jeden za każdym razem
  • timestamp: 32 bity – wartosc bezwgledna informujace o przedziale pomiedzy przesylanymi probkami danych
  • SSRC: 32 bity – identyfikuje zrodlo synchronizacji
  • CSRC list: od 0 tdo 15 , 32 bity każde – listaidentyfikatorow CSRC ktorych dane sa mixowane

RTCP

Zasada dzialanie RTCP polega na cyklicznej wymianie wiadomosci kontrolnych w oparciu o te same mechanizmy dystrubucji co RTP np. uzywajac innego portu udp. RTCP realizuje 4 podstawowe funkcje:
  • informowanie o jakosci dystrybucji danych oraz mozliwosciach adaptacji na poziomie kodowania
  • przenosi parametr CNAME odpowiedzialny za persystentna identyfikacje sesji RTP
  • ustala czestotliowsc wymiany pakietow w zaleznosci od liczby uczestnikow
  • opcjonalnie pozawala przenosic minimalna ilosc informacji identyfikujaca strony

Struktura pakietu

RFC 3550 definiuje pieć rodzajów pakietów, które przenoszą różne informacje kontrolne

  • SR (Sender Report) – statystyki transmisji i odbioru danych od aktywnych uczestnikow
  • RR (Receiver Report ) – statystyki odbioru dla nieaktywnych uczestnikow
  • SDES (Source Description) – parametry informacyjne zródła np CNAME
  • BYE – informuje o razlaczeniu
  • APP – informacje specyficzne dla danej aplikacji
Kazdy pakiet RTCP składa sie ze stałej części oraz następującej po niej czesci o zmiennej długosci, w zależności od typu pakietu wyrówanej do 32 bitów. Kilka pakietów może być szeregowo łączonych tworząc tak zwany złożony pakiet RTP, umieszczany w pakiecie warstwy niższej. Nie ma żadnego konkretnego wymogu na wielkosc pakietu złożonego, gdyż jest ona kontrolowana przez protokół transportowy. Każdy pakiet wchodzący w skład pakietu złożonego jest analizowany niezależnie od innych, stad kolejność i kombinacja pakietów nie są istotne. Tym niemniej aby spełnić wymaganie realizowane przez protokół na strukturę pakietu złożonego zostały nałożone następujące ograniczenia:
  • SR lub RR musza byc wysylane w kazdym pakiecie zlozonym, tak aby statystyki odbioru byly jak najbardziej dokladne
  • SDES z parametrem CNAME musi być wysylany w kazym pakieci zlozonym, tak aby odbiorca jak najszyciej otrzymal informacje o nadawcy
  • liczba pakietów wyslana w pierwszym pakiecie zlozonym powinna byc jak najmniejsza (2) tak aby liczba stalych bitów byla jak najwieksza i prawdopodobienstwo walidacji pakietu najwieksze

Stad tez struktura pakietu zlozonego musi zawierac conajmniej dwa pakiety o nastepujacej formie:

random encryption prefix: losowy 32-bitowy integer
|
|[--------- packet --------][---------- packet ----------][-packet-]
|
|                receiver            chunk        chunk
V                reports           item  item   item  item
--------------------------------------------------------------------
R[SR #sendinfo #site1#site2][SDES #CNAME PHONE #CNAME LOC][BYE##why]
--------------------------------------------------------------------
|                                                                  |
|<-----------------------  compound packet ----------------------->|
|<--------------------------  UDP packet ------------------------->|

#: SSRC/CSRC identifier
  • Encryption prefix – jesli pakiet zlozony jest szyforowany jest poprzedzany 32 bitową wartościa calkowita
  • SR lub RR – zawsze pierwszy pakiet w pakiecie zlozonym to SR lub RR nawet jesli zadne dane nie byly jeszcze wyslane
  • Dodatkowe RR – jesli liczba zrodel dla ktorych generowane sa statystyki przewyzsza 31 i nie moga byc umieszczone w jednym RR lub SS sa one umieszczane w dodatkowych pakietach RR
  • SDES – w kazdym pakiecie zlozonym musi byc dolaczony pakiet zawierajacy parametr CNAME inne parametry sa umieszczane w zalezności od aplikacji
  • BYE lub APP – pozostale pakiety moga sie pojawiac w dowolnej ilosci i kolejnosci z tym wyjatkiem ze pakiet BYE zawierajacy SSRC/CSRC musi byc ostatni
Kazdy uczestnik powinnien wysylac tylko jeden pakiet zlozony w trakcie okresu raportowania aby oszacowanie pasma bylo precyzyjniejsze. Jesli ilosc dodatkowych pakietow RR nie miesci sie w MTU nalezy ograniczyc ich ilosc i przeslac w nastepnej turze, tak by wszystkie zrodla byly raportowane.

Czestotliwosci RTCP

RTP zostalo tak zaprojektowane aby umozliwiac regulowanie ruchu kontrolnego w zaleznosci o ilosci uczestnikow i przyjetej charakterystyki lacza. Z kazda sesja danych RTP zwiazane jest maksymalne dopuszczalne pasmo sesji (session bandwidth) bedace agregacja danych poszczegolnych uczestnikow. Mechanizm doboru pasma sesji moze byc praktycznie dowolny ale najczesniej przyjmuje sie jego wartosc jako nominalna sume pasm zajmowanych przez maksymalna liczbe jednoczesnie aktywnych uczestnikow. Wartosc pasma sesji najczesciej ustalana jest przez warstwe aplikacji odpowiedzialna za zarzadzanie sesja przy czym najczesciej wartosc domyslna ustalana jest jako pasmo odpowiadajace jednemu aktywnemu uzytkownikowi. Wszyscy uczestnicy sesji musza uzywac tego samego pasma tak aby okres retransmisji RTCP byl taki sam. Warto pamietac ze w trakcie obliczania utylizacji dostepnego pasma brane sa pod uwage tez protokoly transportowe (UDP i IP) ale warstwa lacza danych juz nie gdyz te sie od siebie różnia. Ruch kontrolny jest ograniczany zarówno z góry jak i z dołu. Z góry jako czastkowa wartość calkowitego dostepnego pasma (norma 5%) lub jako wartość ilościowa. Z dolu natomiast ustala sie minimalna wartość tak aby nie generować nadmiernego ruchu (norma 5s), istnieja przypadki kiedy ta wartosc moze byc jeszcze bardziej zredukowana i odwrotnie proporcjonalna do dostepnego pasma. Zaleca sie rowniez aby z posrod calego ruchu RTCP, 1/4 byla przypisana do aktywnych uczestnikow, tak aby nowo dolaczajacy sie uzytkownicy szybko dowiadywali sie aktywnych CNAME. Algorytm oblicza czestotliowsci wysylania pakietów zlozonych tak aby dostępne pasmo na ruch kontrolny było równie rozdzielone pomiedzy uczestników. Wyznaczona czestotliwosc skaluje sie liniowe w stosunku do liczby uczestników, co zapewnia stała wartość ruchu kontrolnego. Aby uniknąc pelnej synchronizacji kazdy z uczestnikow posluguje sie lekka wariacja okresu wysylania RTCP oraz losowym opoznienieniem dla pierwszego wysylanego pakietu zlozonego. Dodatkowo obslugiwane sa mechanizmy obslugujace sytuacje wyjatkowe kiedy wielu uczestnikow jednoczesnie dolacza lub opuszcza sesje.

Ilosc Uczestnikow

Wyznaczanie czestotliwosci RTCP bazuje na znajomosci oszacowanej liczby uczestnikow. Uczestnik okreslany jest jako nowy gdy w sesji pojawi sie ruch z nowym identyfikatorem SSRC lub CSRC. Istnieje możliwosc przyjecia ze musi byc zarejstrowanych kilka pakietow by uznac ze pojawil sie nowy uczestnik lub ze musi zostac odebrany pakiet SDES z nowym CNAME. Uczestnika uwaza sie za usunietego gdy wysyla pakiet BYE lub przez okreslony czas nie wysyla pakietow.

Zasady Wysylania i Odbierania pakietow RTCP

Aby zrealizowac powyzsze zalozenia kazdy uzytkownik musi lokalnie przechowywac szereg informacji zwiazanych z realizowana sesja:

  • tp – czas ostatniej transmisji
  • tc- obecny czas
  • tn – czas nastepnej transmisji
  • pmembers – oszacowana liczba uczestnikow podczas podczas ostatniej transmisji
  • members – aktualna oszacowana liczba uczestnikow
  • senders – aktualna oszacowana liczba aktywnych uczestnikow
  • rtcp_bw – pasmo przydzielone dla calego ruchu RTCP wszystkich uczestnikow
  • we_sent – flaga informujaca czy od ostatniego raportu uczestnik wyslal dane
  • avg_rtcp_size – sredni rozmiar wyslanych i odebranych pakietow przez uzytkownika
  • initial – ustawiona na true gdy uzytkownik nie wyslal jeszcze zadnego pakietu RTCP
W trakcie inicjalizacji aplikacji parametry ustawiane sa na wartosci domyslne. Wartosc okresu nadawania wiadomosci kontrolnych jest obliczana na podstawie powyzej wymienionych parameterow. Procedura w efekcie daje przedzial ktory jest losowy i przydziela minimum 1/4 calego dostepnego pasma uzytkownikom aktywnym. Jesli uzytkoników aktywnych jest wiecej niż 1/4 wszystkich uzytkownikow dostepne pasmo jest dytrybuowane po rowno do wszystkich uczestników. Po otrzymaniu pakietu RTP lub RTCP od uczesnika, ktorego SSRC nie jest obecne w tablicy uczestników, jest on dodawany do listy i liczba uczestnikow jest aktualizowana. Kiedy pakiet RTP jest od uczestnika ktory nie znajduje sie na liscie aktywnych uczestnikow jest on do niej dodawany i ich liczba jest aktualizowana. Jak zawsze przy kazdym odebranym i wyslanym pakiecie wartosc avg_rtcp_size jest aktualizowana. Gdy uczestnik odbiera pakiet BYE sprawdza czy na liscie uczestników lub aktywnych uczestników znajduje nadawca pakietu, jesli tak, jest on z niej usuwany, aktualizowane sa parametry oraz czas wyslania nastepnego zlozonego pakietu RTCP. Przynajmniej raz na jeden okres przesylania pakietu kontrolnego uczestnik weryfikuje czy na którejś z list nie nastapil timeout dla danego SSRC. Kiedy uczestnik chce opuscic sesje moze ale nie musi wyslac pakiet BYE, jesli tego nie zrobi nastapi timeout. Jesli liczba uzytkownikow jest mala (zalecane 50) moze wyslac pakiet od razu, w przeciwnym wypadku stosuje mechanizm zapobiegajacy masowemu opuszczaniu sesji przez duza liczbe uczestnikow.

Pakiety SR i RR

W oparciu o pakiety RR odbiorcy informuja o jakosci odbieranych danych, jeśli odbiorca jest uczestnikiem aktywnym i wysyłał dane od ostatniego raportu wykorzystuje pakiet SR zawierajacy dodatkowe informacje o nadawcy. W kazdym pakiecie SR i RR znajduje sie po jednym bloku raportujacym skojarzonym z jednym źródłem synchronizacji. Jeśli zródeł jest wiecej niż 31 powinny zostać umieszczone w kolejnych pakietach RR.
SR składa sie z trzech sekcji obowiazkowych: nagłówka, informacji o nadawcy, listy bloków raportujacych i czwartej opcjonalnej dedykowanej dla konkretnego profilu. opcjonalna cześć jest wykorzystywana gdy profil RTP wymaga przesylania dodatkowych informacji pomiedzy stronami.
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P|    RC   |   PT=SR=200   |             length            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         SSRC of sender                        |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sender |              NTP timestamp, most significant word             |
info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |             NTP timestamp, least significant word             |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         RTP timestamp                         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                     sender's packet count                     |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      sender's octet count                     |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |                 SSRC_1 (SSRC of first source)                 |
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  1    | fraction lost |       cumulative number of packets lost       |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           extended highest sequence number received           |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      interarrival jitter                      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         last SR (LSR)                         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                   delay since last SR (DLSR)                  |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |                 SSRC_2 (SSRC of second source)                |
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  2    :                               ...                             :
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
       |                  profile-specific extensions                  |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • wersja (V): 2 bity – identyfikuje wersje, tak samo jak RTP 2
  • padding (P): 1 bit – wskazuje czy koniec pakietu jest uzupelniony zerami, jesli tak ostatni oktet wskazuje na liczbe oktetow do pominiecia
  • reception report count (RC): 5 bitów – liczba blokow raportujacych w tym pakiecie
  • packet type (PT): 8 bitów – indentyfikuej pakiet RTCP SR (stala wartosc 200)
  • length: 16 bitów – dlugosc pakietu w 32 bitowych słowach właczając nagłówek i wyrównanie
  • SSRC: 32 bity – identyfikator SSRC zródla pakietu SR
  • NTP timestamp: 64 bity – zegarowy czas wyslania pakietu
  • RTP timestamp: 32 bity – okresowy czas wyslania pakietu
  • sender’s packet count: 32 bity – calkowita liczba pakietow RTP wyslanych przez uczest
  • SSRC_n (source identifier): 32 bity – identyfikator SSRC dla zrodla ktorego dotyczy raport
  • fraction lost: 8 bitów – stosunek pakietow odebranych do pakietow spodziewanych RTP
  • cumulative number of packets lost: 24 bity – calkowita liczba wszystkich zgóbionych pakietów RTP
  • xtended highest sequence number received: 32 bity – najwieszky numer sekwencyjny odebranego pakietu
  • interarrival jitter: 32 bity – roznica pomiedzy odstepem w wysylaniu kolejnych pakietow
  • last SR timestamp (LSR): 32 bity – srodkowe 32 bity otrzymane w SR od nadawcy
  • delay since last SR (DLSR): 32 bity – czas pomiedzy odbiorem pakietu SR od nadawcy a nadaniem tego bloku raportujacego
Struktura pakietu RR jest taka sama jak pakietu SR, z tą różnicą że pakiet RR nie zawiera czesci informacyjnej o nadawcy a pole typu pakietu zawiera wartosc 201:
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P|    RC   |   PT=RR=201   |             length            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                     SSRC of packet sender                     |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |                 SSRC_1 (SSRC of first source)                 |
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  1    | fraction lost |       cumulative number of packets lost       |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           extended highest sequence number received           |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      interarrival jitter                      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         last SR (LSR)                         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                   delay since last SR (DLSR)                  |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
report |                 SSRC_2 (SSRC of second source)                |
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  2    :                               ...                             :
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
       |                  profile-specific extensions                  |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Po otrzymaniu raportu w postaci pakietu SR lub RR nadawca moze zmodyfikować na jego podstawie charakterysytke sesji, określić zakres wystepujących problemów, określić skutecznosc w dostarczaniu raportow itp. Dane raportujace moga byc rowniez agregowane przez aplikacje monitorujace nadzorujace wydajnosc sieci.

Pakiety SDES

Pakiet SDES posiada trzy poziomową strukture, w której skład wchodzi nagłówek, zero lub wiecej fragmentów zawierających atrybuty opisujące zródło identyfikowane w danym fragmencie. Każdy fragment zawiera indentyfikator SSRC/CSRC oraz listę atrybótów. Każdy atrybut zawiera 2 8-śmio bitowe pola wskazujace na jego typ oraz dlugość oraz sam tekst, gdzie tekst nie może być dłuższy niż 255 oktetów
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P|    SC   |  PT=SDES=202  |             length            |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
chunk  |                          SSRC/CSRC_1                          |
  1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                           SDES items                          |
       |                              ...                              |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
chunk  |                          SSRC/CSRC_2                          |
  2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                           SDES items                          |
       |                              ...                              |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  • version (V) – wersja, padding (P) – dopełnienie, length – dlugość
  • packet type (PT): 8 bitów – typ pakietu (202)
  • source count (SC): 5 bitów – liczba fragmentów w pakiecie

autor: Tomasz Zieleniewski

You May Also Like

How we use Kotlin with Exposed at TouK

Why Kotlin? At TouK, we try to early adopt technologies. We don’t have a starter project skeleton that is reused in every new project, we want to try something that fits the project needs, even if it’s not that popular yet. We tried Kotlin first it mid 2016, right after reaching 1.0.2 version

Clojure web development – state of the art

It’s now more than a year that I’m getting familiar with Clojure and the more I dive into it, the more it becomes the language. Once you defeat the “parentheses fear”, everything else just makes the difference: tooling, community, good engineering practices. So it’s now time for me to convince others. In this post I’ll try to walktrough a simple web application from scratch to show key tools and libraries used to develop with Clojure in late 2015.

Note for Clojurians: This material is rather elementary and may be useful for you if you already know Clojure a bit but never did anything bigger than hello world application.

Note for Java developers: This material shows how to replace Spring, Angular, grunt, live-reload with a bunch of Clojure tools and libraries and a bit of code.

The repo with final code and individual steps is here.

Bootstrap

I think all agreed that component is the industry standard for managing lifecycle of Clojure applications. If you are a Java developer you may think of it as a Spring (DI) replacement - you declare dependencies between “components” which are resolved on “system” startup. So you just say “my component needs a repository/database pool” and component library “injects” it for you.

To keep things simple I like to start with duct web app template. It’s a nice starter component application following the 12-factor philosophy. So let’s start with it:

lein new duct clojure-web-app +example

The +example parameter tells duct to create an example endpoint with HTTP routes - this would be helpful. To finish bootstraping run lein setup inside clojure-web-app directory.

Ok, let’s dive into the code. Component and injection related code should be in system.clj file:

(defn new-system [config]
  (let [config (meta-merge base-config config)]
    (-> (component/system-map
         :app  (handler-component (:app config))
         :http (jetty-server (:http config))
         :example (endpoint-component example-endpoint))
        (component/system-using
         {:http [:app]
          :app  [:example]
          :example []}))))

In the first section you instantiate components without dependencies, which are resolved in the second section. So in this example, “http” component (server) requires “app” (application abstraction), which in turn is injected with “example” (actual routes). If your component needs others, you just can get then by names (precisely: by Clojure keywords).

To start the system you must fire a REPL - interactive environment running within context of your application:

lein repl

After seeing prompt type (go). Application should start, you can visit http://localhost:3000 to see some example page.

A huge benefit of using component approach is that you get fully reloadable application. When you change literally anything - configuration, endpoints, implementation, you can just type (reset) in REPL and your application is up-to-date with the code. It’s a feature of the language, no JRebel, Spring-reloaded needed.

Adding REST endpoint

Ok, in the next step let’s add some basic REST endpoint returning JSON. We need to add 2 dependencies in project.clj file:

:dependencies
 ...
  [ring/ring-json "0.3.1"]
  [cheshire "5.1.1"]

Ring-json adds support for JSON for your routes (in ring it’s called middleware) and cheshire is Clojure JSON parser (like Jackson in Java). Modifying project dependencies if one of the few tasks that require restarting the REPL, so hit CTRL-C and type lein repl again.

To configure JSON middleware we have to add wrap-json-body and wrap-json-response just before wrap-defaults in system.clj:

(:require 
 ...
 [ring.middleware.json :refer [wrap-json-body wrap-json-response]])

(def base-config
   {:app {:middleware [[wrap-not-found :not-found]
                      [wrap-json-body {:keywords? true}]
                      [wrap-json-response]
                      [wrap-defaults :defaults]]

And finally, in endpoint/example.clj we must add some route with JSON response:

(:require 
 ...
 [ring.util.response :refer [response]]))

(defn example-endpoint [config]
  (routes
    (GET "/hello" [] (response {:hello "world"}))
    ...

Reload app with (reset) in REPL and test new route with curl:

curl -v http://localhost:3000/hello

< HTTP/1.1 200 OK
< Date: Tue, 15 Sep 2015 21:17:37 GMT
< Content-Type: application/json; charset=utf-8
< Set-Cookie: ring-session=37c337fb-6bbc-4e65-a060-1997718d03e0;Path=/;HttpOnly
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< Content-Length: 151
* Server Jetty(9.2.10.v20150310) is not blacklisted
< Server: Jetty(9.2.10.v20150310)
<
* Connection #0 to host localhost left intact
{"hello": "world"}

It works! In case of any problems you can find working version in this commit.

Adding frontend with figwheel

Coding backend in Clojure is great, but what about the frontend? As you may already know, Clojure could be compiled not only to JVM bytecode, but also to Javascript. This may sound familiar if you used e.g. Coffescript. But ClojureScript philosophy is not only to provide some syntax sugar, but improve your development cycle with great tooling and fully interactive development. Let’s see how to achieve it.

The best way to introduce ClojureScript to a project is figweel. First let’s add fighweel plugin and configuration to project.clj:

:plugins
   ...
   [lein-figwheel "0.3.9"]

And cljsbuild configuration:

:cljsbuild
    {:builds [{:id "dev"
               :source-paths ["src-cljs"]
               :figwheel true
               :compiler {:main       "clojure-web-app.core"
                          :asset-path "js/out"
                          :output-to  "resources/public/js/clojure-web-app.js"
                          :output-dir "resources/public/js/out"}}]}

In short this tells ClojureScript compiler to take sources from src-cljs with figweel support and but resulting JavaScript into resources/public/js/clojure-web-app.js file. So we need to include this file in a simple HTML page:

<!DOCTYPE html>
<head>
</head>
<body>
  <div id="main">
  </div>
  <script src="js/clojure-web-app.js" type="text/javascript"></script>
</body>
</html>

To serve this static file we need to change some defaults and add corresponding route. In system.clj change api-defaults to site-defaults both in require section and base-config function. In example.clj add following route:

(GET "/" [] (io/resource "public/index.html")

Again (reset) in REPL window should reload everything.

But where is our ClojureScript source file? Let’s create file core.cljs in src-cljs/clojure-web-app directory:

(ns ^:figwheel-always clojure-web-app.core)

(enable-console-print!)

(println "hello from clojurescript")

Open another terminal and run lein fighweel. It should compile ClojureScript and print ‘Prompt will show when figwheel connects to your application’. Open http://localhost:3000. Fighweel window should prompt:

To quit, type: :cljs/quit
cljs.user=>

Type (js/alert "hello"). Boom! If everything worked you should see and alert in your browser. Open developers console in your browser. You should see hello from clojurescript printed on the console. Change it in core.cljs to (println "fighweel rocks") and save the file. Without reloading the page your should see updated message. Figweel rocks! Again, in case of any problems, refer to this commit.

In the next post I’ll show how to fetch data from MongoDB, serve it with REST to the broser and write ReactJs/Om components to render it. Stay tuned!