Sunteți pe pagina 1din 102

8.

Java Media Framework

8.1. Introducere n Java Media Framework

Java Media FrameworkTM (pe scurt JMF) este un API care folosete
la interconectarea multimedia folosind mediul Java. Acest mediu de dezvoltare
face parte dintr-unul mult mai amplu, numit Java Media, care conine mai multe
API-uri. Acestea sunt urmtoarele:

Nume API Ce aduce nou


Java 2D - manipulare de imagini i grafic 2D
- motor grafic 3D orientat obiectual
Java 3D
- introducere suport VRML
Java Media - suport pentru playback multimedia (versiunea 1.0)
Framework - suport captur, streaming audio i video (versiunea 2.0)
Java Sound - procesor software de sunet i sintetizator MIDI
Java Speech - recunoatere vocal i sintetizare
Java Advanced
- implementeaz multe interfee Java 2D
Imaging
Java Image I/O - posibilitate extins la lucrul cu imagini
Java Telephony - integrarea legturii computer-telefon
Java Shared Data - permite integrarea legturilor ntre aplicaii care se
Toolkit bazeaz pe tehnologia Java
Tabel 8.1. Componentele Java Media

Prima versiune de API JMF a fost versiunea 1.0, versiune care a fost
dezvoltat de ctre SUN Microsystems Inc, Silicon Graphics Inc i Intel
Corporation. Aceast versiune permitea programatorilor s scrie cod cu care s
poat manipula procese multimedia n timp real.
Urmtoarea versiune de API JMF a fost versiunea 2.0, dezvoltat de
ctre SUN Microsystems Inc n colaborare cu IBM Corporation. Fa de
versiunea anterioar, specialitii au introdus i suport pentru captur, prelucrare
n timp real i au extins lucrul cu codec-urile. Dar, poate cea mai revoluionar
modificare este introducerea suportului pentru protocolul RTP (Real-Time
Transfer Protocol).

1
8.2. Lucrul cu date dependente de timp (Time-Based
Media)
Orice date care i modific structura n timp pot fi considerate ca
dependente de timp (time based). Clip-urile audio, secvenele MIDI, filmele i
animaiile sunt forme comune ale datelor dependente de timp. Aceste date pot
proveni dintr-o varietate de surse, cum ar fi fiiere locale sau din reea, camere
video sau microfoane etc.

Figura 8.1. Modelul de procesare a datelor


O caracteristic fundamental a datelor dependente pe timp este c
necesit livrarea informaiei n timpi strici (dead-lines) pentru procesare i
livrare a rezultatelor. Din acest motiv, datele multimedia dependente de timp
poart denumirea de streaming media, n sensul c trebuie transmis un stream
care trebuie recepionat i prelucrat ntr-un interval de timp care s duc la
obinerea unui rezultat rezonabil.
Spre exemplu, cnd este rulat un film, dac informaia nu poate fi
prelucrat i transmis suficient de rapid, pot aprea ntrzieri (delays) la rularea
filmului. Pe de alt parte, dac datele nu pot fii recepionate i prelucrate
suficient de rapid, rularea poate prea instabil deoarece peste unele cadre se sare
intenionat (dropped frames) n tentativa de a menine numrul de cadre pe
secund (playback rate) constant.
8.2.1. Stream-uri media
Un stream media este de fapt o secven de date multimedia care poate
fi obinut dintr-un fiier local, din reea sau capturat de la o camer video sau
un microfon.

2
De obicei, stream-urile media conin canale multiple de date care se
numesc piste (tracks). Aceste stream-uri media care conin mai multe piste
poart n general denumirea de stream-uri media multiplexate sau complexe.
Demultiplexarea este procesul de extragere a unei piste individuale dintr-un
stream media complex.
Tipul pistei identific tipul de date pe care acesta l conine, de exemplu
video sau audio.
Un stream media poate fi identificat prin locaia n care se afl i
protocolul folosit la accesarea lui. Spre exemplu, un URL ar putea fi folosit
pentru a descrie locaia unui fiier de tipul QuickTime ntr-un sistem local sau
aflat la distan. Dac fiierul este local, poate fi accesat prin protocolul FILE. Pe
de alt parte, dac fiierul se afl pe un server WEB, poate fi accesat prin
protocolul HTTP. Un locator media (media locator) este o cale de a identifica
locaia unui stream media n cazul n care un URL nu poate fi folosit.
Stream-urile media pot fi clasificate n funcie de modul n care datele
sunt distribuite:

de tipul pull (trase), caz n care transferul de date este iniiat i


controlat de ctre client. Spre exemplu, HTTP (Hypertext Transfer
Protocol) i FILE sunt protocoale de tipul pull.
de tipul push (mpinse), caz n care server-ul iniiaz i
controleaz transferul de date. Spre exemplu, RTP (Real-time Transport
Protocol) este este un protocol de tip push folosit pentru stream-ingul
media. n mod similar, protocolul SGI Media Base este un protocol de
tip push folosit la VOD (Video-On-Demand).

Urmtoarele tabele identific o parte din caracteristicile formatelor


media uzuale. Cnd se alege un format anume in ideea utilizrii lui, este
important s se in cont de caracteristicile acelui format, de mediul cruia i este
destinat i de ateptrile audienei. De exemplu, dac se dorete transmiterea de
coninut media prin Internet, trebuie inut cont de lrgimea de band.
Coloana Solicitare Procesor reprezint puterea de procesare necesar
pentru procesarea optim a formatului specificat. Coloana Lrgime de band
reprezint viteza de transmisie necesar pentru a trimite i a recepiona date
suficient de rapid pentru o prezentare optim.

Lrgime de
Format Tip coninut Calitate Solicitare Procesor
band
AVI
Cinepak Medie Sczut Ridicat
QuickTime
MPEG-1 MPEG Ridicat Ridicat Ridicat

3
Lrgime de
Format Tip coninut Calitate Solicitare Procesor
band
AVI
H.261 Sczut Medie Medie
RTP
QuickTime
H.263 AVI Medie Medie Sczut
RTP
QuickTime
JPEG AVI Ridicat Ridicat Ridicat
RTP
QuickTime
Indeo Medie Medie Medie
AVI
Tabel 8.2. Formate video uzuale

Tip Lrgime de
Format Calitate Solicitare Procesor
coninut band

AVI
PCM QuickTime Ridicat Sczut Ridicat
WAV
AVI
QuickTime
Mu-Law Sczut Sczut Ridicat
WAV
RTP
ADPCM AVI
(DVI, QuickTime Medie Medie Medie
IMA4) WAV, RTP
MPEG-1 MPEG Ridicat Ridicat Ridicat
MPEG
MPEG Ridicat Ridicat Medie
Layer3
WAV
GSM Sczut Sczut Sczut
RTP
WAV
G.723.1 Medie Medie Sczut
RTP
Tabel 8.3. Formate audio uzuale
Unele dintre aceste formate au fost proiectate pentru anumite aplicaii i
necesiti. Formatele care necesit calitate i lime de band ridicate sunt n
4
general destinate aplicaiilor stocate local sau pe CD-uri. Formatele H.261 i
H.263 sunt folosite n general la aplicaii de tip video conferin (video
conferencing) i sunt optimizate pentru stream-uri video care nu implic mult
aciune. Similar, G.723 este folosit pentru a stoca/transmite cuvinte la o rat
mic pentru aplicaiile telefonice.
8.2.2. Prezentarea datelor de tip media
n majoritatea cazurilor datele media sunt de tip audio sau video i pot fi
prezentate prin intermediul dispozitivelor de ieire (output devices), cum ar fi
boxele (speakers) sau monitorul. Stream-urile media pot fi trimise i spre alte
destinaii, spre exemplu pot fi salvate ntr-un fiier sau transmise prin reea. n
general aceste destinaii poart numele de data sink.
8.2.2.1. Controlul prezentrii
n timpul prezentrii unui stream media, se folosesc controale de genul
celor folosite la VCR pentru a permite utilizatorului controlul rulrii. Spre
exemplu, un panou de comand pentru un film ar trebui s aib funcii care s
permit pornirea, oprirea, i derularea nainte/napoi a filmului.
8.2.2.2. Latena
n multe cazuri, de obicei cnd este necesar prezentarea unui stream
media care provine din reea, prezentarea stream-ului media nu poate ncepe
imediat. Timpul scurs pn la nceperea prezentrii poart numele de laten de
start (start latency). Pentru utilizatori aceast laten este sesizabil ca timpul
scurs ntre momentul n care s-a apsat pe butonul de start i momentul n care
ncepe rularea efectiv.
Prezentrile multimedia combin de obicei cteva tipuri de date
dependente de timp ntr-o prezentare sincronizat. De exemplu, n timpul unui
slide-show poate rula n fundal muzic, sau, n cadrul unui clip audio sau video,
se poate face sincronizarea cu un text animat. Cnd prezentarea unor stream-uri
media multiple este sincronizat, este esenial s se in cont de latena fiecrui
stream, n caz contrar rularea diferitelor stream-uri putnd ncepe la momente
diferite.

8.2.2.3. Calitatea prezentrii


Calitatea prezentrii stream-urilor media depinde de mai muli factori,
printre care:
schema de compresie utilizat
capabilitatea de rulare a sistemului care ruleaz stream-urile
respective
lrgimea benzii disponibile (pentru stream-urile media primite
prin reea)

5
n general, cu ct este mai mare calitatea, cu att este mai mare fiierul
i sunt necesare o putere de calcul mai ridicat i o lrgime de band mai mare.
Lrgimea de band este n general reprezentat de numrul de bii care sunt
transmii ntr-o anumit perioad de timp (bit rate).
Pentru a realiza prezentri video de nalt calitate, numrul de cadre
(frames) afiat n fiecare perioad de timp (frame rate) trebuie s fie ct mai
mare posibil. n general, filme care au 30 de cadre pe secund sunt considerate
insesizabil diferite de distribuiile TV pe scar larg sau de casetele video (ca i
senzaie de continuitate, nu ca i calitate).
8.2.3. Procesarea datelor multimedia
n majoritatea cazurilor asupra datele din stream-ul media se efectueaz
diverse operaii de procesare nainte de a fi prezentate utilizatorului. n general,
acestea sunt:

dac stream-ul este mutiplexat, sunt extrase pistele individuale;


dac pistele individuale sunt comprimate, ele sunt decodate;
dac este necesar, pistele sunt convertite ntr-un format diferit;
asupra pistelor decodate se aplic filtre de efect (dac se
dorete).

Apoi, pistele sunt transmise dispozitivului de ieire adecvat. Dac


stream-ul media trebuie stocat n loc de a fi transmis unui dispozitiv de ieire,
stagiile de procesare difer puin. Spre exemplu, dac se dorete realizarea
capturii audio i video de la o camer, apoi procesarea i n final salvarea ei ntr-
un fiier se efectueaz urmtoarele operaii:

se realizeaz capturarea pistelor audio i video;


se aplic filtrele de efect pistelor neprelucrate (dac se
dorete);
pistele individuale se codeaz;
pistele comprimate sunt multiplexate ntr-un singur stream;
media
stream-ul media multiplexat se salveaz ntr-un fiier.
8.2.3.1. Multiplexoare i demultiplexoare
Un demultiplexor extrage pistele individuale ale datelor de tip media
dintr-un stream media multiplexat. Un multiplexor execut funcia invers, adic
ia pistele individuale i le combin ntr-un singur stream media multiplexat.
8.2.3.2. Codec-urile
Un codec execut operaiile de comprimare/decomprimare a datelor de
tip media. Cnd o pist este codat, ea este convertit ntr-un format comprimat
6
potrivit pentru stocare sau transmisie. Cnd este decodat, este convertit ntr-un
format necomprimat (raw), care este potrivit pentru prezentare.
Fiecare codec are anumite formate de intrare pe care le poate trata i
anumite formate de ieire pe care le poate genera. n anumite situaii, o serie de
codec-uri pot fi utilizate pentru a realiza conversia dintr-un format n altul.
8.2.3.3. Filtrele de efect
Un filtru de efect modific datele dintr-o pist ntr-un anumit mod, de
obicei pentru a crea efecte speciale cum ar fi cel de estompare (blur) sau ecou
(echo).
Filtrele de efect pot fi categorizate ca filtre pre-procesare sau filtre post-
procesare, dup cum sunt folosite, nainte sau dup ce codec-ul proceseaz pista.
n general, filtrele de efect sunt aplicate datelor necomprimate (raw data).
8.2.3.4. Redarea
Redarea (rendering) este procesul prin care rezultatul final este
prezentat utilizatorului. Pentru audio, dispozitivul care se ocup de redare
(rendering device) este de obicei placa de sunet a calculatorului, care trimite
sunetul spre boxe. Pentru video, dispozitivul care se ocup de redare este de
obicei monitorul calculatorului.
Anumite dispozitive specializate suport mixarea (compositing).
Mixarea datelor dependente de timp este procesul prin care se combin track-uri
multiple de date ntr-un singur mediu de prezentare. Spre exemplu, suprapunerea
unui text peste o secven video este o form comun de mixare. Mixarea poate
fi realizat att hardware ct i software. Spre exemplu, pentru mixarea hardware
se pot folosi plci de achiziie profesionale (cele mai cunoscute sunt cele produse
de MIRO) sau software specializat (cel mai cunoscut este Adobe Premiere,
produs al firmei Adobe)
8.2.4. Captura
Datele media dependente de timp pot fi capturate direct de la o surs
pentru procesare i rulare. Spre exeplu, date audio pot fi capturate de la un
microfon, n timp ce datele video pot fi obinute de la o camer video. Captura
poate fi definit ca i faza de intrare pentru modelul de procesare media.
Un dispozitiv de captur poate captura stream-uri media multiple. Spre
exemplu, o camer video poate captura att video ct i audio. Captura i
manipularea acestor stream-uri se poate face separat sau mpreun, caz n care
track-urile video i audio sunt combinate ntr-un singur stream multiplexat.
Pentru realizarea capturii datelor de tip media dependente de timp este
necesar hardware specializat. Spre exemplu, pentru a realiza captur audio sunt
necesare un microfon i o plac de sunet. Similar, pentru realizarea capturii video
este necesar un TV Tuner care suport realizarea de captur video.
Dispozitivele de captur pot fi categorizate ca surse de tip push sau
pull. Spre exemplu, o camer video este o surs de tip pull, n sensul c
7
utilizatorul controleaz nceperea/terminarea capturii. Un microfon este o surs
de tip push deoarece el capureaz continuu stream audio.
Formatul stream-ului media capturat depinde de procesarea fcut de
dispozitivul de captur. Unele dispozitive fac foarte puine prelucrri i transmit
datele mai departe necomprimate. Alte dispozitive de captur execut operaii de
comprimare nainte de transmisie.
Controlul capturii este necesar pentru a permite utilizatorului s
administreze procesul de captur. Spre exemplu, un panou de control a capturii
poate permite utilizatorului s specifice rata i tipul codrii care s se foloseasc
pentru stream-ul capturat i, de asemenea, s porneasc i s opreasc procesul
de captur.
8.3. Principiile Java Media Framework
JMF se bazeaz pe o arhitectur unificat i pe protocoale de
management pentru administrarea proceselor de achiziie, procesare i transmisie
a datelor media dependente de timp. JMF este conceput pentru a suporta
majoritatea standardelor media cum ar fi: AIFF, AU, AVI, GSM, MIDI, MPEG,
QuickTime, RMF i WAV.
Prin exploatarea avantajelor platformei JAVA, JMF respect standardul
Scrie odat, ruleaz oriunde (Write Once, Run Anywhere TM), el adresndu-
se programatorilor care doresc s utilizeze audio i video n programele Java.
Cu JMF se pot crea uor applet-uri i aplicaii capabile s prezinte,
captureze, manipuleze i stocheze date media dependente de timp. Programatorii
au posibilitatea de a procesa datele dup voia lor, i de asemenea:

pot extinde JMF s suporte formate adiionale;


pot modifica i optimiza formatele suportate;
pot crea mecanisme noi de prezentare.
8.3.1. Arhitectura de nivel nalt
Dispozitivele cum este VCR-ul (Video Cassete Recorder) sunt des
ntlnite pentru realizarea operaiilor de nregistrare, procesare i prezentare a
datelor media dependente de timp. Cnd se ruleaz un film folosind un VCR,
stream-ul media provine de la caseta video. VCR-ul citete i interpreteaz datele
de pe caset i trimite semnalul adecvat spre televizor i boxe.
n Figura 8.2 este prezentat acest proces de nregistrare, procesare i
prezentare.

8
Figura 8.2. Procesul de nregistrare, procesare i prezentare
JMF se bazeaz pe acest model fundamental. Sursa datelor folosit n
JMF ncapsuleaz stream-ul media ca i caseta video despre care vorbeam mai
devreme. Player-ul din JMF are aceleai funcii i mecanisme de control ca i un
VCR. Rularea i captura audio i video din JMF necesit dispozitive de intrare i
ieire similare, cum ar fi microfonul, camera, boxele i monitorul.
Sursele datelor i dispozitivele de rulare sunt parte integrant a
arhitecturii API de nalt nivel a JMF. Aceast arhitectur de nivel nalt se refer
la componentele standard din JMF. Arhitectura JMF de nivel jos se refer la
posibilitatea programatorului de a dezvolta sau extinde instrumente de lucru
proprii. Aceast arhitectur multistrat permite dezvoltatorilor de programe Java
s aib la dispoziie un API uor de utilizat i de integrat n aplicaii Java,
beneficiind n continuare de flexibilitatea i extensibilitatea necesar pentru
crearea unor aplicaii media avansate i uor upgrade-abile.
8.3.1.1. Modelul Time (Timp)
JMF controleaz timpul cu o precizie de nanosecunde. Un anumit punct
particular n timp este vzut n general ca un obiect Time, dei exist i cteva
clase care suport specificarea timpului n nanosecunde.
Clasele care suport modelul de timp JMF implementeaz interfaa
Clock pentru a ine cont de timpul necesar pentru o anumit operaie efectuat
asupra unui stream media. Interfaa Clock definete operaiile de baz de
cronometrare i sincronizare, care sunt necesare pentru a controla prezentarea
media.

9
Figura 8.3. Modelul Time la Java Media Framework
Un Clock folosete TimeBase pentru a contoriza trecerea timpului n
timp ce un stream media este prezentat. Singura informaie pe care TimeBase o
ofer este timpul curent, care poart numele de time-base time. Acest timp nu
poate fi oprit sau resetat. De obicei, timpul oferit de TimeBase se bazeaz pe
ceasul sistemului, ora exact fiind aflat de la BIOS-ul calculatorului.
Media time reprezint poziia curent n cadrul unui stream media
(nceputul stream-ului este media time zero, sfritul stream-ului fiind maximul
media time pentru acel stream). Durata stream-ului media este timpul scurs ntre
start i stop, adic lungimea de timp necesar pentru prezentarea stream-ului
media.
Pentru a cunoate timpul curent, Clock folosete:

timpul de start al bazei de timp timpul pe care TimeBase l


raporteaz ca nceput al prezentrii;
timpul de start al datelor poziia din stream-ul media cnd
prezentarea ncepe;
rata de playback ct de rapid Clock-ul ruleaz fa de
TimeBase. Rata este un factor scalar care se aplic la TimeBase. Spre
exemplu, o rat de 1.0 reprezint rata de playback normal pentru
stream-ul media, n timp ce o rat de 2.0 indic c prezentarea va rula
de dou ori mai rapid dect rata normal. O rat negativ indic faptul
c Clock-ul ruleaz n direcie opus de TimeBase-ul lui (spre exemplu,
se folosete la derularea napoi).

Cnd ncepe prezentarea, timpul media este mapat la timpul de baz i


avansarea timpului de baz este folosit pentru a msura scurgerea timpului. n
timpul prezentrii, timpul media curent este calculat folosind urmtoarea
formul:

10
MediaTime=MediaStartTime+Rate(TimeBaseTime-TimeBaseStartTime)

Cnd prezentarea se oprete, timpul media se oprete i el, dar timpul de


baz media continu s avanseze. Dac prezentarea este repornit, timpul media
se remapeaz n funcie de timpul media de baz curent.
8.3.1.2. Managerele
API-ul JMF const n special din interfee care definesc comportarea i
interaciunea obiectelor folosite pentru a captura, a procesa i prezenta date
media dependente de timp. Prin folosirea unor interfee intermediare numite
managere, JMF uureaz integrarea implementrilor interfeelor importante care
pot fi folosite prin intercalarea cu clasele existente.
JMF folosete patru managere:

Manager Managerul general, trateaz construirea Player-


