Transmisia stream-urilor RTP
Pentru a transmite un stream RTP, trebuie folosit un Processor pentru a obtine o DataSource codata RTP. De asemenea trebuie construit un SessionManager sau un DataSink pentru a controla transmisia.
Intrarea pentru Processor poate fi de la o sursa stocata sau poate proveni de la un dispozitiv de captura. Pentru datele stocate se foloseste MediaLocator pentru a identifica fisierul sursa, cānd se creeaza Processor-ul. Pentru datele capturate este necesara folosirea unei DataSource la intrarea Processor-ului.
Exita doua moduri de transmisie a stream-urilor RTP:
folosirea unui MediaLocator care are parametrii unei sesiuni RTP pentru a construi un DataSink RTP, prin apelarea Manager.createDataSink;
folosirea unui manager de sesiune pentru a creea stream-uri de transmisie pentru continutul si pentru controlul transmisiei.
Daca se folose 535g64f ste un MediaLocator pentru a construi un DataSink RTP, se poate transmite doar primul stream din DataSource. Daca se doreste transmisia de stream-uri multiple īntr-o sesiune sau daca se doreste monitorizarea statisticilor pentru o sesiune, trebuie folosit direct SessionManager-ul.
Indiferent de modul de transmisie a stream-ului RTP, trebuie:
creat un Processor cu o DataSource care reprezinta datele ce trebuie transmise;
configurat acest Processor pentru a transmite la iesire date codate RTP;
considerata iesirea din Processor ca o DataSource.
8.9.1.1. Configurarea Processor-ului
Pentru a configura Processor-ul pentru generarea de date codate RTP, trebuie setate formatele specifice RTP pentru fiecare pista.
Formatele pistelor se seteaza prin obtinerea TrackControl pentru fiecare pista si prin apelarea metodei setFormat. Un format specific RTP se selecteaza prin setarea string-ului de codare īntr-un string de format RTP, cum ar fi: "AudioFormat.GSM_RTP". Processor-ul īncearca sa īncarce un plug-in care suporta acest format. Daca nu este instalat un plug-in potrivit īnseamna ca acel format RTP nu este suportat si o exceptie UnSupportedFormatException este "aruncata". Formatul iesirii se seteaza cu metoda setOutputContentDescriptor. Daca nu sunt necesare multiplexari speciale, descriptorul iesirii poate fi setat pe "ContentDescriptor.RAW". Stream-urile audio si video nu trebuie intercalate (interleave). Daca pistele Processor-ului sunt tipuri de date diferite, fiecare stream media este transmis īntr-o sesiune RTP separata.
8.9.1.2. Obtinerea datelor la iesirea Processor-ului
Odata ce formatul pistei Processor-ului a fost setat si Processor-ul a ajuns īn starea Realized, se poate obtine DataSource-ul de la iesirea Processor-ului. Pentru aceasta trebuie apelata metoda getDataOutput. DataSource-ul returnat poate fi de tip PushBufferDataSource sau PullBufferDataSource, īn functie de sursa datelor.
DataSource-ul obtinut este conectat la SessionManager folosind metoda createSendStream. Managerul de sesiune trebuie sa fie initializat īnainte de a creea stream-ul de transmisie.
Daca DataSource-ul contine mai multe SourceStreams, fiecare SourceStream este transmis ca un stream RTP separat, īn aceeasi sesiune sau īn sesiuni diferite. Daca DataSource-ul contine atāt audio cāt si video, trebuie create sesiuni separate pentru fiecare dintre aceste tipuri de date. De asemenea DataSource-ul se poate clona iar clonele se pot trimite ca stream-uri RTP diferite, īn aceeasi sesiune sau īn sesiuni diferite.
8.9.1.3. Controlul īntarzierii pachetelor
Īntārzierea pachetului, cunoscuta si sub numele de interval de īmpachetare, este timpul petrecut de fiecare pachet RTP de-a lungul transmisiei lui prin retea. Intervalul de īmpachetare determina īntarzierea minima pentru transmisia de la un capat la altul (end-to-end delay). Pachetele mai mari micsoreaza header-ul dar maresc īntārzierile si fac pierderea de pachete mai sesizabila.
Un receptor ar trebui sa accepte pachete reprezentānd īntre 0 si 200 ms de date audio. Aceasta restrictie permite o marime rezonabila a buffer-ului pentru receptor. Fiecare codec de īmpachetare are un interval de īmpachetare implicit, potrivit pentru acel tip de codare.
Daca codec-ul permite modificarea acestui interval, el exporta un PacketSizeControl. Intervalul de īmpachetare poate fi modificat sau setat prin metoda setPacketSize. Pentru stream-urile video, fiecare cadru este transmis īn pachete RTP multiple. Marimea fiecarui pachet este limitata de MTU-ul (Maximum Transmission Unit) retelei. Acest parametru se poate seta prin metoda setPacketSize care apartine de PacketSizeControl.
8.9.2. Transmisia datelor RTP folosind DataSink
Cea mai simpla metoda de a transmite date RTP este sa construim un DataSink RTP folosind metoda Manager.createDataSink. Parametrii sunt DataSource-ul de la iesirea Processor-ului si un MediaLocator care descrie sesiunea RTP. MediaLocator-ul contine addresa si portul sesiunii RTP.
Pentru a controla transmisia, trebuie apelate start si stop asupra DataSink-ului. Doar primul stream din DataSource este transmis.
Īn Exemplul 8.19, se captureaza date audio, dupa care sunt transmise folosind un DataSink.
Exemplul 8.19. Transmisia datelor RTP folosind DataSink |
// cauta un dispozitiv de captura care poate captura audio // pe 8biti la 8Khz AudioFormat format= new AudioFormat(AudioFormat.LINEAR, 8000, 8, 1); Vector devices= CaptureDeviceManager.getDeviceList( format); CaptureDeviceInfo di= null; if (devices.size() > 0) else // creaza un Processor sau exit daca nu il poate crea try catch (IOException e) catch (NoProcessorException e) // configureaza Processorul processor.configure(); // blocheaza pana este configurat processor.setContentDescriptor( new ContentDescriptor( ContentDescriptor.RAW)); TrackControl track[] = processor.getTrackControls(); boolean encodingOk = false; // parcurge pistele si incearca sa scoata date format gsm for (int i = 0; i < track.length; i++) else } else } if (encodingOk) catch (NotRealizedError e) // creaza datasnik RTP si transmite stream-ul audio try catch (Exception e) } |
8.9.3. Transmisia datelor RTP folosind SessionManager
Procesul standard pentru transmisia datelor RTP folosind un manager de sesiune este:
se creeaza un Processor si se seteza formatul fiecarei piste īntr-un format specific RTP;
se obtine DataSource-ul de la iesirea Processor-ului;
se apeleaza metoda createSendStream asupra unui SessionManager care a fost creat si initializat īn prealabil. Ca parametrii se transmit DataSource-ul si un index de stream. Managerul de sesiune creaza un SendStream pentru SourceStream-ul specificat;
se porneste managerul de sesiune prin apelarea SessionManager startSession;
se controleaza transmisia cu metodele SendStream. Se poate īnregistra un SendStreamListener pentru a asculta evenimentele ce apar la SendStream.
8.9.3.1. Crearea unui stream de transmisie
Īnainte ca managerul de sesiune sa transmita date, trebuie sa cunoasca locatia datelor ce urmeaza a fi transmise. Cānd se construieste un nou SendStream, acesta trebuie sa cunoasca DataSource-ul. Din moment ce un DataSink poate contine stream-uri multiple, trebuie specificat si indexul stream-ului ce urmeaza a fi transmis īn aceasta sesiune. Se pot creea stream-uri de transmisie multiple prin transmiterea unor DataSource diferite prin apelarea createSendStream sau prin specificarea mai multor indecsi de stream.
Managerul de sesiune interogheaza formatul SourceStream-ului pentru a determina daca are un tip de date īnregistrat pentru acest format. Daca datele nu sunt un format RTP sau daca tipul datelor nu poate fi localizat īn formatul RTP, o exceptie UnSupportedFormatException este "aruncata".
Multe scenarii de folosire a protocolului RTP implica transmisia unui stream īn mai multe sesiuni RTP sau codarea unui stream īn formate multiple si transmisia lui īn mai multe sesiuni RTP. Cānd un stream codat īntr-un singur format trebuie transmis īm sesiuni RTP multiple, trebuie clonata DataSource-ul de la iesirea Processor-ului de unde se captureaza datele. Aceasta se realizeaza prin creearea unei DataSource clonabile prin intermediul Manager-ului si prin apelarea metodei gerClone asupra DataSource-ului clonat. Un nou Processor poate fi creat pentru fiecare DataSource clonat, pistele pot fi codate īn formatul dorit si fiecare stream poate fi transmis īntr-o sesiune RTP.
Daca se doreste mixarea stream-urilor multiple de acelasi tip (cum ar fi audio) īntr-un singur stream, trebuie folosit un mixer RTP. Daca stream-urile ce trebuie mixate provin de la DataSource-uri multiple, trebuie creat un MergingDataSource din DataSource-ele separate si transmise spre SessionManager, care creeaza stream-ul.
8.9.3.2. Controlul unui stream de transmisie
Pentru a controla SendStream-ul se folosesc metodele RTPStream start si stop. La pornirea unui SendStream īncepe transmisia datelor īn retea. La oprirea acestuia transmisia este oprita. Pentru a īncepe o transmisie RTP, fiecare SendStream trebuie pornit.
Daca DataSource-ul este pornit independent īn timp ce SendStream-ul este oprit, datele vor fi ignorate (PushBufferDataSource) sau nu vor fi cerute (PullBufferDataSource) de managerul de sesiune. Īn acest timp, nu vor fi transmise date īn retea.
8.9.3.3. Transmisia datelor audio capturate īntr-o singura sesiune
Exemplul 8.20. arata cum se realizeaza captura si transmisia īntr-o sesiune RTP a unui stream audio mono.
Exemplul 8.20. Transmisia datelor audio capturate īntr-o singura sesiune |
// Avem nevoie de DataSource pt. a captura audio AudioFormat format = new AudioFormat(AudioFormat.ULAW, 8000, 8, 1); Vector devices= CaptureDeviceManager.getDeviceList( format); CaptureDeviceInfo di= null; if (devices.size() > 0) else // creeaza un processor pt. dispozitivul de captura // si iese daca nu il poate crea try catch (IOException e) catch (NoProcessorException e) // processorul trece in starea realize si il blocam pana // trece in starea configured processor.configure(); processor.setContentDescriptor( new ContentDescriptor( ContentDescriptor.RAW)); TrackControl track[] = processor.getTrackControls(); boolean encodingOk = false; // parcurge pistele si incearca sa seteze una dintre ele la // iesire date ULAW_RTP for (int i = 0; i < track.length; i++) else } else } processor.realize(); // il blocheaza pana ajunge la realized // obtinem datasource-ul de iesire si iese daca da eroare DataSource ds = null; try catch (NotRealizedError e) // Creeaza un SessionManger si primeste DataSource-ul // pentru a crea SendStream-ul SessionManager rtpsm = new com.sun.media.rtp.RTPSessionMgr(); // Managerul de sesiune care trebuie initializat si pornit // rtpsm.initSession(...); // rtpsm.startSession(...); try catch (IOException e) catch( UnsupportedFormatException e) |
8.9.3.4. Transmisia datelor audio capturate īn sesiuni multiple
Exemplul 8.21. ilustreaza codarea datelor audio si transmiterea acestora īn sesiuni RTP multiple. Datele sunt codate īn format gsm.
Exemplul 8.21 Transmisia datelor audio capturate īn sesiuni multiple īn format gsm |
// cauta un dispozitiv de captura care poate captura audio // pe 8biti la 8Khz AudioFormat format= new AudioFormat(AudioFormat.LINEAR, 8000, 8, 1); Vector devices= CaptureDeviceManager.getDeviceList( format); CaptureDeviceInfo di= null; if (devices.size() > 0) else // creeaza un processor pentru disp. de captura // sau exit daca nu il poate crea try catch (IOException e) catch (NoProcessorException e) // configureaza processorul processor.configure(); // il blocheaza pana ajunge configured processor.setContentDescriptor( new ContentDescriptor( ContentDescriptor.RAW)); TrackControl track[] = processor.getTrackControls(); boolean encodingOk = false; // parcurge pistele si incearca sa gaseasca una care // care poate prelucra date format gsm for (int i = 0; i < track.length; i++) else } else } // acum stim unde putem trimite date gsm si unde nu // trece processor in starea realize if (encodingOk) catch (NotRealizedError e) // vrem sa transmitem acest stream in doua sesiuni RTP // trebuie sa clonam datasource-ul // SessionManagerul preia controlul asupra clonei DataSource cloneableDataSource = null; DataSource clonedDataSource = null; cloneableDataSource = Manager.createCloneableDataSource(origDataSource); clonedDataSource = ((SourceCloneable)cloneableDataSource).createClone(); // creeaza primul SessionManager si preia prima datasource // creerea SendStream-ului SessionManager rtpsm1 = new com.sun.media.rtp.RTPSessionMgr(); // managerul de sesiune care trebuie initializat si pornit // rtpsm1.initSession(...); // rtpsm1.startSession(...); try catch (IOException e) catch( UnsupportedFormatException e) try catch (IOException e) // creeaza al doilea RTPSessionMgr si preia controlul asupra // datasource-ului clonat if (clonedDataSource != null) catch (IOException e) catch( UnsupportedFormatException e) } } else |
8.9.4. Transmisia datelor RTP cu RTPSocket
Pentru a transmite date RTP se mai poate folosi RTPSocket. Pentru a folosi RTPSocket pentru transmisie, trebuie creat un DataSink RTP folosind createDataSink prin alocarea unui MediaLocator cu un nou protocol care este o varianta a RTP, "Ratibor". Managerul īncearca sa construiasca un DataSink din:
Acest manager de sesiune pregateste pachete individuale RTP pentru a fi pregatite sa fie transmise īn retea si le transmite la RTPSocket, care este creat din:
<protocol package-prefix>.media.protocol.rtpraw.DataSource
RTPSocket-ul creat la <protocol-prefix>.media.protocol.rtpraw. DataSource
este implementarea proprie a RTPSocket. API-ul JMF nu defineste
o implemetare implicita a RTPSocket.
Implementarea RTPSocket este
dependenta de protocolul din stratul transport pe care īl folosim.
Clasa RTPSocket
trebuie localizata la <protocol-prefix>.media.
protocol.rtpraw.DataSource
. Utilizatorul este responsabil pentru
transmiterea de pachete RTP īn retea.
Exemplul 8.22 ilustreaza modul īn care RTPSocket este folosit pentru a transmite datele audio capturate.
Exemplul 8.22 Transmisia datelor RTP folosind RTPSocket |
// cauta un dispozitiv de captura care poate captura audio // pe 8biti la 8Khz AudioFormat format = new AudioFormat(AudioFormat.LINEAR, 8000, 8, 1); Vector devices= CaptureDeviceManager.getDeviceList( format); CaptureDeviceInfo di= null; if (devices.size() > 0) else // creaza un processor pentru disp. de captura si paraseste // aplicatia daca nu il poate creea try catch (IOException e) catch (NoProcessorException e) // configureaza processor-ul processor.configure(); // se blocheaza pana ajunge configured processor.setContentDescriptor( new ContentDescriptor( ContentDescriptor.RAW)); TrackControl track[] = processor.getTrackControls(); boolean encodingOk = false; // parcurge pistele si incearca sa programeze una // sa prelucreze datele gsm for (int i = 0; i < track.length; i++) else } else } // processor-ul trece in starea realize if (encodingOk) catch (NotRealizedError e) // managerul primeste datasource-ul si creeaza un // datasink RTP try catch (Exception e) } |
|