Nos, akkor leirom, hogyan is működik ez az egész. A fő modulok: 1. streamer.c: ez az input layer, azaz ez olvassa a filet, VCD-t vagy stdin-t. amit tudnia kell: megfelelő sectoronkenti bufferelés, seek, skip funkciók, byte-onkénti ill. tetszőleges méretű blockonkénti olvasás. Egy stream (input device/file) leírására a stream_t struktura szolgál. 2. demuxer.c: ez végzi az input szétszedését audio és video csatornákra, és a kiválasztott csatornák bufferelt package-enkénti olvasását. A demuxer.c inkább csak egy framework, ami közös minden input formátumra, és az egyes formátumokhoz (mpeg-es,mpeg-ps, avi, avi-ni, asf) külön parser van, ezek a demux_*.c fileokban vannak. A hozza tartozo struktura a demuxer_t. osszesen egy demuxer van. 2.a. demux_packet_t, azaz dp. ez egy darab chunk-ot (avi) vagy packet-et (asf,mpg) tartalmaz. memoriaban ezek lancolt listaban vannak, mivel kulonbozo meretuek. 2.b. demuxer stream, azaz ds. struct: demux_stream_t minden egyes csatornahoz (a/v) tartozik egy ilyen. ez tartalmazza a stream-hez tartozo packeteket (lasd. 2.a.) egyelore demuxer-enkent 3 ilyen lehet: - hang (d_audio) - kep (d_video) - DVD felirat (d_dvdsub) 2.c. stream header. 2 fele van (egyelore): sh_audio_t es sh_video_t ez tartalmaz minden, a dekodolashoz szukseges parametert, igy az input es output buffereket, kivalasztott codecet, fps/framerate stb adatokat. Annyi van belole, ahany stream van a fileban tarolva. Lesz minimum egy a videohoz, ha van hang akkor ahhoz is, de ha tobb audio/video stream is van, akkor mindegyikhez lesz egy ilyen struct. Ezeket avi/asf eseten a header alapjan tolti fel a header beolvaso, mpeg eseten pedig a demux_mpg.c fogja letrehozni ha egy uj streamet talal. Uj stream eseten ====> Found audio/video stream: jelenik meg. A kivalasztott stream header es a hozza tartozo demuxer stream kolcsonosen hivatkoznak egymasra (ds->sh es sh->ds) az egyszerubb hasznalat miatt. (igy a funkciotol fuggoen eleg vagy csak a ds vagy csak az sh atadasa) Pelda: van egy .asf fileunk, abban 6 db stream, ebbol 1 audio es 5 video. A header beolvasasakor letre fog jonni 6 db sh struct, 1 audio es 5 video. Amikor elkezdi olvasni a packeteket, az elso talalt audio es video packethez tartozo streamet kivalasztja, es ezekre allitja be a d_audio es d_video sh pointereit. Igy kesobbiekben mar csak ezeket a streameket olvassa, a tobbit nem. Persze ha az user masik streameket szeretne kivalasztani, akkor force-olhatja a -aid es -vid kapcsolokkal. Jo pelda erre a DVD, ahol nem mindig az angol szinkron hang az elso megtalalt stream, es igy random minden vob mas nyelven szolalhat meg :) Ilyenkor kell pl. az -aid 128 kaocsolot hasznalni. hogy is muxik ez a beolvasosdi? - meghivodik a demuxer.c/demux_read_data(), megkapja melyik ds-bol (audio vagy video), mennyi byteot es hova (memoriacim) szeretnenk beolvasni. ezt hivogatjak gyakorlatilag a codec-ek. - ez megnezi,hogy az adott ds bufferében van-e valami, ha igen akkor onnan olvas amennyit kell. ha nincs/nincs eleg, akkor meghivja a ds_fill_buffer()-t ami: - megnezi hogy az adott ds-ben vannak-e bufferelve csomagok (dp-k) ha igen, akkor a legregebbit atrakja a bufferbe es olvas tovabb. ha ures a lancolt lista, akkor meghivja a demux_fill_buffer()-t: - ez az input formatumnak megfelelo parser-t meghivja ami olvassa tovabb a filet, es a talalt csomagokat rakja be a megfelelo bufferbe. na ha mondjuk audio csomagot szeretnenk, de csak egy rakat video csomag van, akkor jon elobb-utobb a DEMUXER: Too many (%d in %d bytes) audio packets in the buffer... hibauzenet. Eddig kb tiszta ugy, ezt akarom majd atrakni kulon lib-be. na nezzuk tovabb: 3. mplayer.c - igen, o a fonok :) az idozites eleg erdekesen van megoldva, foleg azert mert minden fileformatumnal maskepp kell/celszeru, es neha tobbfele keppen is lehet. van egy a_frame es egy v_frame nevu float valtozo, ez tarolja az epp lathato/hallhato a/v poziciojat masodpercben. A lejatszo ciklus felepitese: while(not EOF) { fill audio buffer (read & decode audio) + increase a_frame read & decode a single video frame + increase v_frame sleep (wait until a_frame>=v_frame) display the frame apply A-V PTS correction to a_frame check for keys -> pause,seek,... } amikor lejatszik (hang/kep) akkor a lejatszott valami idotartamaval noveli a megfelelo valtozot: - audional ez a lejatszott byteok / sh_audio->o_bps megj: i_bps = tomoritett byteok szama egy masodpercnyi hanghoz o_bps = tomoritetlen byteok szama egy masodpercnyi hanghoz (ez utobbi == bps*samplerate*channels) - videonal ez altalaban az sh_video->frametime. Ez altalaban == 1.0/fps, persze meg kell jegyeznem hogy videonal nem igazan szamit az fps, asf-nel pl. nincs is olyan, ahelyett duration van es framenkent valtozhat. mpeg2-nel pedig repeat_count van ami 1-2.5 idotartamban elnyujtja a framet... avi-nal van talan egyedul fix fps, meg mpeg1-nel. Na most ez addig nagyon szepen mukodik, amig a hang es kep tokeletes szinkronban van, mivel igy vegulis a hang szol, az adja az idozitest, es amikor eltelt egy framenyi ido akkor kirakja a kovetkezo framet. de mi van ha valamiert az input fileban csuszik a ketto? Akkor jon be a PTS correction. az input demuxer-ek olvassak a csomagokkal egyutt a hozzajuk tartozo PTS-t (presentation timestamp) is, ami alapjan eszreveheto ha el van csuszva a ketto. ilyenkor egy megadott maximalis hataron (lasd -mc opcio) belul kepes az mplayer korrigalni az a_frame erteket. a korrekciok osszege van a c_total-ban. persze ez meg nem minden szinkron ugyben, van meg nemi gaz. pl. az hogy a hangkartya eleg rendesen kesleltet, ezt az mplayernek korrigalnia kell! Az osszes audio kesleltetes masodpercben ezek osszege: - az utolso timestamp (PTS) ota beolvasott byteok: t1 = d_audio->pts_bytes/sh_audio->i_bps - Win32/ACM eseten az audio input bufferben tarolt byteok: t2 = a_in_buffer_len/sh_audio->i_bps - az audio out bufferben tarolt tomoritetlen byteok: t3 = a_buffer_len/sh_audio->o_bps - a hangkartya buffereben (vagy DMA bufferben) tarolt, meg nem lejatszott byteok: t4 = get_audio_delay()/sh_audio->o_bps Ezekbol kiszamolhato egeszen pontosan, hogy az epp hallhato hanghoz milyen PTS tartozik, majd ezt osszevetve a video-hoz tartozo PTS-el meg is kapjuk az A-V eltereset! avi-nal sem egyszeru az elet. ott a 'hivatalos' idozitesi mod a BPS-alapu, azaz a headerben le van tarolva hany tomoritett audio byte tartozik egy masodpercnyi (fps darab) kephez. ez persze nem mindig mukodik... miert is mukodne :) ezert en megcsinaltam hogy az mpeg-nel hasznalatos sectoronkenti PTS erteket emulalom avi-ra is, azaz az AVI parser minden beolvasott chunk-nal szamol egy kamu PTS-t a framek tipusa alapjan. es ez alapjan idozitek. es van amikor ez mukodik jobban. persze itt meg bejatszik az is, hogy AVI-nal altalaban elore letarolnak egy nagyobb adag hangot, es csak utana kezdodik a kep. ezt persze bele kell szamolni a kesleltetesbe, ez az Initial PTS delay. ilyen persze 2 is van, az egyik a headerben le is van irva, es nem nagyon hasznlajak :) a masik sehol nincs leirva de hasznaljak, ezt csak merni lehet... 3.a. audio playback: par szo az audio lejatszasrol: az egeszben nem maga a lejatszas a nehez, hanem: 1. hogy tudjuk mikor lehet irni a bufferbe, blocking nelkul 2. hogy tudjuk, mennyit jatszott mar le abbol amit a bufferbe irtunk Az 1. az audio dekodolashoz kell, valamint hogy a buffert mindig teli allapotban tudjuk tartani (igy sose fog megakadni a hang). A 2. pedig a korrekt idoziteshez szukseges, ugyanis nemely hangkartya akar 3-7 masodpercet is kesleltet, ami azert nem elhanyagolhato! Ezek megvalositasara az OSS tobbfele lehetoseget is kinal: - ioctl(SNDCTL_DSP_GETODELAY): megmondja hany lejatszatlan byte varakozik a hangkartya bufferjeben -> idoziteshez kivallo, de nem minden driver tamogatja :( - ioctl(SNDCTL_DSP_GETOSPACE): megmondja mennyit irhatunk a kartya bufferebe blocking nelkul. ha a driver nem tudja a GETODELAY-t, akkor ezt hasznalhatjuk arra is, hogy megtudjuk a kesleltetest. - select(): meg kene mondja, hogy irhatunk-e a kartya bufferebe blocking nelkul. azt, hogy emnnyit irhatunk, nem mondja meg :( valamint sok driverrel egyaltalan nem, vagy rosszul mukodik :(( csak akkor hasznalom, ha egyik fenti ioctl() sem mukodik. 4. codecek. ezek kulonbozo lib-ek szanaszet mindenfelol. mint pl. libac3, libmpeg2, xa/*, alaw.c, opendivx/*, loader, mp3lib. az mplayer.c hivogatja oket amikor egy egy darab hangot vagy framet kell lejatszani (lasd 3. pont elejen). ezek pedig hivjak a megfelelo demuxert hogy megkapjak a tomoritett adatokat (lasd 2. pont). parameterkent a megfelelo stream headert (sh_audio/sh_video) kell atadni, ez elvileg tartalmaz minden infot ami szukseges a dekodolashoz (tobbek kozott a demuxert is: sh->ds). A codecek szeparalasa folyamatban van, az audio mar el van kulonitve (lasd. dec_audio.c), a videon meg dolgozunk. Cel, hogy ne az mplayer.c kelljen tudja milyen codecek vannak es hogy kell oket hasznalni, hanem egy kozos init/decode audio/video functiont kelljen csak meghivnia. 5. libvo: ez vegzi a kep kirakasat. Az img_format.h-ban definialva vannak konstansok a kulonbozo pixel- formatumokhoz, ezeket kotelezo hasznalni. 1-1 vo driver a kovetkezoket kell kotelezoen implementalja: query_format() - lekerdezi hogy egy adott pixelformat tamogatott-e. return value: flags: 0x1 - supported (by hardware or with conversion) 0x2 - supported (by hardware, without conversion) 0x4 - sub/osd supported (has draw_alpha) FONTOS: minden vo driver kotelezo tamogassa az YV12 formatumot, es egyiket (vagy mindkettot) a BGR15 es BGR24 kozul, ha kell, konvertalassal. Ha ezeket nem tamogatja, akkor nem fog minden codec-kel mukodni! Ennek az az oka, hogy az mpeg codecek csak YV12-t tudnak eloallitani, a regebbi Win32 DLL codecek pedig csak 15 es 24bpp-t tudnak. Van egy gyors MMX-es 15->16bpp konvertalo, igy az nem okoz kulonosebb sebessegcsokkenest! A BPP tablazat, ha a driver nem tud bpp-t valtani: jelenlegi bpp: ezeket kell elfogadni: 15 15 16 15,16 24 24 24,32 24,32 Ha tud bpp-t valtani (pl. DGA 2, fbdev, svgalib) akkor ha lehet, be kell valtani a kert bpp-re. Ha azt a hardver nem tamogatja, akkor a legkozelebbi modra (15 eseten 16-ra, 24 eseten 32-re) kell valtani es konvertalni! init() - ez hivodik meg a legelso frame kirakasa elott - bufferek foglalasa stb a celja. van egy flags parameter is (regen fullscreen volt a neve): 0x01 - fullscreen (-fs) 0x02 - vidmode switch (-vm) 0x04 - scaling enabled (-zoom) 0x08 - flip image (upside-down) draw_slice(): ez planar YV12 kepet rak ki (3 db plane, egy teljes meretu ami a fenyerot (Y) tartalmazza, es 2 negyedakkora, ami a szin (U,V) infot). ezt hasznaljak az mpeg codecek (libmpeg2,opendivx). ez mar tud olyat hogy nem az egesz kep kirakasa, hanem csak kis reszletek updatelese: ilyenkor a sarkanak es a darabka meretenek megadasaval lehet csinalni. draw_frame(): ez a regebbi interface, ez csak komplett framet rak ki, es csak packed formatumot (YUY2 stb, RGB/BGR) tud. ezt hasznaljak a win32 codecek (divx,indeo stb). draw_alpha(): ez rakja ki a subtitle-t es az OSD-t. hasznalata kicsit cseles, mivel ez nem a libvo API resze, hanem egy callback jellegu cucc. a flip_page() kell meghivja a vo_draw_text()-et ugy, hogy parameterkent atadja a kepernyo mereteit es a pixelformatumnak megfelelo draw_alpha() implementaciot (function pointer). Ezutan a vo_draw_text() vegigmegy a kirajzolando karaktereken, es egyenkent meghivja minden karakterre a draw_alpha()-t. Segitseg keppen az osd.c-ben meg van irva a draw_alpha mindenfele pixelformatumhoz, ha lehet ezt hasznald! flip_page(): ez meghivodik minden frame utan, ez kell tenylegesen megjelenitse a buffert. double buffering eseten ez lesz a 'swapbuffers'. 6. libao2: ez vezerli a hang lejatszast A libvo-hoz (lasd 5.) hasonloan itt is kulonbozo driverek vannak, amik egy kozos API-t (interface) valositanak meg: static int control(int cmd,int arg); Ez egy altalanos celu fuggveny, a driverfuggo es egyeb specialis parameterek olvasasara/beallitasara. Egyelore nem nagyon hasznalt. static int init(int rate,int channels,int format,int flags); Driver initje, ilyenkor kell megnyitni a devicet, beallitani samplerate, channels, sample format parametereket. Sample format: altalaban AFMT_S16_LE vagy AFMT_U8, tovabbi definiciokert lasd. dec_audio.c ill. linux/soundcard.h fileok! static void uninit(); talald ki. na jo, segitek: lezarja a devicet, kilepeskor (meg nem) hivodik meg. static void reset(); reseteli a devicet. egesz pontosan a bufferek torlesere szolgal, tehat hogy a reset() utan mar ne szoljon tovabb az amit elotte kapott. (pause ill. seek eseten hivodik meg) static int get_space(); vissza kell adja hogy hany byte irhato az audio bufferbe anelkul hogy blockolna (varakoztatna a hivo processt). amennyiben a buffer (majdnem) tele van, 0-t kell visszaadni! ha sosem ad vissza 0-at akkor nem fog mukodni az MPlayer! static int play(void* data,int len,int flags); lejatszik egy adag hangot, amit a data cimu memoriateruleten kap, es len a merete. a flags meg nem hasznalt. az adatokat at kell masolnia, mert a hivas utan felulirodhatnak! nem kell feltetlen minden byetot felhasznalni, hanem azt kell visszaadnia mennyit hasznalt fel (masolt a bufferbe). static int get_delay(); vissza kell adja hogy hany byte varakozik az audio bufferben. lehetoleg minel pontosabban, mert ettol fugg az egesz idozites! legrosszabb esetben adja vissza a buffer meretet. !!! Mivel a kep a hanghoz (hangkartyahoz) van szinkronizalva, igy nagyon !!! fontos hogy a get_space ill. get_delay fuggvenyek korrektul legyenek megirva!