elor, Procesoarelor, DataSources i DataSinks. Din perspectiva
clientului, aceste obiecte sunt create ntotdeauna prin aceleai metode,
indiferent dac obiectul este construit dintr-o implementare existent
sau una nou creat de utilizator;
PackageManager Managerul de pachete, menine un registru
cu pachete ce conin clasele JMF, cum ar fi Player-ele, Procesoarele,
DataSources i DataSinks;
CaptureDeviceManager Managerul de dispozitive de
captur, menine un registru care conine toate dispozitivele de captur
disponibile;
PlugInManager Managerul de plug-in-uri, menine un
registru cu toate plug-in-urile de procesare JMF disponibile, cum ar fi
Multiplexoarele, Demultiplexoarele, Codec-urile, Effect i Render.

Pentru a scrie programe bazate pe JMF, trebuie apelat la Managere


pentru a crea metode pentru construirea Player-elor, Procesoarelor, DataSource-
lor i DataSink-urilor necesare aplicaiilor.
Dac se face captur de la un dispozitiv, se folosete
CaptureDeviceManager pentru a afla ce dispozitive sunt disponibile i pentru a
accesa informaiile disponibile despre acestea. Dac se dorete i controlarea
proceselor ce au loc asupra datelor, trebuie folosit PlugInManager-ul pentru a
afla ce plug-in-uri au fost nregistrate.
Pentru a extinde funcionalitatea JMF-ului prin implementarea unui
plug-in nou, acesta trebuie nregistrat cu PlugInManager-ul pentru a-l face
diponibil pentru Procesoare. Pentru a modifica un Player, un Procesor,
DataSource sau DataSink, trebuie nregistrat prefixul package-ului din care face
parte. Aceasta se face folosind PackageManager-ul.

11
8.3.1.3. Modelul Event (Eveniment)
JMF utilizeaz un mecanism structurat de raportare a evenimentelor
pentru a informa programele despre starea curent a sistemului media i pentru a
le permite acestora s rspund erorilor aprute. Ori de cte ori un obiect JMF
trebuie s raporteze starea sa, el genereaz un MediaEvent. MediaEvent este
alctuit n aa fel nct s poat clasa toate tipurile particulare de evenimente.
Pentru fiecare tip de obiect care poate genera un MediaEvent, JMF
definete o interfa de ascultare (listener) corespunztoare.
Obiectele cu funcii de control, cum sunt Player-ele sau Procesoarele
genereaz evenimente media.

Figura 8.4. Modelul Event n JMF

8.3.1.4. Modelul Data (Date)


Player-ele JMF folosesc de obicei DataSources pentru a administra
transferul datelor de tip media. Un obiect DataSource ncapsuleaz att
informaii despre locaia unde se afl sursa media ct i despre protocolul i
software-ul care trebuie folosit pentru transferul datelor. Odat accesat, o surs
nu poate fi refolosit pentru a transfera alte date. Un obiect DataSource este
identificat fie printr-un MediaLocator JMF fie printr-un URL (Universal
Resource Locator). Un MediaLocator este similar unui URL i chiar poate fi
construit dintr-un URL. El poate fi construit chiar dac protocolul corespunztor
nu este instalat n sistem, spre deosebire de URL, care necesit existena
protocolului.
Un obiect DataSource administreaz un set de obiecte SourceStream. O
surs standard folosete o matrice de tip byte ca unitate de transfer. O surs de
date de tip Buffer folosete un obiect tampon ca unitate de transfer. JMF
definete mai multe tipuri de obiecte DataSource.

12
Figura 8.5. Modelul Data din JMF
Datele pot fi obinute dintr-o varietate de surse, cum ar fiiere locale sau
din reea sau din transmisii n direct. Sursele de date pot fi categorizate dup
modul n care transferul este iniiat:

surse de date de tip Pull: clientul iniiaz transferul de date i


controleaz fluxul datelor. Protocoalele folosite n acest caz sunt HTTP
(Hypertext Transfer Protocol) i FILE. JMF definete dou tipuri de
surse de date de tip Pull: PullDataSource i PullBufferDataSource, care
folosete un buffer ca unitate de transfer;
surse de date de tip Push: server-ul iniiaz transferul de date i
controleaz fluxul datelor Acestea pot fi date de tip multicast, broadcast
sau VOD (Video On Demand). Pentru datele de tip broadcast, un
protocol folosit este Real-time Transport Protocol (RTP), protocol care
a fost dezvoltat de ctre Internet Engineering Task Force (IETF). Pentru
VOD se folosete protocolul MediaBase dezvoltat de ctre SGI. JMF
definete dou tipuri de date de tip Push: PushDataSource i
PushBufferDtaSource, care folosete un obiect de tip Buffer ca unitate
de transfer.
Gradul de control pe care l are un program client depinde de tipul de
date pe care le conine sursa. Spre exemplu, un fiier MPEG permite
repoziionarea i deci un program client poate permite utilizatorului s ruleze din
nou sau s ruleze dintr-o alt poziie. Spre deosebire de acest caz, o transmisie de
date de tip broadcast este sub controlul server-ului i nu permite modificri n
rulare ca i n cazul anterior. Anumite protocoale VOD permit funcii de control
limitate.

13
JMF definete dou tipuri de surse de date speciale, surse de date
clonabile (cloneable) i surse de date combinate (merging).
O surs de date clonabil poate fi folosit pentru a crea diferite clone
pentru surse de date de tipul Pull sau Push. Pentru a realiza aceast operaie,
trebuie apelat metoda Manager createCloneableDataSource. n continuare
toate operaiile se refer doar la clon. Obiectul original DataSource nu mai este
folosit direct. Sursele de date clonabile implementeaz interfaa
SourceCloneable, care definete o metod, createClone. Prin apelarea acestei
metode se pot crea orict de multe clone ale DataSource.
Clonele nu trebuie s aib neaprat aceleai proprieti ca i sursa
original. Aceste proprieti pot fi modificate dup clonare. O surs
MergingDataSource poate fi folosit pentru a combina stream-urile din mai
multe surse ntr-o singur surs. Aceasta permite ca un set de surse de date s
poat fi administrat dintr-un singur punct de control cnd connect, disconnect,
start sau stop sunt apelate, apelrile se refer la sursele de date combinate.
Pentru a construi o surs de date combinate, trebuie apelat metoda
createMergingDataSource. Pentru a asigura reuita operaiunii, trebuie ca toate
datele s fie de acelai tip. Spre exemplu, nu se pot combina date de tip Pull cu
date de tip Push. Durata sursei combinate este egal cu durata maxim a
obiectelor combinate.
Formatul exact al unui obiect este reprezentat de un obiect de tip
Format. Formatul nsui nu conine informaii despre codare sau despre durat,
ci doar descrie numele codrii i tipul de date necesare formatului.
JMF extinde Format pentru a defini formate audio i video specifice,
dup cum se poate vedea n Figura 8.6.

Figura 8.6. Formatul datelor n JMF

14
Un AudioFormat descrie atributele specifice unui format audio, ca rata
(sample rate) sau numrul de canale. Un VideoFormat ncapsuleaz informaii
referitoare la datele video. Mai multe formate sunt derivate din VideoFormat
pentru a descrie atributele formatelor video uzuale, incluznd:
IndexedColorFormat
RGBFormat
YUVFormat
JPEGFormat
H261Format
H263Format
Pentru a primi informaii despre schimbrile de format de la un
Controller, trebuie implementat o interfa ControllerListener i urmrite
evenimentele FormatChangeEvents.
8.3.1.5. Interfaa Control
Interfaa JMF Control prevede un mecanism pentru setarea i
interogarea atributelor unui obiect. Un Control permite de obicei accesul la
componenta interfeei utilizator corespondent, ceea ce ajut utilizatorul s
controleze atributele obiectului.
Orice obiect JMF care vrea s permit acces la obiectele Control
corespunztoare lui trebuie s aib implementat interfaa Controls. Aceast
interfa definete metode pentru obinerea obiectelor Control asociate.
JMF definete interfeele Control standard din Figura 8.8. Interfaa
CachingControl permite afiarea i monitorizarea progresului ncrcrii. Dac
un Player sau un Processor poate raporta progresul ncrcrii, el implementeaz
aceast interfa pentru ca bara de progres (progress bar) s poat fi afiat
utilizatorului.
Interfaa GainControl permite ajustri ale volumului audio, cum ar fi
setarea nivelului sau reducerea lui la zero (mute) la ieirea unui Player sau
Processor.

Figura 8.7. Interfaa GainControl


15
Figura 8.8. Controalele din JMF
Obiectele DataSink i Multiplexer care citesc date de la o DataSource i
le scriu ntr-un fiier pot implementa interfaa StreamWriterControl. Acest
Control permite utilizatorului s limiteze mrimea stream-ului care este creat.
Obiectele FramePositioningControl i FrameGrabbingControl export
capabilitile de lucru la nivel de cadru pentru Playere i Procesoare.
FramePositioningControl permite poziionarea exact la nivel de cadru
ntr-un stream. FrameGrabbingControl permite extragerea unui cadru dintr-un
flux video.
Obiectele pot implementa interfaa FormatControl pentru a permite
accesul la Format. FormatControl permite de asemenea i metode pentru
interogarea i setarea formatului.
Un TrackControl este un tip de FormatControl care permite controlarea
proceselor efectuate de obiectul Processor asupra unei anumite piste de date. Cu
16
aceast metod, se poate specifica ce conversii de format se aplic unei piste
individuale. De asemenea, se pot selecta ce plug-in-uri Effect, Codec sau Render
sunt folosite de Processor.
Dou controale, PortControl i MonitorControl permite controlul
utilizatorului asupra procesului de captur. MonitorControl permite
previzualizarea (preview) datelor n timp ce sunt capturate sau codate.
BufferControl permite utilizatorului s controleze buffering-ul fcut de
un anumit obiect.
JMF definete i cteva controale pentru codec-uri pentru a permite
utilizatorului s controleze codec-urile hardware i software folosite pentru
codare/decodare:

BitRateControl folosit pentru a afla informaii despre rata (n


bii/sec) unui stream sau pentru a controla codarea lui;
FrameProcessingControl folosit pentru specificarea
parametrilor la nivel de cadru , ceea ce permite codec-ului s execute un
set minimal de operaii;
FrameRateControl permite modificarea numrului de
cadre/secund;
H261Control permite controlul asupra codec-ului H.261,
folosit la transmisia de imagini statice;
H263Control permite controlul asupra parametrilor codec-
ului video H.263, inclusiv suport pentru vectori nerestricionai, codare
aritmetic i extensii de compensare a erorilor;
KeyFrameControl permite specificarea intervalului dintre
cadrele de baz;
MpegAudioControl export capabilitile codec-ului audio
MPEG i permite selectarea parametrilor ;
QualityControl permite specificarea preferinelor pentru
calitatea procesrii i solicitarea procesorului. O valoare mai mare
pentru calitatea biilor rezultai duce la mbuntiria calitii;
SilenceSuppressionControl permite modificare parametrilor
pentru codec-urile audio. Cnd este pe on, codorul audio nu trimite spre
ieire nimic dac nu detecteaz nimic la intrare.
8.3.1.6. Componentele interfeei utilizator
Un Control poate permite accesul la o interfa de tipul Component.
Pentru a afla interfaa impicit pentru un anumit Control, trebuie apelat metoda
getControlComponent. Aceast metod returneaz o component AWT care se
poate include ntr-un applet sau n fereastra unei aplicaii.
Un Controller permite de asemenea i acces la interfaa utilizator
Components. Spre exemplu, un Player permite accesul att la componenta

17
vizual i la componenta panoului de control. Pentru a afla aceste componente
trebuie apelate metodele getVisualComponent i getControlPanelComponent.
Dac nu se dorete folosirea componentelor de control implicite, este
posibil dezvoltarea unor componente utilizator.
8.3.1.7. Extensibilitatea
Programatorii avansai i furnizorii de tehnologii pot extinde
funcionalitatea JMF n dou moduri:

prin implementarea unor plug-in-uri utilizator care pot fi


folosite n paralel cu cele standard;
prin implementarea direct a interfeelor Controller, Player,
Processor, DataSource i DataSink.

Prin implementarea unui plug-in devine posibil extinderea
capabilitilor unui Processor fr a mai fi nevoie de implementarea
proprietilor pe care acesta le are deja. Odat ce un plug-in este nregistrat
(registered) cu JMF, el poate fi selectat ca o opiune de procesare pentru orice
Processor care suport API-ul plug-in-ului respectiv.
Plug-in-urile JMF pot fi folosite la:

extinderea sau nlocuirea capabilitii de procesare a unui


Processor, prin selectarea unui plug-in individual care s fie folosit;
accesul la date ntr-un anumit punct al fluxului. Spre exemplu,
diferite plug-in-uri de efect pot fi folosite nainte i dup procesarea
datelor;
procesarea datelor n afara Player-ului sau Processor-ului.
Spre exemplu, se poate folosi un plug-in Demultiplexer pentru a obine
piste audio individuale dintr-un stream media multiplexat, cu scopul de
a le rula cu Java Sound.

n anumite situaii este nevoie de un grad mai mare de flexibilitate i
control. n aceste cazuri, este necesar modificarea interfeelor Controller,
Player, Processor, DataSource sau DataSink. Spre exemplu, dac avem un
decoder hardware MPEG, este posibil implementarea unui Player care primete
date de la DataSource i folosete decoderul pentru a realiza operaiile de
analiz, decodare i redare, toate ntr-un singur pas. De asemenea, pot fi
implementate Playere utilizator avansate cum ar fi Media Player-ul de la
Microsoft, Real Player-ul de la Real Network sau Hot Media de la IBM.
8.3.2. Prezentarea
n JMF, procesul de prezentare este modelat de interfaa Controller.
Aceasta definete strile de baz i mecanismul de control pentru un obiect care
18
controleaz, prezint sau captureaz date de tip media dependente de timp. El
definete fazele prin care trece un controler i pune la dispoziia utilizatorului un
mecanism ce permite controlarea tranziiilor ntre aceste faze.
O serie de operaii care trebuie efectuate nainte de a ncepe prezentarea
sunt consumatoare de timp. Din acest motiv, JMF permite controlul programatic
al acestora.
Un Controller genereaz o serie de MediaEvent-uri pentru a face posibil
accesul la informaii referitoare la starea n care se afl. Pentru a primi
evenimente de la un Controller cum este un Player, trebuie implementat
interfaa ControllerListener.
API-ul JMF definete dou tipuri de controlere: Player-ele i
Procesoarele. Acestea sunt construite pentru o surs particular de date i n
general nu sunt refolosite pentru a prezenta alte date.
Figura 8.9 prezint Controller-ele din JMF.

Figura 8.9. Controller-ele n JMF

8.3.2.1. Player-ele
Un Player proceseaz un stream de date i le red la un anumit timp. O
surs de date este folosit pentru a asigura acest stream Player-ului. Destinaia
unde se realizeaz redarea depinde de tipul datelor ce sunt prezentate.

19
Figura 8.10. Modelul Player n JMF
Un Player nu permite nici un fel de control al procesrii pe care o
execut i nici asupra modului n care se face redarea.
Player-ul suport controale utilizator standard i ignor cteva restricii
operaionale impuse de Clock i Controller.

Figura 8.11. Player-ele JMF

8.3.2.1.1. Strile unui Player


Un Player poate fi n una din ase stri. Interfaa Clock definete cele
dou stri primare: Stopped (oprit) i Started (pornit). Pentru a facilita
managementul resurselor, interfaa Controller divizeaz starea Stopped n alte 5
stri: Unrealized (Nerealizat), Realizing (nelegerea), Realized (neles),
Prefetching (Pregtirea) i Prefetched (Pregtit).

20
Figura 8.12. Strile unui Player

21
ntr-un mod de operare normal, un Player trece prin toate strile pn
cnd ajunge n starea Started:
Un Player n starea Unrealized a fost iniializat, dar nc nu
tie nimic despre datele pe care urmeaz s le prelucreze. Cnd un
Player este creat pentru prima dat, este n starea Unrealized;
Cnd procedura Realize este apelat, un Player trece din
starea Unrealized n starea Realizing. Un Player n starea Realizing este
n procesul de determinare a cerinelor de resurse. Aceast determinare
este efectuat o singur dat. Resursele ar putea fi resursele de redare,
altele dect resursele folosite exclusiv (exclusive-use resources);
Cnd un Player trece de starea Realizing, ajunge n starea
Realized. Un Player Realized tie de ce resurse are nevoie i are
informaii despre tipul datelor ce urmeaz a fi prezentate. Din cauz c
un Player Realized tie cum s redea aceste date, el poate pune la
dispoziie utilizatorului componente vizuale i controale. Conexiunea
lui cu alte obiecte din sistem este existent, dar nu i nsusete alte
resurse care ar putea mpiedica alt Player s porneasc;
Cnd procedura Prefetch este apelat, un Player trece de la
starea Realized la starea Prefetching. n aceast stare Player-ul se
pregtete s prezinte datele media. La nceput, Player-ul ncarc datele
ce trebuie prezentate, obine acces exclusiv la resursele necesare, dup
care face tot ceea ce este necesar pentru a se pregti de rulare.
Procedura de Prefetch se apeleaz i n cazul n care are loc o
repoziionare (spre exemplu o derulare napoi/nainte) n prezentare sau
dac o schimbare n rata de transfer necesit folosirea unor buffere
suplimentare sau o procesare alternativ;
Cnd un Player termin toate operaiile din starea de
Prefetching, trece n faza urmtoare, i anume Prefetched. n aceast
stare, Player-ul este gata de a fi pornit;
Apelarea procedurii Start pune un Player n starea Started. n
aceast moment, se face o mapare a timpului de start i Clock-ul asociat
este pornit, dei Player-ul s-ar putea s atepte un anumit timp nainte
de a ncepe prezentarea.

Un Player genereaz evenimente de tipul TransitionEvents dup cum


trece dintr-o stare n alta. Interfaa CotrollerListener permite programului s
determine n care din cele ase stri se afl. Spre exemplu, cnd un program
apeleaz o metod asincron unui Player sau Processor, trebuie s asculte
evenimentul potrivit pentru a determina cnd operaia este terminat.
Folosind acest mecanism de raportare a evenimentelor, se poate
administra latena la start a unui Player prin controlarea momentului cnd ncep
Realizing i Prefetching. De asemenea se poate determina dac Player-ul este
ntr-o anumit stare nainte de apelarea metodelor asupra unui Player.
22
8.3.2.1.2. Metode disponibile n fiecare din strile Player -ului
Pentru a preveni conflictele, nu toate metodele pot fi apelate asupra
unui Player n toate strile. Urmtorul tabel identific restriciile impuse de JMF.
Dac se apeleaz o metod care este ilegal n starea curent a Player-ului,
acesta arunc (throws) o eroare sau o excepie.

Unrealized Realized Prefetched Started


Metoda aplicat
Player Player Player Player
NotRealized
addController Legal Legal CSE*
Error
Deallocate Legal Legal Legal CSE*
getControlPanel NotRealized
Legal Legal Legal
Component Error
NotRealized
getGainControl Legal Legal Legal
Error
NotRealized
getStartLatency Legal Legal Legal
Error
NotRealized
getTimeBase Legal Legal Legal
Error
NotRealized
getVisualComponent Legal Legal Legal
Error
mapToTimeBase CSE* CSE* CSE* Legal
NotRealized
removeController Legal Legal CSE*
Error
NotRealized
setMediaTime Legal Legal Legal
Error
NotRealized
setRate Legal Legal Legal
Error
NotRealized StopTime
setStopTime Legal Legal
Error SetError
NotRealized
setTimeBase Legal Legal CSE*
Error
NotPrefetch NotPrefetch
syncStart Legal CSE*
Error Error
*CSE ClockStartedError
Tabel 8.4. Restriciile metodelor aplicate asupra Playerelor

8.3.2.2. Processors (Procesoarele)


Processoarele pot fi utilizate i pentru a prezenta datele media. Un
Processor este de fapt un tip de Player specializat care permite controlul asupra
proceselor ce au loc asupra stream-ului de date. Un Processor suport toate
controalele de prezentare suportate de un Player. Modelul Processor este
prezentat n Figura 8.13.

