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 controlata automat sau poate afisa o componenta tip panou de control care sa permita utilizatorului sa o controleze interactiv. Daca se doreste rul 434d32e area mai multor streamuri media, trebuie folosit câte un Player separat pentru fiecare dintre ele. Pentru a le rula sincronizat, se poate folosi unul dintre Playere pentru a controla operatiile efectuate de celelalte Playere.
Processor-ul permite controlul asupra proceselor ce au loc înainte de realizarea prezentarii. Indiferent daca se foloseste un Player simplu sau unul mai avansat pentru prezentare, se foloseste aceeasi metoda pentru a administra rularea.
Bean-ul MediaPlayer este un bean Java care încapsuleaza un Player JMF ce permite prezentarea datelor multimedia dintr-un applet sau o aplicatie. El construieste automat un nou Player când un stream diferit de date este selectat, ceea ce usureaza operatia de rulare a unei serii de clip-uri media sau permite utilizatorului sa selecteze care clip anume sa fie rulat.
8.4.1. Controlul unui Player
Pentru a prezenta datele, sunt necesare construirea unui Player pentru stream-ul de date, configurarea si pregatirea Player-ului, si în final pornirea acestuia pentru începerea rularii.
8.4.1.1. Crearea unui Player
Un Player se creeaza în mod indirect din Manager. Pentru a afisa Player-ul, trebuie create si afisate componentele necesare controlarii.
Când este necesara creearea unui nou Player, este solicitat acest lucru de la Manager prin apelarea metodei createPlayer sau createProcessor.
Manager-ul foloseste URL-ul sau MediaLocator-ul care trebuie specificat pentru creearea Player-ului. Un URL poate fi construit cu succes doar daca URLStreamHandler-ul corespondent este instalat. Asupra MediaLocator nu se aplica aceasta restrictie.
Multe dintre metodele care pot fi apelate asupra unui Player necesita ca acesta sa fie în starea Realized. O metoda care garanteaza ca Player-ul este în starea Realized când se apleleaza aceste metode este folosirea metodei createRealizedPlayer la construirea Player-ului. Aceasta metoda este o cale convenabila de creeare si trecere a unui Player în starea Realized într-un singur pas. Când aceasta metoda este apelata, se blocheaza pâna când Player-ul este Realized. Manager-ul are o metoda echivalenta, si anume createRealizeProcessor pentru construirea unui Processor în starea Realized.
NOTĂ: Trebuie tinut seama ca blocarea unui Player sau Processor pâna ajunge în starea Realized poate produce rezultate nesatisfacatoare. Spre exemplu, daca metoda createRealizedPlayer este apelata într-un applet, Applet.start si Applet.stop nu vor putea întrerupe procesul de constructie al Player-ului sau Processor-ului.
Un Processor poate fi creat si folosind ProcessorModel. Acesta defineste necesarul de resurse pentru intrare si iesire iar Manager-ul face tot posibilul pentru a îndeplini cererea de resurse. Pentru a construi un Processor folosind modelul ProcessorModel, trebuie apelata metoda Manager.createRealizedProcessor.
Exemplul urmator creaza un Processor în starea Realized care poate produce piste audio stereo codate IMA4 cu o rata de 44.1 kHz, pe 16 biti.
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 specifica un URL sursa în acest exemplu, Manager-ul cauta un dispozitiv de captura audio si creeaza un Processor care poate coda ceea ce se captureaza cu parametrii doriti.
8.4.1.2. Afisarea componentelor interfetei de prezentare
Un Player are de obicei doua tipuri de componente pentru interfata utilizator, o componenta vizuala si o componenta pentru panoul de control. Unele implementari ale Player-elor pot afisa componente aditionale, cum ar fi controlul volumului sau bara pentru progresul operatiunii de download
8.4.1.2.1 Afisarea unei componente vizuale
O componenta vizuala apare în cazul în care un Player prezinta vizual datele multimedia, daca are aceste date. Chiar si un Player audio poate avea componente vizuale, cum ar fi afisarea formelor de unda sau afisarea unor caractere animate.
Pentru a afisa o componenta vizuala a unui Player trebuie urmati urmatorii doi pasi:
trebuie încarcata componenta prin apelarea getVisualComponent;
trebuie adaugata în spatiul de prezentare al applet-ului sau în fereastra aplicatiei.
Este posibil accesul la proprietatile de afisare ale Player-ului (cum ar fi coordonatele x si y) prin componentele sale vizuale.
8.4.1.2.2 Afisarea unei componente de control
Un Player are de obicei un panou de control care permite utilizatorului sa controleze prezentarea. Spre exemplu, un Player poate fi asociat cu un set de butoane care sa îi permita sa porneasca, opreasca, întrerupa un stream de date
Fiecare Player contine implicit un panou de control. Pentru a afisa acest panou de control trebuie urmati urmatorii doi pasi:
apelata metoda getControlPanelComponent pentru a accesa componentele;
adaugate componentele returnate de metoda anterioara spatiului de prezentare al applet-ului sau ferestrei de aplicatie.
Daca se prefera definirea unei interfete utilizator, se pot implementa componente GUI (Graphic User Interface) si apela metodele Player corespondente ca raspuns la actiunile utilizatorului. Daca se înregistreaza aceste componente ca si ControlListeners, componentele se pot adapta când starea Player-ului se modifica.
8.4.1.2.3. Afisarea unei componente de tip GainControl
Implementarile Player-ului care suporta ajustari ale câstigului audio folosesc interfata GainControl. Aceasta contine metode de ajustare a volumului audio, cum ar fi setLevel si setMute. Pentru a afisa o componenta GainControl daca Player-ul permite, trebuie urmati urmatorii pasi:
trebuie apelata metoda getGainControl pentru a obtine GainControl de la Player. Daca acesta returneaza un rezultat nul, înseamna ca nu suporta interfata GainControl;
trebuie apelata getControlComponent asupra componentei GainControl returnate de Player;
se adauga componenta rezultata în spatiul de prezentare al aplicatiei sau în fereastra aplicatiei.
8.4.1.2.4. Afisarea unor componente de control utilizator
Multe Playere au alte proprietati care pot fi administrate de catre utilizator. Spre exemplu, un Player video ar putea permite utilizatorului sa ajusteze luminozitatea si contrastul, care nu sunt administrare de interfata Player. Pentru a afla ce controale utilizator suporta un Player trebuie apelata metoda getControls.
Spre exemplu, se poate apela getControls pentru a determina daca un Player suporta interfata CachingControl.
Exemplul 8.2. Folosirea metodei getControls pentru a afla ce controale sunt suportate |
|
for (int i = 0; i < controls.length; i++) |
|
8.4.1.2.5. Afisarea unei componente de progres
Interfata CachingControl este un tip mai special de Control implementata de Playere care pot raporta progresul download-ului. Ea permite afisarea unei bare de progres care este automat modificata pe masura ce download-ul progreseaza. Pentru a introduce aceasta bara de progres într-un applet trebuie urmati urmatorii pasi:
trebuie implementata interfata ControllerListener si ascultat pentru evenimente CachingControlEvents într-un controllerUpdate;
prima data când se primeste un CachingControlEvent:
se apeleaza getCachingControl asupra evenimentului
se apeleaza getProgressBar asupra CachingControl pentru a accesa componenta bara de control
se adauga aceasta componenta la spatiul de prezentare a applet-ului
De fiecare data când se primeste un CachingControlEvent, trebuie verificat daca download-ul este terminat. Când getContentProgress returneaza aceeasi valoare ca si getContentLength, bara de progres trebuie înlaturata.
Player-ul genereaza un eveniment CachingControlEvent ori de câte ori bara de progres trebuie sa fie modificata. Daca se implementeaza o bara de progress utilizator, trebuie "ascultat" pentru acest eveniment.
8.4.1.3. Setarea ratei de rulare
Rata de rulare a unui Player determina cum se modifica timpul media relativ la timpul de baza. Ea defineste câte unitati de timp avanseaza un Player relativ la o unitate de timp de baza. Rata de rulare poate fi interpretata ca un factor scala temporala. Spre exemplu, o rata de 2.0 indica ca timpul media trece de doua ori mai rapid decât timpul de baza când Player-ul este pornit.
În teorie, rata de rulare a unui Player poate fi orice numar real, valorile negative fiind interpretate ca faptul ca rularea se face invers. Une tipuri de formate media au relatii între cadre care fac imposibila sau nepractica rularea lor înapoi sau rularea la rate non-standard
Pentru a seta rata de rulare, trebuie apelata metoda setRate si setat un parametru care este de tip float. Când setRate este apelata, metoda returneaza rata care este setata. Player-ele pot garanta doar o rata de rulare de 1.0.
8.4.1.4. Setarea pozitiei de start
Setarea timpului unui Player este echivalenta cu setarea unei pozitii de citire într-un stream multimedia. Pentru o sursa de date cum este un fisier, timpul este delimitat: timpul maxim este definit ca sfârsitul stream-ului media.
Pentru a seta timpul trebuie apelata metoda setMediaTime iar rezultatul trebuie pasat într-un obiect de tip Time. Valoarea acestui obiect reprezinta timpul care pe vrem sa îl setam.
Unele Playere permit cautarea unui anumit cadru într-o secventa video. Aceasta permite utilizatorului sa seteze pozitia de start la începutul unui cadru anume fara sa fie necesara specificarea timpului care corespunde acelei pozitii. Player-ele care suporta pozitionarea la nivel de cadru implementeaza FramePositioningControl.
Pentru a seta pozitionarea la nivelul unui cadru, trebuie apelata metoda de cautare FramePositioningControl. Când se cauta un cadru, timpul corespondent Player-ului este setat la o valoare care corespunde cu începutul acelui cadru. În acelasi timp, un eveniment MediaTimeSetEvent este generat.
Unele Playere pot face conversia între timp si pozitia cadrelor. Pentru aceasta trebuie folosite metodele FramePositioningControl mapToTime si mapTimetoFrame pentru a accesa aceste informatii, daca sunt dispoibile.
8.4.1.5. Pregatirea startului
Majoritatea Player-elor nu pot fi pornite instant. Înainte de a putea realiza pornirea unui Player, trebuie îndeplinite anumite conditii hardware si software. Spre exemplu, daca un Player nu a mai fost pornit niciodata, este necesara alocarea unor buffere în memorie pentru stocarea datelor. Sau, daca datele se afla undeva în retea, Player-ul trebuie sa stabileasca conexiunea în retea înainte de a aduce datele. Chiar si daca Player-ul a mai fost pornit, buffer-ele ar putea contine date care nu corespund cu datele corespondente pozitiei curente.
JMF împarte procesul de pregatire de pornire a unui Player în doua faze, Realizing si Prefetching. Daca aceste faze se executa înainte de pornirea Player-ului, se minimizeaza timpul necesar Player-ului pentru începerea prezentarii când metoda start este apelata. Implementarea interfetei ControlListener permite controlul exact al timpului de executie al acestor operatii.
Cea de a treia faza de pregatire a startului este numita Configuring. În timpul acestei faze, pot fi selectate anumite optiuni pentru a controla felul în care Processor-ul manipuleaza datele.
Pentru a trece Player-ul în starea Realizing si pentru a începe procesul de realizare se apeleaza metoda realize. Pentru a trece Player-ul în starea Prefetching si pentru a initia procesul de prefetching, se apeleaza metoda prefetch. Aceste metode sunt asincrone si rezultatul este imediat. Când Player-ul a completat o operatie ceruta, el genereaza un evenimet RealizeCompleteEvent sau PrefetchCompleteEvent.
Un Player în starea Prefetched este pregatit de start si latenta sa de pornire nu poate fi redusa mai mult.
Foarte important este faptul ca un Player în starea Prefetched tine blocate unele dintre resursele sistemului. Deoarece unele dispozitive, cum sunt placile de sunet, pot fi utilizate de un singur program într-un moment dat, un Player aflat în starea Prefetched poate împiedica alte Playere sa porneasca.
Pentru a determina cât de mult timp este necesar pentru a porni un Player, trebuie apelata metoda getStartLatency. Pentru Player-ele care au o latenta de start variabila, apelarea metodei getStartLatency returneaza latenta de start maxima posibila. Pentru unele tipuri de date, getStartLatency poate returna LATENCY_UNKNOWN.
Latenta de pornire raportata prin apelarea metodei getStartLatency poate diferi în functie de starea curenta a Player-ului. Spre exemplu, dupa o operatie de prefetch, valoarea returnata este de obicei mai mica.
8.4.1.6. Pornirea si oprirea prezentarii
Interfetele Clock si Player definesc metodele necesare pentru pornirea si oprirea prezentarii.
Pentru pornirea prezentarii se apeleaza metoda start. Aceasta spune Player-ului sa pornesca prezentarea cât mai repede posibil. Daca este necesar, pregateste Player-ul prin efectuarea operatiilor de realize si prefetch. Daca metoda start este apelata asupra unui Player deja pornit, singurul rezultat este faptul ca este generat un eveniment StartEvent.
Pentru realizarea sincronizarii interfata Clock defineste metoda syncStart.
Pentru a porni un Player dintr-un punct specific al stream-ului de date trebuie urmati urmatorii doi pasi:
trebuie specificat punctul exact din stream-ul de date de unde se doreste pornirea. Pentru aceasta se apeleaza metoda setMediaTime;
se apeleaza metoda start asupra Player-ului.
Exista patru situatii în care prezentarea se va opri:
când este apelata metoda stop;
când este atins timpul de stop specificat;
când nu mai sunt date de prezentat;
când datele sunt receptionate prea încet pentru o rulare acceptabila.
Când un Player este oprit, timpul sau corespondent este oprit daca sursa datelor poate fi controlata. Daca el prezinta date care sosesc într-un flux continuu (spre exemplu de la o camera video), este posibil sa nu se poata opri timpul corespondent
Când un Player care a fost oprit este repornit, daca timpul corespondent a fost "înghetat", prezentarea continua din locul unde a fost oprita. Daca timpul corespondent nu a putut fi "înghetat", se primesc datele în continuare si rularea continua cu datele proasptat receptionate.
Pentru a opri imediat un Player, trebuie apelata metoda stop. Daca se apeleaza metoda stop asupra unui Player, singurul rezultat este ca un eveniment StopByRequestEvent este generat.
Pentru a opri o prezentare la un moment dat, trebuie apelata metoda setStopTime. Player-ul se opreste în momentul în care timpul lui corespondent ajunge la timpul de stop specificat. Daca rata Player-ului este pozitiva, acesta se opreste când timpul corespondent devine mai mare sau egal decât timpul de oprire. Daca rata este negativa, Player-ul se opreste când timpul corespondent devine mai mic sau egal decât timpul de oprire. Player-ul se opreste imediat daca timpul sau curent a depasit deja timpul de stop specificat.
Întotdeauna se poate apela metoda setStopTime asupra unui Player oprit. Oricum, timpul de stop se poate seta doar asupra unui Player pornit. Daca exista deja un timp de stop, metoda setStopTime "arunca" o eroare.
Pentru a afla timpul de stop setat, trebuie apelata metoda getStopTime. Daca timpul de stop nu este programat, getStopTime returneaza Clock.RESET. Pentru a retrage timpul de stop si a permite Player-ului sa continue pâna ce ajunge la sfârsitul prezentarii, se foloseste setStopTime(Clock.RESET).
8.4.1.7. Eliberarea resurselor ocupate de Player
Metoda deallocate informeaza Player-ul ca trebuie sa elibereze toate resursele pe care le foloseste în exclusivitate si sa minimizeze accesul la resursele neexclusive. Managementul memoriei si dimensiunea bufferelor alocate unui Player nu sunt specificate, motiv pentru care majoritatea Player-elor aloca buffere care sunt mari raportate la bufferele standard din JAVA. Un Player bine implementat elibereaza maximum de memorie posibil când metoda deallocate este apelata.
Metoda deallocate poate fi apelata doar asupra unui Player oprit. Pentru a evita erorile ClockStartedErrors, trebuie apelata metoda stop înainte de apelarea metodei deallocate. Daca se apeleaza deallocate asupra unui Player aflat în starea Prefetching sau Prefetched, el revine în starea Realized. Daca se apeleaza asupra unui Player aflat în starea Realizing, Player-ul genereaza un eveniment DeallocateEvent si revine în starea Unrealized.
Când se doreste terminarea lucrului cu un Player, trebuie apelata metoda close. Aceasta indica faptul ca Controller-ul nu va mai fi folosit si se poate închide automat. Prin apelarea close se elibereaza toate resursele care au fost folosite de Controller si întrerupe toate activitatile. Tot atunci se genereaza 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 informatii despre parametrii lui curenti, incluzând aici rata de rulare, timpul corespondent si durata.
8.4.1.8.1. Obtinerea ratei de rulare
Pentru a obtine rata de rulare trebuie apelata metoda getRate. Rezultatul este rata de rulare ca o valoare de tip float.
8.4.1.8.2. Obtinerea timpului corespondent
Pentru a obtine timpul corespondent curent, trebuie apelata metoda getMediaTime. Rezultatul este sub forma unui obiect de tipul Time. Daca Player-ul nu prezinta datele în acel moment, acesta este punctul de unde va începe prezentarea.
Trebuie notat faptul ca corespondenta între timpul corespondent unui Player si cadre nu este unu la unu. Fiecare cadru este prezentat pentru o anumita perioada de timp, iar timpul corespondent continua sa avanseze în timpul acelei perioade.
Sa luam exemplul în care un Player prezinta fiecare cadru pentru 5 secunde, caz în care rata cadrelor frame rate) este de 0.2 cadre/secunda. Figura 8.18 ilustreaza aceasta situatie.
Figura 8.18. Durata cadrelor în raport cu timpul corespondent unui Player
Daca Player-ul este pornit la momentul 0.0, în timpul în care primul cadru este afisat, timpul corespondent avanseaza de la 0.0 la 5.0. Daca se porneste la momentul 2.0, primul cadru este afisat pentru 3 secunde, pâna când momentul 5.0 este atins.
8.4.1.8.3. Obtinerea timpului de baza
Obtinerea timpului de baza se face prin apelarea metodei getTime astfel:
myCurrentTBTime = player1.getTimeBase().getTime();
Când un Player ruleaza, se poate afla timpul de baza care corespunde unui anumit timp media prin apelarea mapToTimeBase.
8.4.1.8.4. Obtinerea duratei unui stream de date
Din moment ce programele trebuie sa cunoasca cât timp va rula un anumit stream, toate Controller-ele implementeaza interfata Duration. Aceasta interfata defineste o singura metoda, si anume getDuration. Durata reprezinta lungimea timpului necesar rularii, presupunându-se ca este rulat la rata implicita, si anume 1.0 cadre/secunda. Durata unui stream este accesibila doar prin intermediul unui Player
Daca durata nu poate fi determinata când metoda getDuration este apelata, este returnat DURATION_UNKNOWN. Aceasta situatie poate aparea când Player-ul nu a ajuns într-o stare care sa permita accesul la durata stream-ului. Mai târziu, durata poate fi disponibila si o apelare a aceleiasi metode poate returna valoarea dorita. Daca sursa de date nu are o durata definita, cum este în cazul broadcast-ului în direct, metoda getDuration returneaza valoarea DURATION_UNBOUNDED.
8.4.1.9. Tratarea evenimentelor
Interfata ControllerListener este o interfata asincrona pentru tratarea evenimentelor generate de un obiect de tip Controller. Folosirea acestei interfete permite utilizatorului sa administreze timpul necesar unor operatii potentiale consumatoare de timp (spre exemplu, la un Player, operatia de prefetching).
8.4.1.9.1. Implementarea interfetei ControllerListener
Pentru a implementa o interfata ControllerListener, trebuie:
implementata interfata ControllerListener într-o clasa
înregistrata acea clasa ca si un listener prin apelarea metodei addControllerListener asupra Controller-ului de la care vrem sa primim evenimente.
Când un Controller genereaza un eveniment, este apelata metoda controllerUpdate asupra fiecarui listener înregistrat.
De obicei, metoda controllerUpdate este implementata ca o serie de instructiuni if-else, dupa cum se vede si în Exemplul 8.3.
Exemplul 8.3. Implementarea metodei ControllerUpdate |
if (event instanceof EventType) else if (event instanceof OtherEventType) |
Prin aceasta se executa o operatie de filtrare a evenimentelor care nu ne intereseaza. Daca este înregistrat ca un listener cu Controllere multiple, trebuie determinat si care anume dintre Controllere a generat evenimentul. Din acest motiv, evenimentele ControllerEvent contin o referinta spre sursa care le-a initiat. Aceasta referinta este accesibila prin apelarea getSource.
Când se primesc evenimente de la un Controller, sunt necesare o serie de operatii pentru a fi sigur ca Controller-ul este în starea corespunzatoare înainte de apelarea unei metode de control. Spre exemplu, înainte de apelarea oricarei metode care sunt restrictionate la Player-ele oprite, trebuie verificata starea tintei Player-ului prin apelarea metodei getTargerState. Daca start a fost apelat, Player-ul considera ca este în starea Started, deci este posibil sa genereze evenimente de tranzitie în timp ce se pregateste sa prezinte datele.
Unele tipuri de evenimente ControllerEvents contin informatii de stare aditionale. Spre exemplu, clasele StartEvent si StopEvent definesc fiecare o metoda care permite utilizatorului sa afle timpul exact când a aparut un eveniment
8.4.1.9.2. Folosirea clasei ControllerAdapter
ControllerAdapter este o clasa cu unele avantaje care implementeaza interfata ControllerListener si care poate fi extinsa usor pentru a raspunde unor evenimente particulare. Pentru a implementa interata ControllerListener folosind clasa ControllerAdapter, sunt necesare urmatoarele operatii:
trebuie creata o subclasa ControllerAdapter si trebuie suprapuse peste metodele clasice metodele pentru evenimentele pe care dorim sa le implementam.
clasa ControllerAdapter trebuie înregistrata ca un listener pentru un anumit Controller prin apelarea metodei addControllerListener
Când un Controller genereaza un eveniment, el apeleaza ControllerUpdate asupra fiecarui listener înregistrat. ControllerAdapter expediaza automat evenimentul spre metoda eveniment corespondenta, filtrând în acelasi timp evenimentele care nu ne intereseaza.
Spre exemplu, urmatorul cod extinde un ControllerUpdate cu o clasa care contine un Player care este resetat automat la începutul datelor si este dealocat când Player-ul ajunge la sfârsitul datelor.
Exemplul 8.4. Folosirea ControllerAdapter |
player.addControllerListener(new ControllerAdapter() } |
Daca se înregistreaza 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 apeleaza 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 acelasi TimeBase. Pentru aceasta, se folosesc metodele getTimeBase si setTimeBase definite de interfata Clock. Spre exemplu, daca avem doua Playere, mai exact player1 si player2, le putem sincroniza între ele setând player1 sa foloseasca baza de timp a lui player2:
Exemplul 8.5. Sincronizarea Player-elor |
Player1.setTimeBase(player2.getTimeBase()); |
Chiar daca realizam sincronizarea player-elor prin asocierea lor la o baza de timp comuna, controlul asupra lor trebuie realizat individual. Deoarece administrarea unor Playere sincronizate prin aceasta metoda poate fi complicata, JMF pune la dispozitie un mecanism care permite unui Player sa detina control asupra oricarui alt Controller. Player-ul administreaza starile acestor Controllere automat, permitând utilizatorului sa interactioneze cu întregul grup printr-un singur punct de control.
8.4.1.10.1. Folosirea unui Player pentru sincronizarea Controller -elor
Sincronizarea directa a Player-elor folosind syncStart trebuie realizata cu atentie. Trebuie controlata fiecare stare a fiecarui Player în parte, trebuie ascultate evenimentele si trebuie apelate metodele potrivite pentru fiecare eveniment separat. Prin intermediul interfetei Player, JMF pune la dispozitia programatorilor o solutie mai simpla: un Player poate fi folosit pentru a administra operatiile efectuate de orice Controller
Când se interactioneaza cu Player-ul de administrare, instructiunile sunt automat trimise spre Controller-ul potrivit. Player-ul de administrare se ocupa de administrarea starilor în care se afla celelalte Controllere si de sincronizarea lor.
Acest mecanism este implementat prin metodele addController si removeController. Când se apeleaza metoda addController asupra unui Player, Controller-ul specificat este adaugat la lista de Controllere adminstrate de acel Player. Invers, la apelarea metodei removeController, Controller-ul specificat este înlaturat din lista Controller-elor administrate.
De obicei, când se doreste sincronizarea Player-elor sau altor Controllere, trebuie utilizat acest mecanism addController. Este simplu, este rapid si mai ferit de erori decât încercarea de a administra Playere sincronizate individual.
Când un Player ia controlul asupra unui Controller
Controller-ul îsi atribuie timpul de baza al Player-ului.
Durata Player-ului devine durata maxima dintre durata initiala a Player-ului si durata Controller-ului. Daca mai multe Controllere se afla sub administrarea unui Player, durata Player-ului devine durata cea mai lunga.
Latenta de start a Player-ului devine cea mai lunga dintre cea a Controller-ului administrat si latenta lui. Daca mai multe Controllere se afla sub administrarea unui Player, latenta Player-ului devine latenta cea mai lunga.
Un Player care administreaza genereaza doar evenimente de îndeplinire (completare) pentru metodele asincrone dupa ce fiecare dintre Controller-ele administrate a postat un eveniment
Pentru a adauga un Controller listei de Controllere administrate de un anumit Player se foloseste metoda addController. Pentru a putea fi adaugat, Controller-ul trebuie adaugat fie în starea Realized, în caz contrar fiind generata o eroare NotRealizedError. Doua Playere nu se pot administra simultan unul pe altul. Spre exemplu, daca Player-ul 1 se afla sub administrarea Player-ului 2, Player-ul 2 nu se poate afla sub administrarea Player-ului 1 fara iesirea în prealabil a Player-ului 1 de sub administrarea Player-ului 2.
Odata ce un Controller a fost adaugat unui Player, nu se pot apela metode direct spre el. Pentru aceste operatii se interactioneaza cu Player-ul administrator.
În Exemplul 8.6 se arata cum se ofera Player-ului 2 administrarea Player-ului 1.
Exemplul 8.6. Preluarea administrarii asupra unui Player |
player2.addController(player1); |
Pentru a controla operatiile efectuate de catre un grup de Controllere administrate de catre un anumit Player, trebuie interactionat direct cu Player-ul administrator.
Spre exemplu, pentru a pregati toate Controller-ele administrate de pornire, trebuie apelata metoda prefetch asupra Player-ului administrator. Acesta se asigura ca toate Controller-ele se afla în starea Prefetched, determina latenta de pornire maxima si apeleaza metoda syncStart pentru a le porni, specificând latenta maxima de start care trebuie respectata.
Când se apeleaza o metoda asupra Player-ului administrator, acesta o propaga spre Controller-ele aflate sub administrarea lui. Înainte de aceasta, el se asigura ca Controller-ul se afla în faza potrivita. Urmatorul tabel descrie ce se întâmpla unui Controller administrat când se apeleaza o metoda asupra Player-ului administrator.
Functia |
Player oprit |
Player pornit |
setMediaTime |
Apeleaza setMediaTime asupra tuturor Controller-elor administrate |
Opreste toate Controller-ele administrate, apeleaza setMediaTime si reporneste toate Controller-ele. |
setRate |
Apeleaza setRate asupra tuturor Controller-elor administrate. Returneaza rata actuala care este suportata de toate Controller-ele si o seteaza. |
Opreste toate Controller-ele administrate, apeleaza setRate si reporneste toate Controller-ele. Returneaza rata curenta care este suportata de toate Player-ele. |
Start |
Se asigura ca toate Controller-ele administrate se afla în starea Prefetched si apeleaza syncStart asupra fiecaruia dintre ele, tinând cont de latentele lor de start |
Depinde de implementarea Player ului. Acesta poate genera imediat un eveniment StartEvent. |
Realize |
Player-ul de administrare posteaza imediat un eveniment RealizeCompleteEvent. Pentru a putea fi adaugat, un Controller trebuie sa fie realized. |
Player-ul de administrare genereaza imediat un RealizeCompleteEvent. Pentru a putea fi adaugat, un Controller trebuie sa fie realized. |
Prefetch |
Apeleaza prefetch asupra tuturor Controller-elor administrate. |
Player-ul de administrare genereaza imediat un PrefetchCompleteEvent, indicând ca toate Controller-ele administrate sunt Prefetched. |
Stop |
Nu are efect. |
Apeleaza metoda stop asupra tuturor Controller-elor administrate. |
deallocate |
Apeleaza deallocate asupra tuturor Controller-elor administrate. |
Genereaza o eroare. |
setStopTime |
Apeleaza setStopTime asupra tuturor Controller-elor administrate. (Player-ul trebuie sa fie Realized). |
Apeleaza setStopTime asupra tuturor Controller-elor administrate. (Poate fi apelata o singura data asupra unui Player pornit). |
syncStart |
Apeleaza syncStart asupra tuturor Controller-elor administrate. |
Genereaza o eroare. |
Close |
Apeleaza close asupra tuturor Controller-elor administrate.. |
Genereaza o eroare. |
Tabel 8.6. Metodele de control aplicabile asupra unui Player administrator
Pentru îndepartarea unui Controller se foloseste metoda removeController. Exemplul 8.7 arata cum se face eliberarea lui player1 de sub administrarea lui player2.
Exemplul 8.7. Eliberarea administrarii unui Controller |
player2.removeController(player1); |
|