23
Figura 8.13. Modelul Processor n JMF
n afar de trimiterea datelor spre dispozitivele de redare, un Processor
poate trimite datele spre o DataSource pentru a putea fi prezentate de alt Player
sau Processor sau salvate ntr-un fiier.
8.3.2.3. Controlul prezentrii
n afara controalelor standard definite de Controller, un Player sau
Processor permite i ajustarea volumului.
Tipuri adiionale de interfee Control utilizator pot fi adugate printr-o
implementare particular a unui Player sau Processor care s modifice
comportamentul controalelor i s expun componentele interfeei utilizator.
Aceste controale pot fi accesate prin metoda getControls.
Spre exemplu, interfaa CachingControl extinde interfaa Control pentru
a realiza un mecanism de afiare a unei bare de progres a unui download
(download progress bar). Dac un Player poate raporta progresul lui de
download, el implementeaz aceast interfa. Pentru a afla dac un Player
suport CachingControl, trebuie apelat metoda getControl(CachingControl) sau
trebuie folosit getControls pentru a obine o list a controalelor suportate.
Un Player sau un Processor ofer n general dou componente pentru
interfaa utilizator, una vizual i una pentru panoul de control. Acestea pot fi
accesate direct prin metodele getVisualComponent pentru componenta vizual i
getControlPanelComponent pentru panoul de control.
De asemenea, este posibil implementarea unor interfee utilizator. n
acest caz trebuie folosit un mecanism de ascultare a evenimentelor pentru a
determina cnd trebuie modificate componentele acestor interfee.

8.3.2.4. Evenimente de tip Controller


Evenimentul ControllerEvent generat de un Controller cum ar fi un
Player sau un Processor face parte din una din urmtoarele trei categorii:

Notificrile modificrilor cum sunt RateChangeEvent,


DurationUpdateEvent i FormatChangeEvent indic faptul c unele
atribute ale Controller-ului s-au modificat, de obicei n urma apelrii

24
unei metode. Spre exemplu, un Player genereaz un eveniment
RateChangeEvent cnd rata sa se modific prin apelarea setRate.
Evenimentele de tranziie (TransitionEvents) permit
programului s rspund modificrilor strii unui Controller. Un Player
genereaz evenimente de tranziie ori de cte ori trece de la o stare la
alta.
Evenimentele de ncheiere, cum este ControllerClosedEvents
sunt generate de un Controller cnd acesta se oprete. Cnd un
Controller genereaz un eveniment ControllerClosedEvent, el nu mai
poate fi folosit. Un eveniment ControllerErrorEvent este un caz special
de ControllerClosedEvent. Este posibil ascultarea unui astfel de
eveniment pentru a rspunde defeciunilor unui Controller i a minimiza
impactul acestora asupra utilizatorului.

25
Figura 8.14. Evenimentele n JMF

8.3.3. Procesarea
Un Processor este un Player care primete o DataSource la intrare,
execut procese definite de utilizator asupra datelor media, dup care trimite mai
departe datele media procesate. Figura 8.15 prezint procesarea n JMF.

26
Figura 8.15. Procesarea n JMF
Un Processor poate trimite datele de ieire spre un dispozitiv de
prezentare sau spre o DataSource. n cel de al doilea caz, DataSource poate fi
folosit ca intrare pentru un alt Player sau Processor, sau ca intrare ntr-un
DataSink.
n timp ce procesarea executat de un Player este predefinit de
echipament, un Processor permite programatorului s defineasc tipul de
procesare pe care l vrea aplicat datelor. Aceast procesare poate fi aplicarea de
efecte, mixarea i compunerea de piste n timp real.

Figura 8.16. Stagiile prin care trece un Processor


Procesarea datelor are loc n cteva etape:

Demultiplexarea este procesul de analiz a stream-ului de


intrare. Dac stream-ul conine mai multe piste, ele sunt extrase i
trimise mai departe separat. Spre exemplu, un film QuickTime trebuie
demultiplexat n piste audio i video separate. Demultiplexarea se
realizeaz automat ori de cte ori stream-ul de intrare conine mai multe
piste.
27
Preprocesarea este procesul de aplicare a algoritmilor de efect
pistelor extrase din stream-ul de intrare.
Codarea/decodarea este procesul de conversie al fiecrei piste
de date dintr-un format de intrare n altul. Cnd un stream de date este
convertit dintr-un tip de date comprimate ntr-un tip de date
necomprimate nseamn c se realizeaz o operaie de decodare. Invers,
conversia dintr-un tip de date necomprimate ntr-un tip de date
comprimate se numete operaie de codare.
Postprocesarea este procesul de aplicare a algoritmilor de
efect asupra pistelor decodate.
Multiplexarea este procesul de ntreesere a pistelor de date
ntr-un singur flux de ieire. Spre exemplu, piste separate video i audio
pot fi multiplexate ntr-un singur stream MPEG-1. Cu ajutorul
Processor-ului se poate specifica tipul stream-ului de ieire. Pentru
aceasta se folosete metoda setOutputContentDescriptor.
Redarea este procesul de prezentare a datelor utilizatorului.

Procesarea fiecrei etape este realizat de o component de procesare


separat. Aceste componente de procesare sunt plug-in-urile JMF. Dac
Processor-ul suport TrackControl, este posibil selectarea unui plug-in pentru
procesarea unei piste anume. Exist cinci tipuri de plug-in-uri JMF:

Demultiplexor parcurge stream-uri media ca WAV, MPEG


sau QuickTime. Dac streamul este multiplexat, sunt extrase piste
separate.
Effect execut procesri speciale de efect asupra datelor.
Multiplexor combin mai multe piste n una singur.
Renderer proceseaz datele ntr-o pist i le trimite spre
destinaie, n general spre ecran sau boxe.
8.3.3.1. Strile adiionale ale unui Processor
Un Processor are dou stri adiionale fa de un Player, i anume
Configuring i Configured, stri prin care Processor-ul trece nainte de a intra n
starea Realizing. n Figura 8.17 sunt prezentate toate strile prin care trece un
Processor.

28
Figura 8.17. Strile unui Processor

29
Metoda aplicat Starea Unrealized Starea Configuring Starea Configured Starea Realized

30
addController NotRealizedError NotRealizedError NotRealizedError legal
Deallocate legal legal legal Legal
getControlPanelComponent NotRealizedError NotRealizedError NotRealizedError legal
getControls legal legal legal legal
getDataOutput NotRealizedError NotRealizedError NotRealizedError legal
getGainControl NotRealizedError NotRealizedError NotRealizedError legal
getOutputContentDescriptor NotConfiguredError NotConfiguredError legal legal
getStartLatency NotRealizedError NotRealizedError NotRealizedError legal
getSupportedContent-Descr legal legal legal legal
getTimeBase NotRealizedError NotRealizedError NotRealizedError legal
getTrackControls NotRealizedError NotRealizedError legal FormatChangeException
rspunde cu o eroare sau cu o excepie.

getVisualComponent NotRealizedError NotRealizedError NotRealizedError legal


mapToTimeBase ClockStoppedException ClockStoppedException ClockStoppedException ClockStopped-xception
Realize Legal Legal Legal Legal
removeController NotRealizedError NotRealizedError NotRealizedError legal
setOutputContentDescriptor NotConfiguredError NotConfiguredError legal FormatChangeException
setMediaTime NotRealizedError NotRealizedError NotRealizedError legal
setRate NotRealizedError NotRealizedError NotRealizedError Legal
8.3.3.2. Metode disponibile n fiecare stare a Processorului

setStopTime NotRealizedError NotRealizedError NotRealizedError legal


setTimeBase NotRealizedError NotRealizedError NotRealizedError legal
syncStart NotPrefetchedError NotPrefetchedError NotPrefetchedError NotPrefetchedError

Tabel 8.5. Restriciile metodelor pentru Processor


metode specifice doar Processoar-elor sunt restricionate doar la anumite stri.
Din moment ce un Processor este un tip de Player, restriciile care se
aplic la apelarea unui Player se aplic i la apelarea unui Processor. Unele

Dac se apeleaz o metod care este ilegal n starea curent, Processor-ul


8.3.3.3. Controlul procesrii
Exist posibilitatea de a controla operaiile pe care un Processor le
execut pentru o pist, i anume folosind TrackControl pentru acea pist.
Pentru a controla operaiile efectuate de Processor asupra tuturor pistelor din
stream-ul de date, trebuie apelat metoda getTrackControls.
Folosind TrackControl, se poate selecta explicit tipul de plug-in care
se dorete a fi folosit pentru acea pist. Pentru a afla care sunt opiunile
disponibile, se poate chestiona PlugInManager-ul pentru a afla ce plug-in-uri
sunt instalate.
Pentru a controla operaiile de codare/decodare executate asupra unei
piste de ctre un anumit Codec, trebuie apelat metoda TrackControl
getControls. Aceast metod returneaz controalele codec-urilor disponibile
pentru pista resprectiv, cum ar fi BitRateControl i QualityControl.
Dac se tie ce format a datelor de ieire se dorete, se poate folosi
metoda setFormat pentru a specifica formatul i a lsa Processor-ul s aleag
codec-ul potrivit. Alfel, se poate specifica formatul de ieire dorit cnd un
Processor este creat folosind ProcessorModel. Un ProcessorModel definete
necesitile de intrare i ieire pentru un Processor. Cnd un ProcessorModel
este transmis metodei Manager create potrivite, Manager-ul face tot posibilul
ca s creeze un Processor care s ndeplineasc condiiile cerute.
8.3.3.4. Trimiterea datelor spre destinaie
Metoda getDataOutput returneaz obiectul de la ieirea unui
Processor ca o DataSource. Aceast DataSource poate fi folosit ca intrare
pentru un alt Player sau Processor sau ca intrare la o DataSink.
n Java Media Framework exist mai multe tipuri de obiecte
DataSource, cum ar fi: PushDataSource, PushBufferDataSource,
PullDataSource i PullBufferDataSource.
Nu toate Processoarele trimit date spre destinaie. Un Processor poate
trimite datele napoi n loc de a le trimite spre o DataSource.

8.3.4. Captura
Un dispozitiv de captur multimedia poate aciona ca o surs de
distribuie de date multimedia. Spre exemplu, un microfon poate captura
semnal audio brut sau o plac de captur video poate captura semnal video
digital de la o camer video. Aceste dispozitive de captur poart numele de
DataSources. Exist mai multe tipuri de DataSource care pot fi folosire pentru

31
realizarea capturii: PushDataSource, PushBufferDataSource, PullDataSource i
PullBufferDataSource.
Unele dispozitive captureaz stream-uri de date multiple. Spre
exemplu, o plac pentru audio/video conferin captureaz un stream audio i
unul video. DataSource-ul corespondent poate conine multiple SourceStream-
uri care se mapeaz peste stream-urile de date provenite de la dispozitiv.
8.3.5. Stocarea datelor i transmisia
Un DataSink este folosit pentru a citi datele de la o DataSource i de a
le trimite spre o anumit destinaie (n general spre o alt destinaie dect
dispozitivul de prezentare). Un anumit DataSink poate scrie date ntr-un fiier,
poate scrie date prin reea sau poate funciona ca i un Broadcaster RTP.
Ca i Player-ele, obiectele de tip DataSink sunt construite de Manager
folosind DataSource. Un DataSink poate folosi un StreamWriterControl pentru
a permite controlul asupra modului n care datele sunt scrise ntr-un fiier.
Un DataSink genereaz un eveniment DataSinkEvent pentru a raporta
starea sa. Un eveniment DataSinkEvent poate fi generat cu un cod care explic
motivul sau poate fi generat ca unul dintre urmtoarele subtipuri:

DataSinkErrorEvent, care indic faptul c o eroare a ntrerupt


procedura de scriere a datelor
EndOfStreamEvent, care indic faptul c ntregul stream a
fost scris
Pentru a rspunde evenimentelor generate de un DataSink, trebuie
implementat interfaa DataSinkListener.
8.3.6. Extensibilitatea
Este posibil extinderea JMF prin implementarea unor plug-in-uri,
dispozitive de tratare a datelor i surse de date utilizator.
Prin implementarea interfeelor unui plug-in JMF sunt posibile accesul
direct i manipularea direct la datele asociate unui Processor:

Implementarea interfeei Demultiplexer permite controlul


procesului de extragere al pistelor individuale din streamul de date
multiplexat;
Implementarea interfeei Codec permite efectuarea operaiilor
de procesare necesare decodrii datelor comprimate, de conversie
dintr-un anumit format n altul, de codare a datelor brute ntr-un
format comprimat;
Implementarea interfeei Effect permite efectuarea unor
operaii utilizator asupra datelor;

32
Implementarea interfeei Multiplexer permite specificarea
modului n care pistele individuale sunt combinate pentru a forma un
singur stream de ieire pentru un Processor;
Implementarea interfeei Render permite controlul asupra
modului n care datele sunt procesate i trimise spre dispozitivele de
ieire.
NOT: API-ul JMF pentru plug-in-uri este parte component a API-
ului JMF, dar Player-ele i Processoarele nu trebuie neaprat s suporte plug-
in-uri. Aceste plug-in-uri nu funcioneaz cu Player-ele i Processoarele JMF
1.0. Versiunea 2.0 a JMF dezvoltat de SUN Microsystems, Inc. i IBM
Corporation suport integral acest API .

8.4. Prezentarea datelor dependente de timp n Java


Media Framework
Pentru a prezenta date multimedia n raport cu timpul (cum ar fi audio
sau video) folosind JMF, trebuie folosit un Player. Rularea poate fi controlat
automat sau poate afia o component tip panou de control care s permit
utilizatorului s o controleze interactiv. Dac se dorete rularea mai multor
streamuri media, trebuie folosit cte un Player separat pentru fiecare dintre ele.
Pentru a le rula sincronizat, se poate folosi unul dintre Playere pentru a controla
operaiile efectuate de celelalte Playere.
Processor-ul permite controlul asupra proceselor ce au loc nainte de
realizarea prezentrii. Indiferent dac se folosete un Player simplu sau unul
mai avansat pentru prezentare, se folosete aceeai metod pentru a administra
rularea.
Bean-ul MediaPlayer este un bean Java care ncapsuleaz un Player
JMF ce permite prezentarea datelor multimedia dintr-un applet sau o aplicaie.
El construiete automat un nou Player cnd un stream diferit de date este
selectat, ceea ce uureaz operaia de rulare a unei serii de clip-uri media sau
permite utilizatorului s selecteze care clip anume s fie rulat.
8.4.1. Controlul unui Player
Pentru a prezenta datele, sunt necesare construirea unui Player pentru
stream-ul de date, configurarea i pregtirea Player-ului, i n final pornirea
acestuia pentru nceperea rulrii.
8.4.1.1. Crearea unui Player
Un Player se creeaz n mod indirect din Manager. Pentru a afia
Player-ul, trebuie create i afiate componentele necesare controlrii.
Cnd este necesar creearea unui nou Player, este solicitat acest lucru
de la Manager prin apelarea metodei createPlayer sau createProcessor.

33
Manager-ul folosete URL-ul sau MediaLocator-ul care trebuie
specificat pentru creearea Player-ului. Un URL poate fi construit cu succes
doar dac URLStreamHandler-ul corespondent este instalat. Asupra
MediaLocator nu se aplic aceast restricie.
Multe dintre metodele care pot fi apelate asupra unui Player necesit
ca acesta s fie n starea Realized. O metod care garanteaz c Player-ul este
n starea Realized cnd se apleleaz aceste metode este folosirea metodei
createRealizedPlayer la construirea Player-ului. Aceast metod este o cale
convenabil de creeare i trecere a unui Player n starea Realized ntr-un singur
pas. Cnd aceast metod este apelat, se blocheaz pn cnd Player-ul este
Realized. Manager-ul are o metod echivalent, i anume
createRealizeProcessor pentru construirea unui Processor n starea Realized.
NOT: Trebuie inut seama c blocarea unui Player sau Processor
pn ajunge n starea Realized poate produce rezultate nesatisfctoare. Spre
exemplu, dac metoda createRealizedPlayer este apelat ntr-un applet,
Applet.start i Applet.stop nu vor putea ntrerupe procesul de construcie al
Player-ului sau Processor-ului.
Un Processor poate fi creat i folosind ProcessorModel. Acesta
definete necesarul de resurse pentru intrare i ieire iar Manager-ul face tot
posibilul pentru a ndeplini cererea de resurse. Pentru a construi un Processor
folosind modelul ProcessorModel, trebuie apelat metoda
Manager.createRealizedProcessor.
Exemplul urmtor creaz un Processor n starea Realized care poate
produce piste audio stereo codate IMA4 cu o rat de 44.1 kHz, pe 16 bii.

Exemplul 8.1.Construirea unui Processor folosind ProcessorModel


AudioFormat afs[] = new AudioFormat[1];
afs[0] = new AudioFormat("ima4", 44100, 16, 2);
Manager.createRealizedProcessor(new ProcessorModel(afs, null));

Din moment ce ProcessorModel nu specific un URL surs n acest


exemplu, Manager-ul caut un dispozitiv de captur audio i creeaz un
Processor care poate coda ceea ce se captureaz cu parametrii dorii.
8.4.1.2. Afiarea componentelor interfeei de prezentare
Un Player are de obicei dou tipuri de componente pentru interfaa
utilizator, o component vizual i o component pentru panoul de control.
Unele implementri ale Player-elor pot afia componente adiionale, cum ar fi
controlul volumului sau bara pentru progresul operaiunii de download.
8.4.1.2.1 Afiarea unei componente vizuale

34
O component vizual apare n cazul n care un Player prezint vizual
datele multimedia, dac are aceste date. Chiar i un Player audio poate avea
componente vizuale, cum ar fi afiarea formelor de und sau afiarea unor
caractere animate.
Pentru a afia o component vizual a unui Player trebuie urmai
urmtorii doi pai:

trebuie ncrcat componenta prin apelarea


getVisualComponent;
trebuie adugat n spaiul de prezentare al applet-ului sau n
fereastra aplicaiei.

Este posibil accesul la proprietile de afiare ale Player-ului (cum ar


fi coordonatele x i y) prin componentele sale vizuale.
8.4.1.2.2 Afiarea unei componente de control
Un Player are de obicei un panou de control care permite utilizatorului
s controleze prezentarea. Spre exemplu, un Player poate fi asociat cu un set de
butoane care s i permit s porneasc, opreasc, ntrerup un stream de date.
Fiecare Player conine implicit un panou de control. Pentru a afia
acest panou de control trebuie urmai urmtorii doi pai:

apelat metoda getControlPanelComponent pentru a accesa


componentele;
adugate componentele returnate de metoda anterioar
spaiului de prezentare al applet-ului sau ferestrei de aplicaie.
Dac se prefer definirea unei interfee utilizator, se pot implementa
componente GUI (Graphic User Interface) i apela metodele Player
corespondente ca rspuns la aciunile utilizatorului. Dac se nregistreaz aceste
componente ca i ControlListeners, componentele se pot adapta cnd starea
Player-ului se modific.
8.4.1.2.3. Afiarea unei componente de tip GainControl
Implementrile Player-ului care suport ajustri ale ctigului audio
folosesc interfaa GainControl. Aceasta conine metode de ajustare a volumului
audio, cum ar fi setLevel i setMute. Pentru a afia o component GainControl
dac Player-ul permite, trebuie urmai urmtorii pai:

trebuie apelat metoda getGainControl pentru a obine


GainControl de la Player. Dac acesta returneaz un rezultat nul,
nseamn c nu suport interfaa GainControl;

35
trebuie apelat getControlComponent asupra componentei
GainControl returnate de Player;
se adaug componenta rezultat n spaiul de prezentare al
aplicaiei sau n fereastra aplicaiei.
8.4.1.2.4. Afiarea unor componente de control utilizator
Multe Playere au alte proprieti care pot fi administrate de ctre
utilizator. Spre exemplu, un Player video ar putea permite utilizatorului s
ajusteze luminozitatea i contrastul, care nu sunt administrare de interfaa
Player. Pentru a afla ce controale utilizator suport un Player trebuie apelat
metoda getControls.
Spre exemplu, se poate apela getControls pentru a determina dac un
Player suport interfaa CachingControl.

Exemplul 8.2. Folosirea metodei getControls pentru a afla ce


controale sunt suportate
Control[] controls = player.getControls();
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof CachingControl) {
cachingControl = (CachingControl)
controls[i];
}
}

8.4.1.2.5. Afiarea unei componente de progres

Interfaa CachingControl este un tip mai special de Control


implementat de Playere care pot raporta progresul download-ului. Ea permite
afiarea unei bare de progres care este automat modificat pe msur ce
download-ul progreseaz. Pentru a introduce aceast bar de progres ntr-un
applet trebuie urmai urmtorii pai:

trebuie implementat interfaa ControllerListener i ascultat


pentru evenimente CachingControlEvents ntr-un controllerUpdate;
prima dat cnd se primete un CachingControlEvent:
se apeleaz getCachingControl asupra evenimentului
se apeleaz getProgressBar asupra CachingControl pentru a
accesa componenta bar de control
se adaug aceast component la spaiul de prezentare a
applet-ului

36
De fiecare dat cnd se primete un CachingControlEvent,
trebuie verificat dac download-ul este terminat. Cnd
getContentProgress returneaz aceeai valoare ca i getContentLength,
bara de progres trebuie nlturat.

Player-ul genereaz un eveniment CachingControlEvent ori de cte


ori bara de progres trebuie s fie modificat. Dac se implementeaz o bar de
progress utilizator, trebuie ascultat pentru acest eveniment.
8.4.1.3. Setarea ratei de rulare
Rata de rulare a unui Player determin cum se modific timpul media
relativ la timpul de baz. Ea definete cte uniti de timp avanseaz un Player
relativ la o unitate de timp de baz. Rata de rulare poate fi interpretat ca un
factor scal temporal. Spre exemplu, o rat de 2.0 indic c timpul media trece
de dou ori mai rapid dect timpul de baz cnd Player-ul este pornit.
n teorie, rata de rulare a unui Player poate fi orice numr real,
valorile negative fiind interpretate ca faptul c rularea se face invers. Une tipuri
de formate media au relaii ntre cadre care fac imposibil sau nepractic
rularea lor napoi sau rularea la rate non-standard.
Pentru a seta rata de rulare, trebuie apelat metoda setRate i setat un
parametru care este de tip float. Cnd setRate este apelat, metoda returneaz
rata care este setat. Player-ele pot garanta doar o rat de rulare de 1.0.
8.4.1.4. Setarea poziiei de start
Setarea timpului unui Player este echivalent cu setarea unei poziii de
citire ntr-un stream multimedia. Pentru o surs de date cum este un fiier,
timpul este delimitat: timpul maxim este definit ca sfritul stream-ului media.
Pentru a seta timpul trebuie apelat metoda setMediaTime iar
rezultatul trebuie pasat ntr-un obiect de tip Time. Valoarea acestui obiect
reprezint timpul care pe vrem s l setm.
Unele Playere permit cutarea unui anumit cadru ntr-o secven
video. Aceasta permite utilizatorului s seteze poziia de start la nceputul unui
cadru anume fr s fie necesar specificarea timpului care corespunde acelei
poziii. Player-ele care suport poziionarea la nivel de cadru implementeaz
FramePositioningControl.
Pentru a seta poziionarea la nivelul unui cadru, trebuie apelat metoda
de cutare FramePositioningControl. Cnd se caut un cadru, timpul
corespondent Player-ului este setat la o valoare care corespunde cu nceputul
acelui cadru. n acelai timp, un eveniment MediaTimeSetEvent este generat.
Unele Playere pot face conversia ntre timp i poziia cadrelor. Pentru
aceasta trebuie folosite metodele FramePositioningControl mapToTime i
mapTimetoFrame pentru a accesa aceste informaii, dac sunt dispoibile.

37
8.4.1.5. Pregtirea startului
Majoritatea Player-elor nu pot fi pornite instant. nainte de a putea
realiza pornirea unui Player, trebuie ndeplinite anumite condiii hardware i
software. Spre exemplu, dac un Player nu a mai fost pornit niciodat, este
necesar alocarea unor buffere n memorie pentru stocarea datelor. Sau, dac
datele se afl undeva n reea, Player-ul trebuie s stabileasc conexiunea n
reea nainte de a aduce datele. Chiar i dac Player-ul a mai fost pornit, buffer-
ele ar putea conine date care nu corespund cu datele corespondente poziiei
curente.
JMF mparte procesul de pregtire de pornire a unui Player n dou
faze, Realizing i Prefetching. Dac aceste faze se execut nainte de pornirea
Player-ului, se minimizeaz timpul necesar Player-ului pentru nceperea
prezentrii cnd metoda start este apelat. Implementarea interfeei
ControlListener permite controlul exact al timpului de execuie al acestor
operaii.
Cea de a treia faz de pregtire a startului este numit Configuring. n
timpul acestei faze, pot fi selectate anumite opiuni pentru a controla felul n
care Processor-ul manipuleaz datele.
Pentru a trece Player-ul n starea Realizing i pentru a ncepe procesul
de realizare se apeleaz metoda realize. Pentru a trece Player-ul n starea
Prefetching i pentru a iniia procesul de prefetching, se apeleaz metoda
prefetch. Aceste metode sunt asincrone i rezultatul este imediat. Cnd Player-
ul a completat o operaie cerut, el genereaz un evenimet
RealizeCompleteEvent sau PrefetchCompleteEvent.
Un Player n starea Prefetched este pregtit de start i latena sa de
pornire nu poate fi redus mai mult.
Foarte important este faptul c un Player n starea Prefetched ine
blocate unele dintre resursele sistemului. Deoarece unele dispozitive, cum sunt
plcile de sunet, pot fi utilizate de un singur program ntr-un moment dat, un
Player aflat n starea Prefetched poate mpiedica alte Playere s porneasc.
Pentru a determina ct de mult timp este necesar pentru a porni un
Player, trebuie apelat metoda getStartLatency. Pentru Player-ele care au o
laten de start variabil, apelarea metodei getStartLatency returneaz latena de
start maxim posibil. Pentru unele tipuri de date, getStartLatency poate returna
LATENCY_UNKNOWN.
Latena de pornire raportat prin apelarea metodei getStartLatency
poate diferi n funcie de starea curent a Player-ului. Spre exemplu, dup o
operaie de prefetch, valoarea returnat este de obicei mai mic.
8.4.1.6. Pornirea i oprirea prezentrii
Interfeele Clock i Player definesc metodele necesare pentru pornirea
i oprirea prezentrii.

38
Pentru pornirea prezentrii se apeleaz metoda start. Aceasta spune
Player-ului s pornesc prezentarea ct mai repede posibil. Dac este necesar,
pregtete Player-ul prin efectuarea operaiilor de realize i prefetch. Dac
metoda start este apelat asupra unui Player deja pornit, singurul rezultat este
faptul c este generat un eveniment StartEvent.
Pentru realizarea sincronizrii interfaa Clock definete metoda
syncStart.
Pentru a porni un Player dintr-un punct specific al stream-ului de date
trebuie urmai urmtorii doi pai:

trebuie specificat punctul exact din stream-ul de date de unde


se dorete pornirea. Pentru aceasta se apeleaz metoda setMediaTime;
se apeleaz metoda start asupra Player-ului.

Exist patru situaii n care prezentarea se va opri:

cnd este apelat metoda stop;


cnd este atins timpul de stop specificat;
cnd nu mai sunt date de prezentat;
cnd datele sunt recepionate prea ncet pentru o rulare
acceptabil.

Cnd un Player este oprit, timpul su corespondent este oprit dac


sursa datelor poate fi controlat. Dac el prezint date care sosesc ntr-un flux
continuu (spre exemplu de la o camer video), este posibil s nu se poat opri
timpul corespondent.
Cnd un Player care a fost oprit este repornit, dac timpul
corespondent a fost ngheat, prezentarea continu din locul unde a fost
oprit. Dac timpul corespondent nu a putut fi ngheat, se primesc datele n
continuare i rularea continu cu datele proasptt recepionate.
Pentru a opri imediat un Player, trebuie apelat metoda stop. Dac se
apeleaz metoda stop asupra unui Player, singurul rezultat este c un eveniment
StopByRequestEvent este generat.
Pentru a opri o prezentare la un moment dat, trebuie apelat metoda
setStopTime. Player-ul se oprete n momentul n care timpul lui corespondent
ajunge la timpul de stop specificat. Dac rata Player-ului este pozitiv, acesta
se oprete cnd timpul corespondent devine mai mare sau egal dect timpul de
oprire. Dac rata este negativ, Player-ul se oprete cnd timpul corespondent
devine mai mic sau egal dect timpul de oprire. Player-ul se oprete imediat
dac timpul su curent a depit deja timpul de stop specificat.

39
ntotdeauna se poate apela metoda setStopTime asupra unui Player
oprit. Oricum, timpul de stop se poate seta doar asupra unui Player pornit. Dac
exist deja un timp de stop, metoda setStopTime arunc o eroare.
Pentru a afla timpul de stop setat, trebuie apelat metoda getStopTime.
Dac timpul de stop nu este programat, getStopTime returneaz Clock.RESET.
Pentru a retrage timpul de stop i a permite Player-ului s continue pn ce
ajunge la sfritul prezentrii, se folosete setStopTime(Clock.RESET).
8.4.1.7. Eliberarea resurselor ocupate de Player
Metoda deallocate informeaz Player-ul c trebuie s elibereze toate
resursele pe care le folosete n exclusivitate i s minimizeze accesul la
resursele neexclusive. Managementul memoriei i dimensiunea bufferelor
alocate unui Player nu sunt specificate, motiv pentru care majoritatea Player-
elor aloc buffere care sunt mari raportate la bufferele standard din JAVA. Un
Player bine implementat elibereaz maximum de memorie posibil cnd metoda
deallocate este apelat.
Metoda deallocate poate fi apelat doar asupra unui Player oprit.
Pentru a evita erorile ClockStartedErrors, trebuie apelat metoda stop nainte
de apelarea metodei deallocate. Dac se apeleaz deallocate asupra unui
Player aflat n starea Prefetching sau Prefetched, el revine n starea Realized.
Dac se apeleaz asupra unui Player aflat n starea Realizing, Player-ul
genereaz un eveniment DeallocateEvent i revine n starea Unrealized.
Cnd se dorete terminarea lucrului cu un Player, trebuie apelat
metoda close. Aceasta indic faptul c Controller-ul nu va mai fi folosit i se
poate nchide automat. Prin apelarea close se elibereaz toate resursele care au
fost folosite de Controller i ntrerupe toate activitile. Tot atunci se genereaz
un eveniment ControllerClosedEvent. Un Controller nu poate fi redeschis iar
apelarea unor metode asupra lui poate duce la generarea unor erori.
8.4.1.8. Interogarea unui Player
Un Player poate da informaii despre parametrii lui cureni, incluznd
aici rata de rulare, timpul corespondent i durata.
8.4.1.8.1. Obinerea ratei de rulare
Pentru a obine rata de rulare trebuie apelat metoda getRate.
Rezultatul este rata de rulare ca o valoare de tip float.

8.4.1.8.2. Obinerea timpului corespondent


Pentru a obine timpul corespondent curent, trebuie apelat metoda
getMediaTime. Rezultatul este sub forma unui obiect de tipul Time. Dac
Player-ul nu prezint datele n acel moment, acesta este punctul de unde va
ncepe prezentarea.

40
Trebuie notat faptul c corespondena ntre timpul corespondent unui
Player i cadre nu este unu la unu. Fiecare cadru este prezentat pentru o
anumit perioad de timp, iar timpul corespondent continu s avanseze n
timpul acelei perioade.
S lum exemplul n care un Player prezint fiecare cadru pentru 5
secunde, caz n care rata cadrelor frame rate) este de 0.2 cadre/secund. Figura
8.18 ilustreaz aceast situaie.

Figura 8.18. Durata cadrelor n raport cu timpul corespondent unui Player


Dac Player-ul este pornit la momentul 0.0, n timpul n care primul
cadru este afiat, timpul corespondent avanseaz de la 0.0 la 5.0. Dac se
pornete la momentul 2.0, primul cadru este afiat pentru 3 secunde, pn cnd
momentul 5.0 este atins.
8.4.1.8.3. Obinerea timpului de baz
Obinerea timpului de baz se face prin apelarea metodei getTime
astfel:

myCurrentTBTime = player1.getTimeBase().getTime();

Cnd un Player ruleaz, se poate afla timpul de baz care corespunde


unui anumit timp media prin apelarea mapToTimeBase.
8.4.1.8.4. Obinerea duratei unui stream de date
Din moment ce programele trebuie s cunoasc ct timp va rula un
anumit stream, toate Controller-ele implementeaz interfaa Duration. Aceast
interfa definete o singur metod, i anume getDuration. Durata reprezint
lungimea timpului necesar rulrii, presupunndu-se c este rulat la rata

41
implicit, i anume 1.0 cadre/secund. Durata unui stream este accesibil doar
prin intermediul unui Player.
Dac durata nu poate fi determinat cnd metoda getDuration este
apelat, este returnat DURATION_UNKNOWN. Aceast situaie poate aprea
cnd Player-ul nu a ajuns ntr-o stare care s permit accesul la durata stream-
ului. Mai trziu, durata poate fi disponibil i o apelare a aceleiai metode poate
returna valoarea dorit. Dac sursa de date nu are o durat definit, cum este n
cazul broadcast-ului n direct, metoda getDuration returneaz valoarea
DURATION_UNBOUNDED.
8.4.1.9. Tratarea evenimentelor
Interfaa ControllerListener este o interfa asincron pentru tratarea
evenimentelor generate de un obiect de tip Controller. Folosirea acestei
interfee permite utilizatorului s administreze timpul necesar unor operaii
poteniale consumatoare de timp (spre exemplu, la un Player, operaia de
prefetching).
8.4.1.9.1. Implementarea interfeei ControllerListener
Pentru a implementa o interfa ControllerListener, trebuie:

implementat interfaa ControllerListener ntr-o clas


nregistrat acea clas ca i un listener prin apelarea metodei
addControllerListener asupra Controller-ului de la care vrem s
primim evenimente.

Cnd un Controller genereaz un eveniment, este apelat metoda


controllerUpdate asupra fiecrui listener nregistrat.
De obicei, metoda controllerUpdate este implementat ca o serie de
instruciuni if-else, dup cum se vede i n Exemplul 8.3.

Exemplul 8.3. Implementarea metodei ControllerUpdate


if (event instanceof EventType){
...
} else if (event instanceof OtherEventType){
... }

Prin aceasta se execut o operaie de filtrare a evenimentelor care nu


ne intereseaz. Dac este nregistrat ca un listener cu Controllere multiple,
trebuie determinat i care anume dintre Controllere a generat evenimentul. Din
acest motiv, evenimentele ControllerEvent conin o referin spre sursa care le-
a iniiat. Aceast referin este accesibil prin apelarea getSource.

42
Cnd se primesc evenimente de la un Controller, sunt necesare o serie
de operaii pentru a fi sigur c Controller-ul este n starea corespunztoare
nainte de apelarea unei metode de control. Spre exemplu, nainte de apelarea
oricrei metode care sunt restricionate la Player-ele oprite, trebuie verificat
starea intei Player-ului prin apelarea metodei getTargerState. Dac start a fost
apelat, Player-ul consider c este n starea Started, deci este posibil s
genereze evenimente de tranziie n timp ce se pregtete s prezinte datele.
Unele tipuri de evenimente ControllerEvents conin informaii de stare
adiionale. Spre exemplu, clasele StartEvent i StopEvent definesc fiecare o
metod care permite utilizatorului s afle timpul exact cnd a aprut un
eveniment.
8.4.1.9.2. Folosirea clasei ControllerAdapter
ControllerAdapter este o clas cu unele avantaje care implementeaz
interfaa ControllerListener i care poate fi extins uor pentru a rspunde unor
evenimente particulare. Pentru a implementa interaa ControllerListener
folosind clasa ControllerAdapter, sunt necesare urmtoarele operaii:

trebuie creat o subclas ControllerAdapter i trebuie


suprapuse peste metodele clasice metodele pentru evenimentele pe
care dorim s le implementm.
clasa ControllerAdapter trebuie nregistrat ca un listener
pentru un anumit Controller prin apelarea metodei
addControllerListener

Cnd un Controller genereaz un eveniment, el apeleaz


ControllerUpdate asupra fiecrui listener nregistrat. ControllerAdapter
expediaz automat evenimentul spre metoda eveniment corespondent, filtrnd
n acelai timp evenimentele care nu ne intereseaz.
Spre exemplu, urmtorul cod extinde un ControllerUpdate cu o clas
care conine un Player care este resetat automat la nceputul datelor i este
dealocat cnd Player-ul ajunge la sfritul datelor.

Exemplul 8.4. Folosirea ControllerAdapter


player.addControllerListener(new ControllerAdapter() {
public void endOfMedia(EndOfMediaEvent e) {
Controller controller = e.getSource();
controller.stop();
controller.setMediaTime(new Time(0));
controller.deallocate();
}
}

43
Dac se nregistreaz un singur ControllerAdapter ca listener pentru
mai multe Playere, n implementarea metodelor evenimentelor trebuie
determinat care anume dintre Playere a generat evenimentul. Pentru aceasta se
apeleaz getSource pentru a determina unde a fost un eveniment
ControllerEvent generat.
8.4.1.10. Sincronizarea stream-urilor multiple
Pentru a sincroniza prezentarea mai multor stream-uri media, trebuie
sincronizate Player-ele. Ele trebuie asociate cu acelai TimeBase. Pentru
aceasta, se folosesc metodele getTimeBase i setTimeBase definite de interfaa
Clock. Spre exemplu, dac avem dou Playere, mai exact player1 i player2, le
putem sincroniza ntre ele setnd player1 s foloseasc baza de timp a lui
player2:

Exemplul 8.5. Sincronizarea Player-elor


Player1.setTimeBase(player2.getTimeBase());

Chiar dac realizm sincronizarea player-elor prin asocierea lor la o


baz de timp comun, controlul asupra lor trebuie realizat individual. Deoarece
administrarea unor Playere sincronizate prin aceast metod poate fi
complicat, JMF pune la dispoziie un mecanism care permite unui Player s
dein control asupra oricrui alt Controller. Player-ul administreaz strile
acestor Controllere automat, permind utilizatorului s interacioneze cu
ntregul grup printr-un singur punct de control.
8.4.1.10.1. Folosirea unui Player pentru sincronizarea Controller-elor
Sincronizarea direct a Player-elor folosind syncStart trebuie realizat
cu atenie. Trebuie controlat fiecare stare a fiecrui Player n parte, trebuie
ascultate evenimentele i trebuie apelate metodele potrivite pentru fiecare
eveniment separat. Prin intermediul interfeei Player, JMF pune la dispoziia
programatorilor o soluie mai simpl: un Player poate fi folosit pentru a
administra operaiile efectuate de orice Controller.
Cnd se interacioneaz cu Player-ul de administrare, instruciunile
sunt automat trimise spre Controller-ul potrivit. Player-ul de administrare se
ocup de administrarea strilor n care se afl celelalte Controllere i de
sincronizarea lor.
Acest mecanism este implementat prin metodele addController i
removeController. Cnd se apeleaz metoda addController asupra unui Player,
Controller-ul specificat este adugat la lista de Controllere adminstrate de acel

44
Player. Invers, la apelarea metodei removeController, Controller-ul specificat
este nlturat din lista Controller-elor administrate.
De obicei, cnd se dorete sincronizarea Player-elor sau altor
Controllere, trebuie utilizat acest mecanism addController. Este simplu, este
rapid i mai ferit de erori dect ncercarea de a administra Playere sincronizate
individual.
Cnd un Player ia controlul asupra unui Controller:

Controller-ul i atribuie timpul de baz al Player-ului.


Durata Player-ului devine durata maxim dintre durata
iniial a Player-ului i durata Controller-ului. Dac mai multe
Controllere se afl sub administrarea unui Player, durata Player-ului
devine durata cea mai lung.
Latena de start a Player-ului devine cea mai lung dintre cea
a Controller-ului administrat i latena lui. Dac mai multe Controllere
se afl sub administrarea unui Player, latena Player-ului devine
latena cea mai lung.

Un Player care administreaz genereaz doar evenimente de


ndeplinire (completare) pentru metodele asincrone dup ce fiecare dintre
Controller-ele administrate a postat un eveniment.
Pentru a aduga un Controller listei de Controllere administrate de un
anumit Player se folosete metoda addController. Pentru a putea fi adugat,
Controller-ul trebuie adugat fie n starea Realized, n caz contrar fiind generat
o eroare NotRealizedError. Dou Playere nu se pot administra simultan unul pe
altul. Spre exemplu, dac Player-ul 1 se afl sub administrarea Player-ului 2,
Player-ul 2 nu se poate afla sub administrarea Player-ului 1 fr ieirea n
prealabil a Player-ului 1 de sub administrarea Player-ului 2.
Odat ce un Controller a fost adugat unui Player, nu se pot apela
metode direct spre el. Pentru aceste operaii se interacioneaz cu Player-ul
administrator.
n Exemplul 8.6 se arat cum se ofer Player-ului 2 administrarea
Player-ului 1.

Exemplul 8.6. Preluarea administrrii asupra unui Player


player2.addController(player1);

Pentru a controla operaiile efectuate de ctre un grup de Controllere


administrate de ctre un anumit Player, trebuie interacionat direct cu Player-ul
administrator.

45
Spre exemplu, pentru a pregti toate Controller-ele administrate de
pornire, trebuie apelat metoda prefetch asupra Player-ului administrator.
Acesta se asigur c toate Controller-ele se afl n starea Prefetched, determin
latena de pornire maxim i apeleaz metoda syncStart pentru a le porni,
specificnd latena maxim de start care trebuie respectat.
Cnd se apeleaz o metod asupra Player-ului administrator, acesta o
propag spre Controller-ele aflate sub administrarea lui. nainte de aceasta, el
se asigur c Controller-ul se afl n faza potrivit. Urmtorul tabel descrie ce
se ntmpl unui Controller administrat cnd se apeleaz o metod asupra
Player-ului administrator.

Funcia Player oprit Player pornit


Oprete toate Controller-ele
Apeleaz setMediaTime
administrate, apeleaz
setMediaTime asupra tuturor Controller-elor
setMediaTime i repornete
administrate
toate Controller-ele.
Apeleaz setRate asupra Oprete toate Controller-ele
tuturor Controller-elor administrate, apeleaz
administrate. Returneaz rata setRate i repornete toate
setRate
actual care este suportat de Controller-ele. Returneaz
toate Controller-ele i o rata curent care este
seteaz. suportat de toate Player-ele.
Se asigur c toate Controller-
ele administrate se afl n Depinde de implementarea
starea Prefetched i apeleaz Player-ului. Acesta poate
Start
syncStart asupra fiecruia genera imediat un eveniment
dintre ele, innd cont de StartEvent.
latenele lor de start.
Player-ul de administrare Player-ul de administrare
posteaz imediat un eveniment genereaz imediat un
RealizeCompleteEvent. Pentru RealizeCompleteEvent.
Realize
a putea fi adugat, un Pentru a putea fi adugat, un
Controller trebuie s fie Controller trebuie s fie
realized. realized.
Player-ul de administrare
genereaz imediat un
Apeleaz prefetch asupra
PrefetchCompleteEvent,
Prefetch tuturor Controller-elor
indicnd c toate Controller-
administrate.
ele administrate sunt
Prefetched.

46
Apeleaz metoda stop asupra
Stop Nu are efect. tuturor Controller-elor
administrate.
Apeleaz deallocate asupra
deallocate tuturor Controller-elor Genereaz o eroare.
administrate.
Apeleaz setStopTime asupra
Apeleaz setStopTime asupra
tuturor Controller-elor
tuturor Controller-elor
setStopTime administrate. (Poate fi apelat
administrate. (Player-ul
o singur dat asupra unui
trebuie s fie Realized).
Player pornit).
Apeleaz syncStart asupra
syncStart tuturor Controller-elor Genereaz o eroare.
administrate.
Apeleaz close asupra tuturor
Close Genereaz o eroare.
Controller-elor administrate..
Tabel 8.6. Metodele de control aplicabile asupra unui Player administrator
Pentru ndeprtarea unui Controller se folosete metoda
removeController. Exemplul 8.7 arat cum se face eliberarea lui player1 de sub
administrarea lui player2.

Exemplul 8.7. Eliberarea administrrii unui Controller


player2.removeController(player1);

8.5. Procesarea datelor dependente de timp


Un Processor poate fi folosit ca i un Player programat care permite
utilizatorului s controleze procesele de decodare i afiare. Un Processor poate
fi folosit i ca un Processor de captur, permind utilizatorului s controleze
procesele de codare i multiplexare a datelor capturate.
Controlul asupra aciunilor efectuate de un Processor se poate face n
mai multe moduri:

se folosete un ProcessorModel pentru a construi un


Processor la care se cunosc att intrarea ct i ieirea;
se folosete metoda TrackControl setFormat pentru a
specifica ce conversii de format sunt efectuate asupra pistelor
individuale;

47
se folosete metoda setOutputDescriptor pentru a specifica
formatul datelor multiplexate la ieirea din Processor;
se folosete metoda TrackControl setCodecChain pentru
selecia codec-urilor sau a efectelor folosite de Processor;
se folosete metoda TrackControl setRenderer pentru a
selecta plug-in-ul Renderer folosit de Processor.
8.5.1. Configurarea unui Processor
n afara fazelor Realizing i Prefetching prin care orice Processor trece
cnd se pregtete de pornire, un Processor trece i prin faza de configurare,
numit Configuring. Metoda configure este apelat pentru a trece un Processor
din starea Unrealized n starea Configuring.
n timpul n care se afl n starea Configuring, un Processor culege
informaia de care are nevoie pentru a construi obiecte TrackControl pentru
fiecare pist. Cnd termin, trece n starea Configured i genereaz un
eveniment ConfigureCompleteEvent. Odat ce un Processor este Configured, se
pot seta formatul datelor la ieire i opiunile pentru TrackControl. Cnd se
termin setarea acestor opiuni, se apeleaz metoda realize pentru a trece
Processor-ul n starea Realizing.
Odat ce un Processor este n starea Realized, modificrile viitoare de
modificare a opiunilor de procesare nu sunt garantate c vor funciona. n
majoritatea cazurilor, se va primi o excepie FormatChangeException.
8.5.2. Selectarea opiunilor de procesare a pistelor
Pentru a selecta ce plug-in-uri sunt folosite la procesarea fiecrei piste
din stream-ul de date, trebuie:

apelat metoda PlugInManager.getPlugInList pentru a


determina ce plug-in-uri sunt disponibile. PlugInManager returneaz o
list cu plug-in-urile care corespund formatelor de intrare i ieire
folosite;
apelat metoda getTrackControls asupra Processor-ului
pentru a obine un TrackControl pentru fiecare pist din stream-ul de
date. Processor-ul trebuie s fie n starea Configured nainte de
realizarea apelului;
apelat una din metodele TrackControl setCodecChain sau
setRenderer pentru a specifica plug-in-urile pe care dorim sa le
folosim pentru fiecare pist.

Cnd se folosete setCodecChain pentru specificarea codec-ului i a


plug-in-urilor de efect pentru un Processor, ordinea n care plug-in-urile apar n

48
lanul de procesare este determinat de formatul datelor de intrare i de ieire pe
care fiecare plug-in l suport.
Pentru a controla codarea efectuat asupra unei piste de ctre un
anumit Codec, se pot folosi controalele pentru codec asociate acelei piste.
Pentru a obine aceste controale, trebuie apelat metoda TrackControl
getControls. Aceasta returneaz toate controalele asociate cu aceai pist,
inclusiv controalele de codec-uri cum sunt: H263 Control, QualityControl i
MPEGAudioControl.
8.5.3. Conversia datelor dintr-un format n altul
Se poate selecta formatul unei anumite piste prin apelarea unei metode
TrackControl pentru acea pist:

trebuie apelat metoda getTrackControls asupra Processor-


ului pentru a obine un TrackControl pentru fiecare pist din stream-ul
de date. Processor-ul trebuie s fie n starea Configured nainte de
apelarea metodei;
trebuie folosit metoda TrackControl setFormat pentru a
specifica formatul n care se dorete conversia pistei selectate.
8.5.4. Specificarea formatului datelor de ieire
Se poate folosi metoda setContentDescriptor pentru a specifica
formatul datelor la ieirea din Processor. Pentru a obine lista cu formatele de
date suportate trebuie apelat metoda getSupportedContentDescriptors.
Mai exist o metod de specificare a formatului de ieire prin folosirea
metodei ProcessorModel la crearea Processor-ului.
Specificarea unui formate a datelor de ieire selecteaz automat
opiunile de procesare implicite pentru acest format, suprascriindu-le peste
opiunile selectate cu TrackControls. Setarea formatului datelor la valoarea null
cauzeaz operaia de trimitere napoi a datelor.
8.5.5. Specificarea destinaiei datelor
Este posibil specificarea destinaiei datelor prin selectarea unui
anumit Renderer pentru o pist prin intermediul TrackControl. O alt metod
este folosirea ieirii unui Processor ca intrare ntr-o anumit DataSink. Ieirea
unui Processor poate fi folosit i ca intrare pentru un alt Player sau Processor
care are o alt destinaie.
8.5.6. Selectarea dispozitivului de transmisie
Pentru a selecta dispozitivul de transmisie dorit, trebuie:

49
apelat metoda getTrackControls asupra Processor-ului
pentru a obine un TrackControl pentru fiecare pist din stream-ul de
date. Processor-ul trebui s fie n starea Configured nainte de apelarea
metodei;
apelat metoda TrackControl setRenderer pentru a specifica
plug-in-ul.
8.5.7. Salvarea detelor ntr-un fiier
Pentru a salva datele ntr-un fiier trebuie folosit un DataSink pentru a
citi datele de la ieirea Processor-ului. Trebuie urmai urmtorii pai:

obinut DataSource-ul de ieire de la Processor prin


apelarea metodei getDataOutput;
trebuie construit o procedur de scriere n fiier prin
apelarea metodei Manager.createDataSink;
trebuie apelat metoda open asupra DataSink-ului pentru a
deschide fiierul;
trebuie apelat metoda start asupra DataSink-ului pentru a
ncepe scrierea datelor.

Formatul datelor scrise n fiierul specificat este controlat de ctre
Processor. Implicit acesta trimite spre ieire date neprelucrate. Pentru a
schimba tipul coninutului pentru DataSource, trebuie folosit metoda
setContentDescriptor, lucru demonstrat n Exemplul 8.8.

Exemplul 8.8. Schimbarea tipului coninutului unei DataSource


DataSink sink;
MediaLocator dest = new MediaLocator(file://exemplul8.wav);
try{
sink = Manager.createDataSink(p.getDataOutput(), dest);
sink.open();
sink.start();
} catch (Exception) {}

Un Processor permite utilizatorului s controleze un numr de bii pe


care l poate scrie la destinaie. Aceasta se realizeaz prin metoda
StreamWriterControl. Pentru a afla dac un stream suport aceast operaie
trebuie apelat metoda sub forma:

getControl("javax.media.datasink.StreamWriterControl")

50
asupra Processor-ului.
8.5.8. Conectarea unui Processor la un alt Player
Datele de la ieirea unui Processor pot fi folosite ca date de intrare la
alt Player. Pentru a obine datele de la ieirea unui Processor trebuie apelat
metoda getDataOutput, care returneaz un obiect de tipul DataSource. Acest
obiect poate fi folosit la construirea unui Player sau a unui Processor prin
intermediul unui Manager.
8.6. Captura datelor n Java Media Framework
ncepnd cu versiunea 2.0, JMF ofer programatorilor posibilitatea de
a achiziiona date de la un dispozitiv de captur, cum ar fi un microfon sau o
camer video. Aceast operaie poart numele de capturare. Datele capturate
pot fi capturate i afiate sau pot fi stocate pentru o utilizare ulterioar.
Pentru a captura date trebuie:

localizat dispozitivul de captur prin interogarea


CaptureDeviceManager;
creat un obiect CaptureDeviceInfo pentru acel dispozitiv;
creat un MediaLocator pentru obiectul CaptureDeviceInfo i
folosit la crearea unei DataSource;
creat un Player sau Processor folosind acest DataSource;
pornit Player-ul sau Processor-ul pentru a ncepe procesul de
captur.

Cnd se folosete o DataSource cu un Player, datele capturate pot fi


doar afiate. Pentru a procesa i stoca datele trebuie folosit o DataSource n
combinaie cu un Processor.
8.6.1. Accesarea dispozitivelor de captur
Dispozitivele de captur sunt accesibile prin intermediul
CaptureDeviceManager. Acesta este registrul central pentru toate dispozitivele
de captur disponibile n JMF. Pentru a obine o list cu dispozitivele de captur
disponibile trebuie apelat metoda CaptureDeviceManager.getDeviceList.
Fiecare dispozitiv de captur este reprezentat ca un obiect
CaptureDeviceInfo. Pentru a obine acest obiect pentru un anumit dispozitiv
trebuie apelat metoda CaptureDeviceManager.getDevice astfel:

Exemplul 8.9. Obinerea informaiei despre dispozitivul de captur


CaptureDeviceInfo deviceInfo =
CaptureDeviceManager.getDevice("devName");

51
8.6.2. Captura efectiv a datelor
Pentru a captura date de la un anumit dispozitiv de captur, trebuie
obinut MediaLocator din obiectul CaptureDeviceInfo al acelui dispozitiv.
Exist posibilitatea de a folosi acest MediaLocator pentru a construi un Player
sau Processor direct sau de a-l folosi pentru a construi o DataSource care s fie
folosit ca intrare ntr-un Player sau Processor. Pentru a iniia procesul de
captur trebuie pornit Player-ul sau Processor-ul.
8.6.3. Controlul utilizatorului asupra procesului de captur
n general un dispozitiv de captur are implementat un set de atribute
specifice care pot fi folosite pentru a controla acel dispozitiv. Pentru a permite
controlul utilizatorului asupra acestor setri, n JMF se folosesc dou tipuri de
control: PortControl i MonitorControl. Accesarea acestor controale se
realizeaz prin apelarea metodei getControl asupra DataSource-ului avnd ca
parametru atributul care dorim s l modificm.
Un PortControl ofer posibilitatea selectrii portului de unde datele
vor fi capturate.
Un MonitorControl ofer posibilitatea monitorizrii procesului de
captur.
Ca i alte obiecte de tip Control, dac exit o component vizual care
corespunde lui PortControl sau MonitorControl, aceasta poate fi obinut prin
apelarea getControlComponent. Adugarea acestei componente ferestrei applet-
ului sau ferestrei aplicaiei ofer utilizatorului posibilitatea controlului capturii.
Este posibil afiarea panoului standard de control i a componentelor
vizuale asociate cu un Player sau cu un Processor. Exemplul 8.10 ilustreaz
cum se realizeaz afiarea componentelor GUI (Graphic User Interface) pentru
un Processor.

Exemplul 8.10. Afiarea componentelor GUI pentru un Processor


Component controlPanel, visualComponent;
if ((controlPanel = p.getControlPanelComponent()) != null)
add(controlPanel);
if ((visualComponent=p.getVisualComponent())!=null)
add(visualComponent);

8.6.4. Stocarea datelor capturate


Pentru a salva datele capturate ntr-un fiier trebuie folosit un
Processor n locul unui Player. Se folosete un DataSink pentru citirea datelor
la ieirea din Processor i pentru a le salva ntr-un fiier. Paii ce trebuie urmai
sunt:

52
se citesc datele de la ieirea Processor-ului prin apelarea
getDataOutput;
se construiete un DataSink pentru scrierea n fiier prin
apelarea Manager.createDataSink. Parametrii sunt DataSource-ul de
ieire i un MediaLocator care specific locaia fiierului n care se
dorete salvarea;
se apeleaz metoda open asupra DataSink-ului pentru a
deschide fiierul;
se apeleaz metoda start asupra DataSink-ului;
se apeleaz metoda start asupra Processor-ului pentru a
ncepe captura datelor;
se ateapt pentru un eveniment EndOfMediaEvent, un
anumit timp sau intervenia utilizatorului;
se apeleaz metoda stop asupra Processor-ului pentru a
termina captura datelor;
se apeleaz metoda close asupra Processor-ului;
cnd Processor-ul este oprit i DataSink-ul posteaz un
eveniment EndOfStreamEvent trebuie apelat metoda close asupra
DataSink-ului.

Exemplul 8.11 arat cum se realizeaz toate aceste operaii necesare


pentru salvarea datelor capturate ntr-un fiier.

Exemplul 8.11. Salvarea datelor capturate ntr-un fiier


DataSink sink;
MediaLocator dest = new MediaLocator("file://exemplul10.avi");
try {
sink = Manager.createDataSink(p.getDataOutput(), dest);
sink.open();
sink.start();
} catch (Exception) {}

8.6.5. Exemplu de captur i salvare a datelor audio i video


n cele ce urmeaz voi prezenta un exemplu n care un
ProcessorModel pentru a crea un Processor pentru a captura date audio i
video, pentru a coda datele IMA4 (pentru audio) i Cinepak (pentru video),
pentru a intercala pistele i pentru a salva pista rezultat n urma intercalrii
ntr-un fiier QuickTime.
Cnd se construiete un ProcessorModel prin specificarea formatului
pistelor i a coninutului ieirii iar apoi se folosete acel model la construirea

53
unui Player, Processor-ul este conectat automat la dispozitivul de captur care
ndeplinete necesitile formatului (dac exist unul).

Exemplul 8.12. Captura i salvarea datelor audio i video


formats[0] = new AudioFormat(AudioFormat.IMA4);
formats[1] = new VideoFormat(VideoFormat.CINEPAK);

FileTypeDescriptor outputType = new


FileTypeDescriptor(FileTypeDescriptor.QUICKTIME);
Processor p = null;
try {
p = Manager.createRealizedProcessor(new
ProcessorModel(formats, outputType));

} catch (IOException e) {
System.exit(-1);

} catch (NoProcessorException e) {
System.exit(-1);

} catch (CannotRealizeException e) {
System.exit(-1);
}
// citete de la ieirea Processor-ului
DataSource source = p.getDataOutput();

// creaza un MediaLocator cu locatia fisierului unde se doreste


salvarea bitilor

MediaLocator dest = new MediaLocator("file://test.mov");

//creaza un DataSink care sa realizeze deschiderea fisierului


DataSink filewriter = null;

try {
filewriter = Manager.createDataSink(source, dest);
filewriter.open();

} catch (NoDataSinkException e) {
System.exit(-1);
} catch (IOException e) {

54
System.exit(-1);
} catch (SecurityException e) {
System.exit(-1);
}

// porneste FileWriter si Processor-ul


try {
filewriter.start();
} catch (IOException e) {
System.exit(-1);
}

p.start();
//opreste si inchide Processor-ul cand se termina captura
// inchide DataSink-ul cand apare evenimentul EndOfStream

8.7. Real-Time Transport Protocol


n continuare ne vom ocupa de protocolul RTP (Real-Time Transport
Protocol), protocol folosit la transmiterea i recepionarea stream-urilor media
n reea. Suportul pentru acest protocol a fost introdus abia ncepnd cu
versiunea 2.0.
8.7.1. Lucrul cu stream-uri media n timp real
8.7.1.1. Stream-urile media
Streamul reprezint un canal de comunicaie ntre dou entiti, o
entitate care emite i una care recepioneaz. Stream-urile pot fi de dou feluri:
de intrare i de ieire.
Stream-urile media se ntlnesc peste tot n Internet, numrul de
portaluri care ofer transmisii radio i TV fiind ntr-o continu cretere.
8.7.1.2. Protocoale folosite pentru stream-urile media
Transmiterea datelor media n timp real necesit ca procesul s se
desfoare ntr-o reea cu o lrgime de band mare. La datele media, pierderea
pachetelor este mai acceptabil dect apariia unor ntrzieri mari n
transmiterea datelor. Acest lucru este total diferit de accesarea unor date statice,
de exemplu un fiier, cnd cel mai important lucru este ca toate pachetele s
ajung la destinaie. Din acest motiv protocoalele care se folosesc la transmisia
acestor date statice nu se potrivesc prea bine datelor multimedia.
Protocoalele HTTP i FTP sunt bazate pe TCP (Transmission Control
Protocol) care este un protocol al stratului transport pentru care este important

55
transmiterea i recepionarea datelor fr erori n reea. Cnd un pachet este
pierdut sau recepionat incorect acel pachet se retransmite.
Din aceast cauz la transmiterea datelor multimedia sunt folosite alte
protocoale dect TCP, cel mai frecvent utilizat fiind UDP (User Datagram
Protocol). UDP nu garanteaz recepia pachetelor la destinaie: nu este capabil
s compenseze datele pierdute i nici nu cere retransmisia.
Ca i TCP, UDP este un protocol al stratului transport, un protocol la
nivelul de jos al reelei, care st la baza dezvoltii multor aplicaii.
Standardul Internet folosit la transmiterea datelor audio i video este
RTP (Real-Time Transport Protocol).
8.7.1.3. Protocolul RTP
RTP ofer servicii punct-la-punct pentru transmiterea datelor n timp
real. RTP este un protocol independent de reea, dei este folosit des peste UDP.

Real-Time Media Framework i Aplicaii


Real-Time Control Protocol (RTCP)
Real-Time Transport Protocol (RTP)
Alte protocoale din straturile Reea i UDP
Transport (TCP, ARM, ST-II, etc.) IP
Figura 8.19. Arhitectura RTP
RTP poate fi utilizat att n reele unicast ct i multicast. n reelele
unicast copii separate ale datelor sunt trimise de la surs spre fiecare destinaie.
n reelele multicast datele sunt trimise de la surs o singur dat i reeaua este
responabil s trimit datele spre locaii multiple. Reelele multicast sunt mai
eficiente pentru aplicaii multimedia cum ar fi videoconferinele. Standardul IP
(Internet Protocol) suport multicastul.
8.7.1.3.1. Serviciile RTP
RTP permite identificarea tipurilor de date transmise, determin
ordinea pachetelor de date care trebuie prezentate i sincronizeaz datele media
provenite de la surse diferite.
RTP nu garanteaz ordinea de sosire a pachetelor. Ordonarea acestora
i detectarea pachetelor pierdute este n sarcina receptorului. Informaiile
folosite pentru aceste operaii se afl n header-ele pachetelor.
Protocolul de control RTCP permite monitorizarea calitii datelor
distribuite i identific mecanismele necesare transmisiunii RTP.
Dac calitatea serviciilor este esenial pentru o anumit aplicaie, RTP
poate fi folosit peste un protocol care permite servicii orientate pe conexiune.
8.7.1.3.2. Arhitectura RTP

56
O sesiune RTP este o asociere ntre un set de aplicaii care comunic
folosind protocolul RTP. O sesiune este identificat printr-o adres a reelei i o
pereche de porturi. Un port este folosit pentru datele media iar cellalt este
folosit pentru datele de control (RTCP).
Un participant este o main unic, gazd sau utilizator care particip
la o sesiune. Participarea ntr-o sesiune poate consta ntr-o recepie pasiv a
datelor (receptor), ntr-o transmisie activ a datelor (transmitor) sau ambele.
Fiecare tip de date este transmis ntr-o sesiune diferit. Spre exemplu,
dac ntr-o conferin se transmit att video ct i audio, o sesiune este folosit
la transmiterea datelor video i o sesiune separat este folosit la transmiterea
datelor audio. Aceasta permite participanilor la conferin s aleag care tip de
date media doresc s recepioneze spre exemplu, cineva care are o conexiune
la reea cu band redus ar putea dori s recepioneze doar partea audio a
conferinei.
Transmisia datelor media ntr-o sesiune RTP se face sub form de
pachete. O serie de pachete care provin din aceeai surs poart numele de
stream RTP. Fiecare pachet are un header (antet) i un payload (datele utile).
Figura urmtoare prezint header-ul unui pachet RTP.
Bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

V P X CC M PT Numrul secvenei
Timestamp
Sursa sincronizrii (SSRC)
Sursa coninutului (CSRC) (0-15)

Figura 8.20. Header-ul unui pachet RTP


Header-ul unui pachet RTP conine:

Numrul versiunii RTP (V): 2 bii. Versiunea definit de


specificaiile curente este 2.
Padding (umplere) (P): 1 bit. Dac bitul este setat, la sfritul
pachetului exist unul sau mai muli bii care nu fac parte din payload.
Ultimul byte din pachet indic numrul de bii de padding. Este folosit
de unii algoritmi de criptare.
Extensia (X): 1 bit. Dac bitul este setat, dup header-ul fix
urmeaz o extensie. Acest mecanism de extensie permite
implementrilor s adauge informaii header-ului RTP.
CSRC Count (CC): 4 bii. Semnific numrul de
identificatori CSRC care urmeaz header-ului fix. Dac contorul
CSRC este zero, sursa sincronizrii este sursa payload-ului.
Marker (M): 1 bit. Bitul de marker este definit de profilul
particular al mediului de transmisie.

57
Tipul payload-ului (PT): 7 bii. Este un index ntr-un tabel cu
profilele posibile care descrie formatul payload-ului. Maprile pentru
payload-urile audio i video sunt specificate n RFC 1890.
Numrul secvenei: 16 bii. Un numr unic care identific
poziia pachetului n secvena de pachete. Se incrementeaz automat
cu 1 pentru fiecare pachet trimis.
Timestamp: 32 bii. Mai multe pachete consecutive pot avea
acelai timestamp dac sunt generate logic n acelai timp spre
exemplu, dac toate fac parte acelai cadru video.
SSRC: 32 bii. Identific sursa sincronizrii. Dac CSRC este
zero, sursa payload-ului este sursa sincronizrii.
CSRC: 32 bii fiecare. Identific sursele care contribuie la
payload. Pot fi maxim 16 surse.

n afara datelor media, participanii la sesiune primesc pachete de


control (RTCP) care sunt trimise periodic de ctre surs. Pachetele RTCP pot
conine informaii despre calitatea serviciilor (Quality of Services) pentru
participanii la sesiune, informaii despre portul pe care transmite sursa i
statistici despre datele transmise pn n acel moment.
Exist mai multe tipuri de pachete RTCP:

raportul sursei
raportul destinaiei
descrierea sursei
de tip Bye
specifice aplicaiei

Pachetele RTCP sunt stivuibile (stackable) i sunt trimise ca un pachet
combinat care conine cel puin dou pachete, un pachet de raport i un pachet
cu descrierea sursei.
Toi participanii ntr-o sesiune transmit pachete RTCP. Un participant
care a trimis de curnd date emite un raport de tipul raportul sursei. Acesta
conine att informaii despre numrul total de pachete i octei trimii, precum
i informaii care pot fi folosite la sincronizarea stream-urilor din sesiuni
diferite.
Participanii la sesiune transmit periodic pachete de tip raportul
destinaiei spre toate sursele de unde primesc pachete cu date. Acesta conine
informaii despre numrul de pachete pierdute, numrul maxim al secvenelor
primite i despre estimarea ntrzierilor ce apar la comunicarea ntre surs i
destinaie.

58
Primul pachet dintr-un pachet RTCP combinat trebuie s fie un pachet
de raport, chiar dac nu au fost transmise sau recepionate date (n acest caz un
raport gol este trimis).
Toate pachetele RTCP combinate trebuie s includ o descriere a
sursei (SDES), element care conine numele canonic (CNAME) care identific
sursa. Alte informaii pot fi incluse n descrierea sursei, cum ar fi numele sursei,
adresa de e-mail, numrul de telefon, locaia geografic sau numele aplicaiei.
Cnd o surs nu mai este activ, trimite un pachet RTCP Bye. Acesta
poate include i motivul pentru care sursa prsete sesiunea.
Pachetele RTCP APP prevd un mecanism care permite aplicaiilor s
defineasc i s transmit informaii utilizator prin portul de control RTP.
8.7.1.3.3. Aplicaiile RTP
Aplicaiile RTP sunt clasificate de obicei n cele care primesc date din
reea (clieni RTP) i cele care transmit date n reea (servere RTP). Unele
aplicaii pot ndeplini ambele funcii spre exemplu aplicaiile de
videoconferin captureaz i transmit date n timp ce recepioneaz date din
reea.
8.7.1.3.4. Recepia stream-urilor media din reea
Recepia streamurilor RTP este necesar n cazul ctorva tipuri de
aplicaii. Spre exemplu:

aplicaiile de conferin trebuie s poate recepiona un stream


media dintr-o sesiune RTP i s o transmit consolei;
un robot telefonic trebuie s poate recepiona stream-uri
media dintr-o sesiune RTP i s le stocheze ntr-un fiier;
o aplicaie care nregistreaz o conversaie sau o conferin
trebuie s poat recepiona stream-uri media dintr-o sesiune, s le
trasnsmit consolei i n acelai timp s le stocheze ntr-un fiier.
8.7.1.3.5. Transmisia stream-urilor media ntr-o reea
Aplicaiile RTP de tip server transmit stream-uri media stocate sau
capturate n reea.
Spre exemplu, ntr-o aplicaie de conferin, un stream media poate fi
capturat de la o camer video i transmis ntr-una sau mai multe sesiuni RTP.
Stream-urile media trebuie codate n formate media multiple i transmise n mai
multe sesiuni RTP pentru realizarea conferinei cu receptoare eterogene.
Conferina multiparty poate fi implementat fr a folosi multicast IP prin
folosirea unui sesiuni RTP unicast multiple.
8.7.2. nelegerea API-ului JMF RTP

59
JMF permite rularea i transmisia stream-urilor RTP prin API-urile
definite n pachetele javax.media.rtp, javax.media.rtp.event i
javax.media.rtp.rtcp.
Stream-urile RTP pot fi rulate local, salvate ntr-un fiier sau ambele.

Figura 8.21. Recepia RTP


Spre exemplu, un API RTP poate fi utilizat pentru a implementa o
aplicaie de telefonie care rspunde la apeluri i nregistreaz mesajele, ca i un
robot telefonic.
Similar, API-ul RTP se poate folosi pentru a transmite n reea stream-
uri media capturate sau stocate local. Aceste stream-uri pot proveni dintr-un
fiier sau direct de la un dispozitiv de captur.

Figura 8.22. Transmisia RTP


Spre exemplu, este posibil implementarea unei aplicaii de
videoconferin care captureaz video i audio i le transmite n reea folosind
sesiuni RTP diferite pentru fiecare tip de date.
Similar, se poate nregistra o conferin pentru transmisie ulterioar
sau se poate folosi un stream audio nregistrat anterior pentru a rula automat n
pauzele ce apar ntr-o videoconferin (hold music).
8.7.2.1. Arhitectura RTP
API-urile JMF RTP sunt proiectate pentru a conlucra cu capabilitile
JMF de capturare, procesare i prezentare. Player-ele i Processoarele prezint
i manipuleaz stream-urile RTP ca i pe alt coninut media. Se pot transmite

60
stream-uri media care au fost capturate de la un dispozitiv de captur local
(folosind o DataSource), sau care au fost stocate ntr-un fiier (folosind un
DataSink).

Aplicaii JAVA, Applet-uri, Bean-uri

API-ul Java Media Framework


API-urile RTP

API-ul Java Media Framework Plug-in

Codec-uri despachetare Codec-uri mpachetare

Figura 8.23. Arhitectura de nivel nalt JMF RTP

8.7.2.1.1. Managerul de sesiune


n JMF, un SessionManager este folosit la coordonarea unei sesiuni
RTP. El urmrete participanii la sesiune i stream-urile care sunt transmise.
Un manager de sesiune este reprezentarea local a unei entiti
distribuite, sesiunea RTP. O alt sarcin a managerului de sesiune este trata
canalul de control RTCP i de a suporta RTCP att pentru transmitor ct i
pentru receptor.
Interfaa SessionManager definete metode care care permit aplicaiei
s se iniializeze i s nceap participarea la o sesiune, s tearg stream-urile
folosite n timpul sesiunii i n final s nchid sesiunea.
Managerul de sesiune menine statistici despre toate pachetele RTP i
RTCP trimise i primite ntr-o sesiune. Statisticile se pot referi la ntreaga
sesiune sau doar la un singur stream de date. Mangerul de sesiune permite
accesul la statisticile globale de recepie i transmisie:

GlobalReceptionStats: menine statisticile globale despre


recepii;
GlobalTransmissionStats: menine statisticile cumulative
despre transmisii pentru toi transmitorii locali.

Statisticile despre un anumit participant la sesiune sau despre un


stream transmis sunt disponibile prin:

61
ReceptionStats: menine statistici despre recepie pentru un
anumit participant;
TransmissionStats: menine statistici despre un anumit stream
transmis n reea.

Managerul de sesiune coordoneaz toi participanii la o sesiune.


Fiecare participant este reprezentat de o instan a unei clase care
implementeaz interfaa Participant. SessionManager creeaz un Participant
ori de cte ori sosete un pachet RTCP care conine descrierea unei surse
(SDES) cu un nume canonic (CNAME) care nu a mai aparut n aceea sesiune
(sau a expirat de la ultima folosire).
Participanii pot fi pasivi (doar trimit pachete de control) sau activi
(trimit i unul sau mai multe stream-uri RTP).
Exist exact un participant local care reprezint un participant local
client/server. Un participant local indic faptul c va trimite mesaje de control
RTCP prin nceperea unei sesiuni.
Un participant poate controla mai mult de un singur stream, fiecare
fiind identificat printr-un SSRC (synchronization source identifier) folosit de
sursa stream-ului.
SessionManager-ul menine un obiect RTPStream pentru fiecare
stream de pachete RTP ntr-o sesiune. Exist dou tipuri de stream-uri RTP:

ReceiveStream reprezint un stream care a fost primit de la un
alt participant la sesiune;
SendStream reprezint un stream primit de la un Processor
sau de la o DataSource.

Un ReceiveStream este construit automat ori de cte ori un manager de


sesiune detecteaz o nou surs de date RTP. Pentru a crea un SendStream,
trebuie apelat metoda SessionManager createSendStream.
8.7.2.1.2. Evenimente RTP
Mai multe evenimente specifice RTP-ului sunt definite n
javax.media.rtp.event. Aceste evenimente sunt folosite la raportarea strilor
sesiunilor RTP i la raportarea strilor stream-urilor.

62
Figura 8.24. Evenimentele RTP

Pentru a primi notificaii despre evenimentele RTP, trebuie


implementat i nregistrat cu manager-ul de sesiune un listener RTP:

SessionListener: primete notificri despre schimbrile strii


sesiunii;
SendStreamListener: primete notificri despre schimbrile
strii unui stream RTP care este transmis;

63
ReceiveStreamListener: primete notificri despre schimbrile
strii unui stream RTP care este primit;
RemoteListener: primete notificri despre evenimente sau
mesaje de control RTP primite de la un participant la sesiune.

Se poate implementa un SessionListener pentru a primi notificri


despre evenimentele care se refer la ntreaga sesiune RTP, cum ar fi primirea
unor noi participani.
Exist dou tipuri de evenimente care se refer la sesiune:

NewParticipantEvent: indic faptul c un nou participant a


aderat la sesiune;
LocalColisionEvent: indic faptul c sursa de sincronizare
este deja folosit.

Se poate implementa un SendStreamListener pentru a primi notificri


ori de cte ori:

noi stream-uri ce urmeaz a fi transmise sunt create de


participantul local;
transferul de date de la DataSource, folosit pentru crearea
stram-ului ce urmeaz a fi trimis, a fost iniiat sau a fost terminat;
formatul datelor efective se modific.

Exist cinci tipuri de evenimente asociate cu un SendStream:

NewSendStreamEvent: indic faptul c un nou stream ce


urmeaz a fi trimis a fost creat de participantul local;
ActiveSendStreamEvent: indic faptul c transferul de date de
la DataSource a nceput;
InactiveSendStreamEvent: indic faptul c transferul datelor
de la DataSource a ncetat;
LocalPayloadChangeEvent: indic faptul c formatul stream-
ului sau datele utile s-au modificat;
StreamClosedEvent: indic faptul c stream-ul a fost terminat.

Se poate implementa un ReceiveStreamListener pentru a primi


notificri ori de cte ori:

noi stream-uri de recepie sunt primite;


transferul datelor este iniiat sau terminat;
transferul datelor expir (times out);

64
un ReceiveStream neidentificat anterior este asociat cu un
Participan;.
este primit un pachet RTCP APP;
formatul datelor utile se modific.

Aceast interfa poate fi folosit i pentru a obine un handler pentru


stream i pentru a accesa DataSource-ul RTP pentru a creea un MediaHandler.
Exist apte tipuri de evenimente asociate cu un ReceiveStream:

NewReceiveStreamEvent: indic faptul c managerul de


sesiune a creat un nou stream pentru o surs nou detectat;
ActiveReceiveStreamEvent: indic faptul c transferul datelor
a nceput;
InactiveReceiveStreamEvent: indic faptul c transferul
datelor a ncetat;
TimeoutEvent: indic faptul c transferul datelor a expirat;
RemotePayloadChangeEvent: indic faptul c formatul
datelor utile din stream-ul recepionat s-a modificat;
StreamMappedEvent: indic faptul c un stream nidentificat
anterior a fost asociat cu un participant la sesiune;
Application Event: indic faptul c un pachet RTCP APP a
fost recepionat;

Pentru a primi notificri despre evenimentele sau mesajele de control


RTP primite de la un participant n reea, se folosete RemoteListener. De
obicei RemoteListener este implementat ntr-o aplicaie folosit la
monitorizarea sesiunii permite primirea de rapoarte RTCP i monitorizarea
calitii recepiei fr a primi date n fiecare stream.
Exist trei tipuri de evenimente asociate cu un participant la distan:

ReceiverReportEvent: indic faptul c un raport de recepie


RTP a fost primit;
SenderReportEvent: indic faptul c un raport de transmisie
RTP a fost primit;
RemoteCollisionEvent: indic faptul c doi participani
folosesc o surs cu acelai ID pentru sincronizare (SSRC).
8.7.2.1.3. Datele RTP
Stream-urile dintr-o sesiune RTP sunt reprezentate de obiecte
RTPStream. Exist dou tipuri ale acestor obiecte: ReceiveStream i
SendStream. Fiecare stream RTP are un buffer pentru sursa de date asociat cu

65
el. Pentru ReceiveStreams, aceast DataSource este ntotdeauna
PushBufferDataSource.
Managerul de sesiune construiete automat noi stream-uri de recepie
n momentul n care detecteaz recepia unor stream-uri de la participanii la
sesiune. Noi stream-uri de transmisie se construiesc prin apelarea
createSendStream asupra managerului de sesiune.
API-urile JMF RTP sunt proiectate pentru a fi independente de
protocolul de transport folosit. Utilizatorul poate crea un handler de date RTP
pentru a permite ca comunicarea s se realizeze folosind un anumit protocol de
transport. Handler-ul de date este o DataSource care poate fi folosit ca intrare
pentru un Player.
Clasa abstract RTPPushDataSource definete elementele de baz ale
uni handler de date RTP. Acesta are att un stream de date de intrare
(PushSourceStream) ct i unul de ieire (OutputDataStream). Un handler de
date poate fi folosit att pentru canalul de date ct i pentru cel de control al
unei sesiuni RTP. Dac este folosit pentru canalul de date, handler-ul
implementeaz interfaa DataChannel.
Un RTPSocket este un RTPPushDataSource care are att canalul de
date ct i cel de control. Fiecare canal are un stream de intrare i unui de ieire
prin care emite/recepioneaz date n reea. Un RTPSocket poate exporta
RTPControls pentru a trimite dinamic managerului de sesiune informaii despre
datele utile.
Deoarece un RTPSocket creat de utilizator poate fi folosit la
construirea unui Player prin intermediul Manager-ului, JMF definete numele
i locaiile implementrilor RTPSocket:

<protocol package-prefix>.media.protocol.rtpraw.DataSource

Toate tipurile de date RTP folosesc un format specific RTP pentru


codare, format definit n clasele AudioFormat i VideoFormat. Spre exemplu,
pachetele gsm ncapsulate RTP au codarea setat pe AudioFormat.GSM_RTP,
n timp ce formatele video codate JPEG au codarea setat pe
VideoFormat.JPEG_RTP.
AudioFormat definete patru string-uri standard specifice codrii RTP:

public static final String ULAW_RTP =


"JAUDIO_G711_ULAW/rtp";
public static final String DVI_RTP = "dvi/rtp";
public static final String G723_RTP = "g723/rtp";
public static final String GSM_RTP = "gsm/rtp";

VideoFormat definete trei string-uri standard specifice codrii RTP:

66
public static final String JPEG_RTP = "jpeg/rtp";
public static final String H261_RTP = "h261/rtp";
public static final String H263_RTP = "h263/rtp";

8.7.2.1.4. Controlul RTP


API-ul RTP definete o interfa de control specific RTP, i anume
RTPControl. De obicei, aceasta este implementat de DataSource specifice
RTP. Asigur un mecanism care permite realizarea unei mapri ntre datele utile
asociate dinamic i un Format. Pe lng aceasta, RTPControl mai asigur i
metode de accesare a statisticilor despre sesiune i de obinere a Format-ului
curent al datelor utile.
SessionManager-ul extinde interfaa Controls, permind unui
manager de sesiune s exporte Controale adiionale prin metodele getControl i
getControls. Spre exemplu, managerul de sesiune poate exporta un
BufferControl pentru a permite utilizatorului s specifice lungimea buffer-ului
i pragul (treshold-ul).
8.7.2.2. Recepia
Prezentarea unui stream RTP recepionat este realizat de un Player.
Pentru a recepiona i prezenta un singur stream dintr-o sesiune RTP, trebuie
folosit un MediaLocator care informeaz sesiune c trebuie s construiasc un
Player. Un MediaLocator pentru o sesiune RTP este de forma:

rtp://address:port[:ssrc]/content-type/[ttl]

Player-ul este construit i conectat la primul stream din sesiune.


Dac se dorete prezentarea mai multor stream-uri din acea sesiune,
trebuie folosit managerul de sesiune. Se pot primi notificri de la acesta ori de
cte ori un stream este adugat sesiunii i se poate construi cte un Player
pentru fiecare stream nou. Folosirea unui manager de sesiune permite i
monitorizarea direct i controlul sesiunii.
8.7.2.3. Transmisia
Un manager de sesiune mai poate fi folosit i la iniializarea i
controlul unei sesiuni, pentru a putea transmite date n reea. Datele ce urmeaz
a fi transmise sunt primite de la un Processor.
Spre exemplu, pentru a crea un stream pentru a transmite date n direct
de la un dispozitiv de captur, trebuie:

67
creat, iniializat i pornit un SessionManeger pentru acea
sesiune;
construit un Processor folosind DataSource-ul de captur
potrivit;
setat formatul de ieire a datelor din Processor ntr-un format
specific RTP. Un codec de mpachetare RTP trebuie s fie disponibil
pentru a formata datele ce urmeaz a fi transmise;
preluate datele de la DataSource-ul de la ieirea Processor-
ului;
apelat createSendStream asupra managerului de sesiune i
trimis DataSource-ul spre acesta.

Transmisia este controlat prin metodele SendStream start i stop.


Cnd este apelat pentru prima dat, SessionManager-ul se comport ca
un receptor (trimite rapoarte de recepie RTCP). Cnd un SendStream este creat,
ncepe s trimit rapoarte de transmisie RTCP i se comport ca i un
transmitor atta timp ct unul sau mai multe stream-uri de transmisie exist.
Dac toate SendStream-urile sunt nchise (nu doar oprite), managerul de
sesiune revine la starea de receptor pasiv.
8.7.2.4. Extensibilitatea
Ca i alte pri ale JMF, capabilitile RTP pot fi modificate i extinse.
API-urile RTP suport un set de baz de formate RTP i tipuri de date.
Programatorii avansai i dezvoltatorii de tehnologii pot implementa plug-in-uri
JMF pentru a suporta date dinamic i pentru a permite lucrul cu formate RTP
adiionale.

8.8. Recepia i prezentarea stream-urilor RTP


Player-ele i Processoarele JMF prevd mecanismele de prezentare,
captur i conversie a datelor pentru stream-urile RTP.

68
Figura 8.25. Fluxul datelor spre recepia RTP
Un Player separat este folosit pentru fiecare stream recepionat de
managerul de sesiune. Pentru a construi un Player pentru un stream RTP se
folosete mecanismul standard Manager createPlayer. Exist dou posibiliti:

se folosete un MediaLocator care are parametrii unei sesiuni


RTP i se construiete un Player prin apelarea
Manager.createPlayer(MediaLocator;
se construiete un Player pentru un anumit ReceiveStream
prin obinerea DataSource-ului din stream-ul de date i prin
transmiterea lui la Manager.createPlayer(DataSource).

Dac se folosete un MediaLocator la construirea unui Player, se


poate prezenta doar primul stream RTP care este detectat n sesiune. Dac se
dorete rularea de stream-uri multiple ntr-o sesiune, trebuie folosit
SessionManager direct i trebuie construit un Player pentru fiecare
ReceiveStream.
8.8.1. Crearea unui Player pentru o sesiune RTP
Cnd se folosete un MediaLocator la construcia unui Player pentru o
sesiune RTP, Manager-ul creaz un Player pentru primul stream detectat n
sesiune. Acest Player posteaz un eveniment RealizeCompleteEvent odat ce
datele au fost detectate n sesiune.
Prin ascultarea pentru apariia evenimentului RealizeCompleteEvent,
se poate determina dac au sosit date i dac Player-ul este capabil s prezinte
aceste date. Odat ce Player-ul posteaz acest eveniment, se pot obine
componentele lui vizuale i de control.
Un Player poate exporta un control specific RTP, RTPControl, care
furnizeaz statistici i care poate fi folosit la nregistrarea dinamic cu
SessionManager-ul.

Exemplul 8.13. Crearea unui Player pentru o sesiune RTP


String url= "rtp://224.144.251.104:49150/audio/1";
MediaLocator mrl= new MediaLocator(url);

if (mrl == null) {
System.err.println("Nu se poate construi MRL-ul pentru RTP");
return false;
}

// Creeaza un Player pentru sesiune RTP curenta

69
try {
player = Manager.createPlayer(mrl);
} catch (NoPlayerException e) {
System.err.println("Eroare:" + e);
return false;
} catch (MalformedURLException e) {
System.err.println("Eroare:" + e);
return false;
} catch (IOException e) {
System.err.println("Eroare:" + e);
return false;
}

if (player != null) {
if (this.player == null) {
this.player = player;
player.addControllerListener(this);
player.realize();
}
}

Cnd un Player posteaz un eveniment FormatChangeEvent, indic


faptul c datele utile au suferit o modificare. Player-ele construite cu un
MediaLocator proceseaz aceste modificri automat. n majoritatea cazurilor,
aceast procesare implic construirea unui nou Player care s manipuleze
datele n noul format.
Aplicaiile care prezint stream-urile RTP trebuie s asculte pentru
evenimente FormatChangeEvents care pot rspunde dac un nou Player este
creat.
Cnd un eveniment FormatChangeEvent este postat, trebuie verificat
dac controlul obiectului i componentele vizuale ale Player-ului s-au
modificat. Dac da, un nou Player trebuie construit iar referinele la vechiul
Player trebuie nlocuite cu cele la noul Player.

Exemplul 8.14. Ascultarea pentru modificri de format RTP


public synchronized void controllerUpdate(ControllerEvent ce) {
if (ce instanceof FormatChangeEvent) {
Dimension vSize = new Dimension(320,0);
Component oldVisualComp = visualComp;

if ((visualComp = player.getVisualComponent()) != null) {

70
if (oldVisualComp != visualComp) {
if (oldVisualComp != null) {
oldVisualComp.remove(zoomMenu);
}
framePanel.remove(oldVisualComp);

vSize = visualComp.getPreferredSize();
vSize.width = (int)(vSize.width * defaultScale);
vSize.height = (int)(vSize.height * defaultScale);

framePanel.add(visualComp);

visualComp.setBounds(0, 0, vSize.width, vSize.height);


addPopupMenu(visualComp);
}
}

Component oldComp = controlComp;


controlComp = player.getControlPanelComponent();

if (controlComp != null)
{
if (oldComp != controlComp)
{
framePanel.remove(oldComp);
framePanel.add(controlComp);

if (controlComp != null) {
int prefHeight = controlComp.getPreferredSize().height;

controlComp.setBounds(0, vSize.height, vSize.width,


prefHeight);
}
}
}
}
}

8.8.2. Crearea unui Player RTP pentru fiecare stream


recepionat

71
Pentru a rula toate ReceiveStream-urile dintr-o sesiune, trebuie creat
un Player separat pentru fiecare stream. Cnd un nou stream este creat,
managerul de sesiune posteaz un eveniment NewReceiveStreamEvent. n
general trebuie nregistrat ca i ReceiveStreamListener i trebuie construit cte
un Player pentru fiecare ReceiveStream. Pentru a construi un Player, trebuie
obinut DataSource-ul din ReceiveStream i transmis la
Manager.createPlayer.
Pentru a crea un Player pentru fiecare stream recepionat ntr-o sesiune
trebuie:
1. setat sesiune RTP:

creat un SessionManager;
apelat RTPSessionMgr addReceiveStreamListener pentru a se
nregistra ca i listener;
pornete sesiunea RTP prin apelarea RTPSessionMgr
startSession;

Exemplul 8.15. Setarea unei sesiuni RTP


public SessionManager createManager(String address,
int port, int ttl, boolean listener,boolean sendlistener)
{
mgr = (SessionManager)new
com.sun.media.rtp.RTPSessionMgr();

if (mgr == null) return null;

mgr.addFormat(new AudioFormat(AudioFormat.DVI_RTP,
44100, 4, 1), 18);

if (listener) mgr.addReceiveStreamListener(this);
if (sendlistener) new RTPSendStreamWindow(mgr);

// cere manag. de sesiune sa genereze CNAME


String cname = mgr.generateCNAME();
String username = null;

try {
username = System.getProperty("user.name");
} catch (SecurityException e){
username = "jmf-user";
}

72
// creaza Session Address local
SessionAddress localaddr = new SessionAddress();

try{
InetAddress destaddr = InetAddress.getByName(address);

SessionAddress sessaddr = new SessionAddress(destaddr,


port, destaddr, port + 1);
SourceDescription[] userdesclist= new SourceDescription[]
{
new SourceDescription(SourceDescription
.SOURCE_DESC_EMAIL,
"jmf-user@sun.com", 1, false),

new SourceDescription(SourceDescription
.SOURCE_DESC_CNAME, cname, 1, alse),

new SourceDescription(SourceDescription
.SOURCE_DESC_TOOL, "JMF RTP Player v2.0",
1, false)
};

mgr.initSession(localaddr, userdesclist, 0.05, 0.25);


mgr.startSession(sessaddr,ttl,null);
} catch (Exception e) {
System.err.println(e.getMessage());
return null;
}

return mgr;
}

2. n metoda ReceiveStreamListener update, trebuie ascultat pentru


evenimentul NewReceiveStreamEvent, care indic c un nou stream de
date a fost detectat.
3. Cnd un eveniment NewReceiveStreamEvent este detectat, trebuie
apelat metoda getReceiveStream pentru a obine ReceiveStream.
4. Trebuie obinut DataSource-ul RTP din ReceiveStream prin apelarea
metodei getDataSource cu un Format specific RTP. Spre exemplu,
codarea pentru un player audio DVI va fi DVI_RTP.
5. Trebuie transmits DataSource-ul la Manager.createPlayer pentru a
construi un Player. Pentru ca acesta s fie construit cu succes, plug-in-

73
urile pentru decodare i despachetare a datelor formatate RTP trebuie
s fie disponibile.

Exemplul 8.16. Ascultarea pentru NewReceiveStreamEvents


public void update( ReceiveStreamEvent event)
{
Player newplayer = null;
RTPPlayerWindow playerWindow = null;

// gaseste sourceRTPSM pentru acest eveniment


SessionManager source = (SessionManager)event.getSource();

//creaza un Player daca un nou recvstream este detectat


if (event instanceof NewReceiveStreamEvent)
{
String cname = "Java Media Player";
ReceiveStream stream = null;

try
{
stream =((NewReceiveStreamEvent)event)
.getReceiveStream();

Participant part = stream.getParticipant();


if (part != null) cname = part.getCNAME();
DataSource dsource = stream.getDataSource();

// creaza un player prin transmiterea datasource la


// Media Manager
newplayer = Manager.createPlayer(dsource);
System.out.println("Player creat " + newplayer);
} catch (Exception e) {
System.err.println("Exceptie NewReceiveStreamEvent"
+ e.getMessage());
return;
}

if (newplayer == null) return;


playerlist.addElement(newplayer);
newplayer.addControllerListener(this);

// trimite acest player spre player GUI

74
playerWindow = new RTPPlayerWindow( newplayer, cname);
}
}

Dac datele utile dintr-un stream dintr-o sesiune RTP se modific,


ReceiveStream posteaz un eveniment RemotePayloadChangeEvent. De obicei,
cnd datele sufer modificri, Player-ul existent nu va recunoate noul format
i JMF va arunca o eroare dac se ncearc prezentarea datelor. Pentru a evita
aceasta, ReceiveStreamListener trebuie s urmreasc pentru evenimente
RemotePayloadChangeEvents. Cnd un astfel de eveniment este detectat,
trebuie:

nchis Player-ul curent;


retrase toate listener-ele care ineau de acel Player;
creat un nou Player cu aceeai DataSource RTP;
obinute Componentele vizuale i de control prntru noul
Player;
adugate listener-ele necesare pentru noul Player;

Exemplul 8.17. Tratarea modificrilor datelor utile din stream


public void update(ReceiveStreamEvent event) {
if (event instanceof RemotePayloadChangeEvent) {
// datele s-au modificat. Trebuie inchis player-ul vechi
// si creat unul nou

if (newplayer != null) {
// opreste player-ul si asteapta pentru evenimentul stop
newplayer.stop();

// retrage controllerlistener
newplayer.removeControllerListener(listener);

// nchide toate componentele vizuale si de control atasate


// acestei aplicatii
// inchide player-ul si asteapta pt. evenimentul close
newplayer.close();

// se blocheaza pana primeste ControllerClosedEvent..

try {
// cand player-ul este inchis, datasource-ul lui a fost

75
// deconectat. Acum trebuie reconectata inainte de a
// crea player-ul

rtpsource.connect();
newplayer = Manager.createPlayer(rtpsource);

if (newplayer == null) {
System.err.println("Nu se poate crea player-ul");
return;
}

newplayer.addControllerListener(listener);
newplayer.realize();

// cand noul player este in starea realized , obtine


// componentele lui vizuale si de control

} catch (Exception e) {
System.err.println("could not create player");
}
}
}
}

Buffer-ul de recepie RTP poate fi controlat prin BufferControl


exportat de ctre SessionManager. Acest control permite setarea a doi
parametrii: lungimea buffer-ului i pragul (treshold).
Lungimea buffer-ului este marimea buffer-ului meninut de ctre
receptor. Pragul este cantitatea minim de date care este stocat n buffer
nainte de trimiterea datelor. Datele vor fi disponibile de la acest obiect cnd
acest prag minim este atins. Dac datele din buffer nu ating acest prag, datele
vor fi buffer-ate din nou pn cnd pragul este atins.
Lungimea buffer-ului i valoarea de prag sunt specificate n
milisecunde. Numrul de pachete audio i de cadre video din buffer depinde de
formatul streamului de la intrare. Fiecare stream de intrare menine propriile
valori implicite i maxime att pentru lungimea buffer-ului ct i pentru pragul
minim. Valorile implicite i maxime sunt independente de aplicaie.
Pentru a obine BufferControl pentru o sesiune, trebuie apelat metoda
getControl asupra SessionManager-ului. Se poate obine o component GUI
pentru un BufferControl prin apelarea getControlComponent.
8.8.3. Prezentarea stream-urilor RTP cu RTPSocket

76
RTP este un protocol independent de protocolul de transport folosit.
Folosind RTPSocket, se pot recepiona stream-uri RTP din orice reea. Formatul
socket-ului RTP este proiectat s aib un canal pentru date i unul de control.
Fiecare canal are un stream de intrare i unul de ieire pentru a
transmite i a recepiona datele n/din reea.
SessionManager-ul se ateapt s primeasc pachete RTP individuale
de la RTPSocket. Utilizatorii sunt responsabili de trimiterea pachetelor
individuale spre RTPSocket.
Pentru a rula un stream RTP de la RTPSocket, trebuie transmis acest
socket la Manager.createPlayer pentru a construi Player-ul. Alternativ, se poate
construi un Player prin apelarea createPlayer(MediaLocator) i trecerea la
MediaLocator cu un nou protocol care este o variant a RTP, rtpraw. Spre
exemplu,
Manager.createPlayer(new MediaLocator("rtpraw://"));
Conform mecanismului de creere a unui Player JMF, Manger-ul va
ncerca s construiasc DataSource-ul:
<protocol package-prefix>.media.protocol.rtpraw.DataSource
Coninutul RTPSocket trebuie setat pe rtpraw. Manager-ul va incerca
s creeze un Player de tipul <content-prefix>.media.content.rptraw.Handler i
va seta RTPSocket-ul pe acesta.
Not: RTPSocket-ul creat la <protocol package-prefix>
.media.protocol.rtpraw.DataSource este implementarea proprie a acestuia.
Implementarea RTPSocket-ului este dependent de protocolul de transport
folosit. Clasa RTPSocket trebuie localizat la <protocol package-
prefix>.media.protocol.rtpraw.DataSource iar canalele de date i control
trebuie setate s arate ca i n Exemplul 8.18. Interfeele RTPControl pentru
RTPSocket pot fi utilizate pentru a aduga informaii dinamice manager-ului de
sesiune RTP.
Exemplul 8.18 implementeaz o sesiune RTP peste un Player UDP
care poate recepiona pachete RTP UDP i le poate transmite unui Player sau
unui manager de sesiune. Acest exemplu folosete interfeele definite n
javax.media.rtp.RTPSocket i clasele care aparin acesteia.

Exemplul 8.18. RTPSocketPlayer


import java.io.*;
import java.net.*;
import java.util.*;

import javax.media.*;
import javax.media.format.*;

77
import javax.media.protocol.*;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
import javax.media.rtp.rtcp.*;

public class RTPSocketPlayer implements ControllerListener {


// Aici trebuie introdusi parametrii sesiunii RTP

// Adresa sesiunii RTP (multicast,unicast sau broadcast)


String address = "224.144.251.245";

// Portul sesiunii RTP


int port = 49150;

// tipul datelor
String media = "audio";

RTPSocket rtpsocket = null;

RTPPushDataSource rtcpsource = null;

Player player;

// marimea maxima a bufferului UDP


private int maxsize = 2000;

UDPHandler rtp = null;


UDPHandler rtcp = null;

public RTPSocketPlayer() {
// creeaza RTPSocket
rtpsocket = new RTPSocket();
// seteaza tipul continutului :
// rtpraw/video pentru o sesiune video
// rtpraw/audio pentru o sesiune audio
String content = "rtpraw/" + media;
rtpsocket.setContentType(content);

// seteaza adresa sesiunii RTP si portul datelor RTP


rtp = new UDPHandler(address, port);

78
rtpsocket.setOutputStream(rtp);

// seteaza adresa sesiunii RTP si portul datelor RTCP


rtcp = new UDPHandler(address, port +1);

rtcpsource = rtpsocket.getControlChannel();

rtcpsource.setOutputStream(rtcp);
rtcpsource.setInputStream(rtcp);

// connect the RTP socket data source before


// creating the player
try {
rtpsocket.connect();
player = Manager.createPlayer(rtpsocket);
rtpsocket.start();
} catch (NoPlayerException e) {
System.err.println(e.getMessage());
e.printStackTrace();
return;
}
catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
return;
}

if (player != null) {
player.addControllerListener(this);
// send this player to out playerwindow
// playerWindow = new PlayerWindow(player);
}
}
public synchronized void controllerUpdate(ControllerEvent ce) {
if ((ce instanceof DeallocateEvent) ||
(ce instanceof ControllerErrorEvent)) {

// opreste handler-ul UDP


if (rtp != null) rtp.close();

if (rtcp != null) rtcp.close();


}

79
}

private DatagramSocket InitSocket(String sockaddress,


int sockport)
{
InetAddress addr = null;
DatagramSocket sock = null;

try {
addr = InetAddress.getByName(sockaddress);

if (addr.isMulticastAddress()) {
MulticastSocket msock;

msock = new MulticastSocket(sockport);

msock.joinGroup(addr);

sock = (DatagramSocket)msock;
}
else {
sock = new DatagramSocket(sockport,addr);
}

return sock;
}
catch (SocketException e) {
e.printStackTrace();
return null;
}
catch (UnknownHostException e) {
e.printStackTrace();
return null;
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}

public class UDPHandler extends Thread implements

80
PushSourceStream, OutputDataStream
{
DatagramSocket mysock;
DatagramPacket dp;
SourceTransferHandler outputHandler;
String myAddress;
int myport;
boolean closed = false;

// in constructor deschidem socketul si cream thread-ul


// UDPHandler

public UDPHandler(String haddress, int hport) {


myAddress = haddress;
myport = hport;
mysock = InitSocket(myAddress,myport);
setDaemon(true);
start();
}

// threadul principal primeste pachete RTP din retea si transfera


// aceste date handlerului de iesire a acestui stream

public void run() {


int len;

while(true) {
if (closed) {
cleanup();
return;
}
try {
do {
dp = new DatagramPacket( new byte[maxsize],
maxsize);

mysock.receive(dp);

if (closed){
cleanup();
return;
}

81
len = dp.getLength();
if (len > (maxsize >> 1)) maxsize = len << 1;
}
while (len >= dp.getData().length);
}catch (Exception e){
cleanup();
return;
}

if (outputHandler != null) {
outputHandler.transferData(this);
}
}
}

public void close() {


closed = true;
}

private void cleanup() {


mysock.close();
stop();
}

// metodele pentru PushSourceStream


public Object[] getControls() {
return new Object[0];
}

public Object getControl(String controlName) {


return null;
}

public ContentDescriptor getContentDescriptor() {


return null;
}

public long getContentLength() {


return SourceStream.LENGTH_UNKNOWN;
}

82
public boolean endOfStream() {
return false;
}

// metoda prin care datele sunt transferate din retea spre


// managerul de sesiune

public int read(byte buffer[],


int offset,
int length)
{
System.arraycopy(dp.getData(),
0,
buffer,
offset,
dp.getLength());

return dp.getData().length;
}

public int getMinimumTransferSize(){


return dp.getLength();
}

public void setTransferHandler(SourceTransferHandler


transferHandler)
{
this.outputHandler = transferHandler;
}

// metode pentru OutputDataStream folosite de managerul de


// sesiune pentru a transmite date in retea

public int write(byte[] buffer,


int offset,
int length)
{
InetAddress addr = null;

try {
addr = InetAddress.getByName(myAddress);
} catch (UnknownHostException e) {

83
e.printStackTrace();
}

DatagramPacket dp = new DatagramPacket( buffer,


length,
addr,
myport);
try {
mysock.send(dp);
} catch (IOException e){
e.printStackTrace();
}

return dp.getLength();
}
}

public static void main(String[] args) {


new RTPSocketPlayer();
}
}
8.9. Transmisia stream-urilor RTP
Pentru a transmite un stream RTP, trebuie folosit un Processor pentru
a obine o DataSource codat RTP. De asemenea trebuie construit un
SessionManager sau un DataSink pentru a controla transmisia.
Intrarea pentru Processor poate fi de la o surs stocat sau poate
proveni de la un dispozitiv de captur. Pentru datele stocate se folosete
MediaLocator pentru a identifica fiierul surs, cnd se creeaz Processor-ul.
Pentru datele capturate este necesar folosirea unei DataSource la intrarea
Processor-ului.
Exit dou 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 coninutul i pentru controlul transmisiei.

Dac se folosete un MediaLocator pentru a construi un DataSink


RTP, se poate transmite doar primul stream din DataSource. Dac se dorete
transmisia de stream-uri multiple ntr-o sesiune sau dac se dorete

84
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 reprezint datele ce


trebuie transmise;
configurat acest Processor pentru a transmite la ieire date
codate RTP;
considerat ieirea 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 pist.
Formatele pistelor se seteaz prin obinerea TrackControl pentru
fiecare pist i prin apelarea metodei setFormat. Un format specific RTP se
selecteaz prin setarea string-ului de codare ntr-un string de format RTP, cum
ar fi: AudioFormat.GSM_RTP. Processor-ul ncearc s ncarce un plug-in
care suport acest format. Dac nu este instalat un plug-in potrivit nseamn c
acel format RTP nu este suportat i o excepie UnSupportedFormatException
este aruncat. Formatul ieirii se seteaz cu metoda
setOutputContentDescriptor. Dac nu sunt necesare multiplexri speciale,
descriptorul ieirii poate fi setat pe ContentDescriptor.RAW. Stream-urile
audio i video nu trebuie intercalate (interleave). Dac pistele Processor-ului
sunt tipuri de date diferite, fiecare stream media este transmis ntr-o sesiune
RTP separat.
8.9.1.2. Obinerea datelor la ieirea Processor-ului
Odat ce formatul pistei Processor-ului a fost setat i Processor-ul a
ajuns n starea Realized, se poate obine DataSource-ul de la ieirea Processor-
ului. Pentru aceasta trebuie apelat metoda getDataOutput. DataSource-ul
returnat poate fi de tip PushBufferDataSource sau PullBufferDataSource, n
funcie de sursa datelor.
DataSource-ul obinut este conectat la SessionManager folosind
metoda createSendStream. Managerul de sesiune trebuie s fie iniializat nainte
de a creea stream-ul de transmisie.
Dac DataSource-ul conine mai multe SourceStreams, fiecare
SourceStream este transmis ca un stream RTP separat, n aceeai sesiune sau n
sesiuni diferite. Dac DataSource-ul conine att audio ct i 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 aceeai sesiune sau n sesiuni diferite.
8.9.1.3. Controlul ntrzierii pachetelor

85
ntrzierea pachetului, cunoscut i sub numele de interval de
mpachetare, este timpul petrecut de fiecare pachet RTP de-a lungul transmisiei
lui prin reea. Intervalul de mpachetare determin ntrzierea minim pentru
transmisia de la un capt la altul (end-to-end delay). Pachetele mai mari
micoreaz header-ul dar mresc ntrzierile i fac pierderea de pachete mai
sesizabil.
Un receptor ar trebui s accepte pachete reprezentnd ntre 0 i 200 ms
de date audio. Aceast restricie permite o mrime rezonabil a buffer-ului
pentru receptor. Fiecare codec de mpachetare are un interval de mpachetare
implicit, potrivit pentru acel tip de codare.
Dac codec-ul permite modificarea acestui interval, el export 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. Mrimea fiecrui pachet este limitat de MTU-ul
(Maximum Transmission Unit) reelei. Acest parametru se poate seta prin
metoda setPacketSize care aparine de PacketSizeControl.
8.9.2. Transmisia datelor RTP folosind DataSink
Cea mai simpl metod de a transmite date RTP este s construim un
DataSink RTP folosind metoda Manager.createDataSink. Parametrii sunt
DataSource-ul de la ieirea Processor-ului i un MediaLocator care descrie
sesiunea RTP. MediaLocator-ul conine addresa i portul sesiunii RTP.
Pentru a controla transmisia, trebuie apelate start i stop asupra
DataSink-ului. Doar primul stream din DataSource este transmis.
n Exemplul 8.19, se captureaz date audio, dup 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) {
di = (CaptureDeviceInfo) devices.elementAt( 0);
}
else {
// daca nu gaseste dispozitivul de captura - exit

86
System.exit(-1);
}

// creaza un Processor sau exit daca nu il poate crea


try {
Processor p = Manager.createProcessor(di.getLocator());
} catch (IOException e) {
System.exit(-1);
} catch (NoProcessorException e) {
System.exit(-1);
}

// 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++) {
if (!encodingOk && track[i] instanceof FormatControl) {
if (((FormatControl)track[i]).
setFormat( new AudioFormat(AudioFormat.GSM_RTP,
8000, 8, 1)) == null) {

track[i].setEnabled(false);
}
else {
encodingOk = true;
}
} else {
// pista aceasta nu este format gsm, asa ca o invalidam
track[i].setEnabled(false);
}
}

87
if (encodingOk) {
processor.realize();
// se blocheaza pana la realized
// obtine datasource-ul de iesire al processorului
// daca da eroare - exit
DataSource ds = null;

try {
ds = processor.getDataOutput();
} catch (NotRealizedError e) {
System.exit(-1);
}

// creaza datasnik RTP si transmite stream-ul audio


try {
String url= "rtp://224.144.251.104:49150/audio/1";
MediaLocator m = new MediaLocator(url);
DataSink d = Manager.createDataSink(ds, m);
d.open();
d.start();
} catch (Exception e) {
System.exit(-1);
}
}
8.9.3. Transmisia datelor RTP folosind SessionManager
Procesul standard pentru transmisia datelor RTP folosind un manager
de sesiune este:
se creeaz un Processor i se setez formatul fiecrei piste
ntr-un format specific RTP;
se obine DataSource-ul de la ieirea Processor-ului;
se apeleaz metoda createSendStream asupra unui
SessionManager care a fost creat i iniializat n prealabil. Ca
parametrii se transmit DataSource-ul i un index de stream. Managerul
de sesiune creaz un SendStream pentru SourceStream-ul specificat;
se pornete managerul de sesiune prin apelarea
SessionManager startSession;
se controleaz 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

88
nainte ca managerul de sesiune s transmit date, trebuie s cunoasc
locaia datelor ce urmeaz a fi transmise. Cnd se construiete un nou
SendStream, acesta trebuie s cunoasc DataSource-ul. Din moment ce un
DataSink poate conine stream-uri multiple, trebuie specificat i indexul
stream-ului ce urmeaz a fi transmis n aceast sesiune. Se pot creea stream-uri
de transmisie multiple prin transmiterea unor DataSource diferite prin apelarea
createSendStream sau prin specificarea mai multor indeci de stream.
Managerul de sesiune interogheaz formatul SourceStream-ului pentru
a determina dac are un tip de date nregistrat pentru acest format. Dac datele
nu sunt un format RTP sau dac tipul datelor nu poate fi localizat n formatul
RTP, o excepie UnSupportedFormatException este aruncat.
Multe scenarii de folosire a protocolului RTP implic transmisia unui
stream n mai multe sesiuni RTP sau codarea unui stream n formate multiple i
transmisia lui n mai multe sesiuni RTP. Cnd un stream codat ntr-un singur
format trebuie transmis m sesiuni RTP multiple, trebuie clonat DataSource-ul
de la ieirea Processor-ului de unde se captureaz datele. Aceasta se realizeaz
prin creearea unei DataSource clonabile prin intermediul Manager-ului i 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 i fiecare stream poate fi transmis ntr-o sesiune RTP.
Dac se dorete mixarea stream-urilor multiple de acelai tip (cum ar
fi audio) ntr-un singur stream, trebuie folosit un mixer RTP. Dac stream-urile
ce trebuie mixate provin de la DataSource-uri multiple, trebuie creat un
MergingDataSource din DataSource-ele separate i transmise spre
SessionManager, care creeaz stream-ul.
8.9.3.2. Controlul unui stream de transmisie
Pentru a controla SendStream-ul se folosesc metodele RTPStream
start i stop. La pornirea unui SendStream ncepe transmisia datelor n reea. La
oprirea acestuia transmisia este oprit. Pentru a ncepe o transmisie RTP, fiecare
SendStream trebuie pornit.
Dac 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 reea.
8.9.3.3. Transmisia datelor audio capturate ntr-o singur sesiune
Exemplul 8.20. arat cum se realizeaz captura i transmisia ntr-o
sesiune RTP a unui stream audio mono.

Exemplul 8.20. Transmisia datelor audio capturate ntr-o


singur sesiune

89
// 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) {
di = (CaptureDeviceInfo) devices.elementAt( 0);
}
else {
// daca nu gasim disp. de captura - exit
System.exit(-1);
}
// creeaza un processor pt. dispozitivul de captura
// si iese daca nu il poate crea
try {
Processor p = Manager.createProcessor(di.getLocator());
} catch (IOException e) {
System.exit(-1);
} catch (NoProcessorException e) {
System.exit(-1);
}

// 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++) {
if (!encodingOk && track[i] instanceof FormatControl) {

if (((FormatControl)track[i]).

90
setFormat( new AudioFormat(AudioFormat.ULAW_RTP,
8000, 8, 1)) == null) {

track[i].setEnabled(false);
}
else {
encodingOk = true;
}
}
else {
// pista aceasta nu poate fi setata pe gsm, asa ca o invalidam
track[i].setEnabled(false);
}
}
processor.realize();

// il blocheaza pana ajunge la realized


// obtinem datasource-ul de iesire si iese daca da eroare

DataSource ds = null;
try {
ds = processor.getDataOutput();
} catch (NotRealizedError e){
System.exit(-1);
}

// 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 {
rtpsm.createSendStream(ds, 0);
} catch (IOException e) {
e.printStackTrace();
} catch( UnsupportedFormatException e) {
e.printStackTrace();

91
}
8.9.3.4. Transmisia datelor audio capturate n sesiuni multiple
Exemplul 8.21. ilustreaz codarea datelor audio i 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) {
di = (CaptureDeviceInfo) devices.elementAt( 0);
}
else {
// daca nu detecteaza disp. de captura - exit
System.exit(-1);
}

// creeaza un processor pentru disp. de captura


// sau exit daca nu il poate crea
try {
Processor p = Manager.createProcessor(di.getLocator());
} catch (IOException e) {
System.exit(-1);
} catch (NoProcessorException e) {
System.exit(-1);
}

// configureaza processorul
processor.configure();

// il blocheaza pana ajunge configured

processor.setContentDescriptor(
new ContentDescriptor( ContentDescriptor.RAW));

92
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++) {


if (!encodingOk && track[i] instanceof FormatControl) {

if (((FormatControl)track[i]).
setFormat( new AudioFormat(AudioFormat.GSM_RTP,
8000, 8, 1)) == null) {

track[i].setEnabled(false);
}
else {
encodingOk = true;
}
}
else {
// pista nu poate prelucra gsm, asa ca o invalidam
track[i].setEnabled(false);
}
}

// acum stim unde putem trimite date gsm si unde nu


// trece processor in starea realize

if (encodingOk) {
processor.realize();

// obtine datasource-ul de la iesirea processor-ului si


// iese din program daca da eroare

DataSource origDataSource = null;


try {
origDataSource = processor.getDataOutput();
} catch (NotRealizedError e) {
System.exit(-1);
}

93
// 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 {
rtpsm1.createSendStream(cloneableDataSource, //
Datasource 1 0);

} catch (IOException e) {
e.printStackTrace();
} catch( UnsupportedFormatException e) {
e.printStackTrace();
}

try {
cloneableDataSource.connect();
cloneableDataSource.start();
} catch (IOException e) {
e.printStackTrace();
}

// creeaza al doilea RTPSessionMgr si preia controlul asupra

94
// datasource-ului clonat
if (clonedDataSource != null) {
SessionManager rtpsm2
= new com.sun.media.rtp.RTPSessionMgr();

// rtpsm2.initSession(...);
// rtpsm2.startSession(...);

try {
rtpsm2.createSendStream(clonedDataSource,0);
} catch (IOException e) {
e.printStackTrace();
} catch( UnsupportedFormatException e) {
e.printStackTrace();
}
}
}
else {
// procedura de codare gsm a dat gres.
// dealocam si inchidem processorul

processor.deallocate();
processor.close();
}
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
variant a RTP, Ratibor. Managerul ncearc s construiasc un DataSink
din:

<protocol package-prefix>.media.datasink.rtpraw.Handler

Acest manager de sesiune pregtete pachete individuale RTP pentru a


fi pregtite s fie transmise n reea i 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

95
definete o implemetare implicit a RTPSocket. Implementarea RTPSocket este
dependent de protocolul din stratul transport pe care l folosim.
Clasa RTPSocket trebuie localizat la <protocol-prefix>.media.
protocol.rtpraw.DataSource. Utilizatorul este responsabil pentru transmiterea
de pachete RTP n reea.
Exemplul 8.22 ilustreaz 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) {
di = (CaptureDeviceInfo) devices.elementAt( 0);
}
else {
// daca nu gaseste disp. de captura - exit
System.exit(-1);
}

// creaza un processor pentru disp. de captura si paraseste


// aplicatia daca nu il poate creea

try {
processor = Manager.createProcessor(di.getLocator());
} catch (IOException e) {
System.exit(-1);
} catch (NoProcessorException e) {
System.exit(-1);
}
// configureaza processor-ul
processor.configure();

// se blocheaza pana ajunge configured

96
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++) {
if (!encodingOk && track[i] instanceof FormatControl) {

if (((FormatControl)track[i]).
setFormat( new AudioFormat(AudioFormat.GSM_RTP,
8000, 8, 1)) == null) {

track[i].setEnabled(false);
}
else {
encodingOk = true;
}
}
else {
// aceasta pista nu poate fi setata pe gsm, asa ca
// trebuie invalidata
track[i].setEnabled(false);
}
}

// processor-ul trece in starea realize


if (encodingOk) {
processor.realize();
// se blocheaza pana ajunge realized
// obtine datasource-ul de iesire al processor-ului
DataSource ds = null;
try {
ds = processor.getDataOutput();
} catch (NotRealizedError e) {
System.exit(-1);
}

// managerul primeste datasource-ul si creeaza un


// datasink RTP

97
try {
MediaLocator m = new MediaLocator("rtpraw://");
// managerul cauta pentru un datasink in
// <protocol.prefix>.media.protocol.rtpraw.DataSink
// datasink-ul va crea un RTPSocket la
// <protocol.prefix>.media.protocol.rtpraw.DataSource
DataSink d = Manager.createDataSink(ds, m);

d.open();
d.start();
} catch (Exception e) {
System.exit(-1);
}
}
8.10. Importul i exportul stream-urilor RTP
Multe aplicaii trebuie s permit scrierea i citirea stream-urilor RTP.
Spre exemplu, aplicaiile de conferint trebuie s poat nregistra conferina i
s o poat rula mai trziu. Sau, aplicaiile de telefonie trebuie s poat transmite
stream-uri audio stocate ca mesaje de ntmpinare.
Stream-urile RTP recepionate prin reea pot fi salvate ntr-un fiier
folosind un DataSink RTP. Similar, se pot citi fiiere salvate, dup care pot fi
prezentate local sau pot fi transmise n reea.
8.10.1. Citirea stream-urilor RTP dintr-un fiier
Pentru a citi date dintr-un fiier i a le prezenta sau a le transmite,
trebuie folosit un MediaLocator pentru a construi direct un Processor. Tipul
fiierelor care pot fi folosite la transmisia RTP depinde de plug-in-urile de
codare care sunt disponibile pentru a coda i mpacheta datele ntr-un format
specific RTP.

Exemplul 8.23. Citirea stream-urilor RTP dintr-un fiier


// creeaza un Processor pentru fisierul selectat
// daca nu poate fi creat - exit
try {
String url= "file:/home/ex/ex823.au";
processor = Manager.createProcessor( new MediaLocator(url));
} catch (IOException e) {
System.exit(-1);
} catch (NoProcessorException e) {
System.exit(-1);

98
}

// configureaza processorul
processor.configure();

// il blocheaza pana ajunge configured

TrackControl track[] = processor.getTrackControls();


boolean encodingOk = false;

// parcurge pistele si incearca sa programeze una dintre ele


// sa prelucreze date format ulaw
for (int i = 0; i < track.length; i++) {
if (!encodingOk && track[i] instanceof FormatControl) {

if (((FormatControl)track[i]).
setFormat( new AudioFormat(AudioFormat.ULAW_RTP,
8000, 8, 1)) == null) {

track[i].setEnabled(false);
}
else {
encodingOk = true;
}
}
else {
// nu se poate seta pista pe ulaw, asa ca o invalidam
track[i].setEnabled(false);
}
}

// processor-ul trece in starea realize

if (encodingOk) {
processor.realize();

// il blocam pana ajunge realized


// obtine datasource-ul de la iesirea processor-ului
// si parasim daca da eroare
DataSource ds = null;

try {

99
ds = processor.getDataOutput();
} catch (NotRealizedError e) {
System.exit(-1);
}
// transmite datasource-ul spre manager pentru a creea
// un datasink RTP
// datasink RTP va transmite audio multicast

try {
String url= "rtp://224.144.251.104:49150/audio/1";
MediaLocator m = new MediaLocator(url);
DataSink d = Manager.createDataSink(ds, m);

d.open();
d.start();
} catch (Exception e) {
System.exit(-1);
}
}
8.10.2. Exportul stream-urilor RTP
Stream-urile RTP recepionate din reea pot fi stocate sau pot fi
prezentate. Pentru a scrie datele ntr-un fiier, trebuie obinut DataSource-ul din
ReceiveStream. Acesta trebuie folosit la scrierea unui fiier cu un DataSink,
prin intermediul Manager-ului.
Dac se dorete decodarea datelor nainte de stocare, trebuie folosit
DataSource-ul obinut din ReceiveStream pentru a construi un Processor. Apoi:

stream-ul este obinut din NewReceiveStreamEvent;


DataSource-ul este obinut din ReceiveStream;
DataSource-ul este trecut pasat Manager.createDataSink
mpreun cu un MediaLocator care identific fiierul n care se dorete
stocarea datelor.

Urmtorul exemplu arat cum se execut aceste operaii asupra unei


singure piste. Pentru a scrie un fiier care conine att date audio ct i video,
trebuie obinute stream-urile audio i video de la manageri de sesiune diferii i
creat o DataSource care s le combine. Apoi, DataSource-ul obinut este
transmis la Manager.createDataSink.

100
Exemplul 8.24. Scrierea unui stream RTP ntr-un fiier
public void update(ReceiveStreamEvent event) {
// gaseste managerul de sesiune pentru acest eveniment
SessionManager source = (SessionManager)event.getSource();

// creeaza un datasink daca un nou ReceiveStream este detectat


if (event instanceof NewReceiveStreamEvent) {
String cname = "Java Media Player";
ReceiveStream stream = null;

try {
// obtine ReceiveStream
stream =((NewReceiveStreamEvent)event)
.getReceiveStream();

Participant part = stream.getParticipant();

// obtine datasource ReceiveStream


DataSource dsource = stream.getDataSource();

// trimite aceasta datasource spre un datasink


MediaLocator f = new MediaLocator("file://foo.au");

Manager.createDataSink(dsource, f);
} catch (Exception e) {
System.err.println("Exceptie newReceiveStreamEvent "
+ e.getMessage());
return;
}
}
}

n Capitolul 10 este prezentat o aplicaie care implementeaz un


sistem de supraveghere. Pentru partea de captur video s-au folosit o serie de
elemente din JMF.

8.11. Bibliografie

[1]. www.java.sun.com
[2]. www.billday.com

101
[3]. www.codesourceonline.com
[4]. www.tutorials.com

102

S-ar putea să vă placă și