Mercurial > mplayer.hg
view DOCS/tech/tech-hun.txt @ 3603:baa8b0c0ff30
Removed unnecessary check after the protocol autodetection.
Now it will try to start streaming even if the autodetection failed.
This will allow to work with web server that doesn't report a
proper mime-type.
author | bertrand |
---|---|
date | Wed, 19 Dec 2001 09:02:52 +0000 |
parents | 14af3106c359 |
children | 9c13e907f284 |
line wrap: on
line source
Nos, akkor leírom, 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ő sectoronkénti 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 struktúra 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 file-okban vannak. A hozzá tartozó struktúra a demuxer_t. Összesen egy demuxer van. 2.a. demux_packet_t, azaz dp. ez egy darab chunk-ot (avi) vagy packet-et (asf, mpg) tartalmaz. memóriában ezek láncolt listában vannak, mivel különböző méretűek. 2.b. demuxer stream, azaz ds. struct: demux_stream_t minden egyes csatornához (a/v) tartozik egy ilyen. ez tartalmazza a stream-hez tartozó packeteket (lásd. 2.a.) egyelőre demuxer-enként 3 ilyen lehet: - hang (d_audio) - kép (d_video) - DVD felirat (d_dvdsub) 2.c. stream header. 2 féle van (egyelőre): sh_audio_t és sh_video_t ez tartalmaz minden, a dekódoláshoz szükséges paramétert, így az input és output buffereket, kiválasztott codecet, fps/framerate stb adatokat. Annyi van belőle, ahány stream van a file-ban tárolva. Lesz minimum egy a videohoz, ha van hang akkor ahhoz is, de ha több audio/video stream is van, akkor mindegyikhez lesz egy ilyen struct. Ezeket avi/asf esetén a header alapján tölti fel a header beolvasó, mpeg esetén pedig a demux_mpg.c fogja létrehozni, ha egy új streamet talál. Új stream esetén ====> Found audio/video stream: <id> jelenik meg. A kiválasztott stream header és a hozzá tartozó demuxer stream kölcsönösen hivatkoznak egymásra (ds->sh és sh->ds) az egyszerűbb használat végett. (így a funkciótól függően elég vagy csak a ds vagy csak az sh átadása) Példa: van egy .asf file-unk, abban 6 db stream, ebből 1 audio és 5 video. A header beolvasásakor létre fog jönni 6 db sh struct, 1 audio és 5 video. Amikor elkezdi olvasni a packeteket, az első talált audio és video packethez tartozó streamet kivalasztja, es ezekre allitja be a d_audio és d_video sh pointereit. Így a későbbiekben már csak ezeket a streameket olvassa, a többit nem. Persze, ha a user másik streameket szeretne kiválasztani, akkor force-olhatja az -aid és -vid kapcsolókkal. Jó pelda erre a DVD, ahol nem mindig az angol szinkron hang az első megtalált stream, és így random minden vob más nyelven szólalhat meg :) Ilyenkor kell pl. az -aid 128 kapcsolót használni. hogy is műxik ez a beolvasósdi? - meghívódik a demuxer.c/demux_read_data(), megkapja melyik ds-ből (audio vagy video), mennyi byte-ot és hova (memóriacím) szeretnénk beolvasni. Ezt hívogatják gyakorlatilag a codec-ek. - ez megnézi, hogy az adott ds bufferében van-e valami, ha igen akkor onnan olvas, amennyit kell. Ha nincs/nincs elég, akkor meghívja a ds_fill_buffer()-t ami: - megnézi, hogy az adott ds-ben vannak-e bufferelve csomagok (dp-k) ha igen, akkor a legrégebbit átrakja a bufferbe és olvas tovább. Ha üres a láncolt lista, akkor meghívja a demux_fill_buffer()-t: - ez az input formátumnak megfelelő parser-t hívja meg, ami továbbol- vassa a file-t, és a talált csomagokat berakja a megfelelő bufferbe. Na, ha mondjuk audio csomagot szeretnénk, de csak egy rakat video csomag van, akkor jön előbb-utóbb a DEMUXER: Too many (%d in %d bytes) audio packets in the buffer... hibaüzenet. Eddig kb. tiszta ügy, ezt akarom majd átrakni külön lib-be. na nézzuk tovább: 3. mplayer.c - igen, ő a főnök :) Fő feladata a különböző modulok összekapcsolása, illetve az A-V szinkron biztosítása. Az adott stream aktuális pozíciója a megfelelo stream header (sh_audio / sh_video) timer field-ben van. (Régen ez volt az a_frame és egy v_frame nevű float változó) A lejátszó ciklus felépítése: 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 lejátszik (hang/kép) akkor a lejátszott valami időtartamával növeli a megfelelő változót: - audionál ez a lejátszott byte-ok / sh_audio->o_bps megj.: i_bps = tömörített byte-ok széma egy másodpercnyi hanghoz o_bps = tömörítetlen byte-ok száma egy másodpercnyi hanghoz (ez utóbbi == bps*samplerate*channels) - videonál ez általában az sh_video->frametime. Ez általában == 1.0/fps, persze meg kell jegyeznem, hogy videonál nem igazán számít az fps, asf-nél pl. nincs is olyan, ahelyett duration van és frame-enként változhat. mpeg2-nél pedig repeat_count van, ami 1-2.5 időtartamban elnyújtja a frame-et... avi-nál van talán egyedül fix fps, meg mpeg1-nél. Na most ez addig nagyon szépen működik, amíg a hang és kép tökéletes szinkronban van, mivel így végülis a hang szól, az adja az időzítést, és amikor eltelt egy frame-nyi idő, akkor kirakja a következő frame-et. De mi van, ha valamiért az input file-ban csúszik a kettő? Akkor jön be a PTS correction. Az input demuxer-ek olvassák a csomagokkal együtt a hozzájuk tartozó PTS-t (presentation timestamp) is, ami alapján észrevehető, ha el van csúszva a kettő. Ilyenkor egy megadott maximális határon (lásd -mc opció) belül képes az mplayer korrigalni az a_frame értékét. A korrekciók összege van a c_total-ban. Persze ez még nem minden szinkron ügyben, van még némi gáz. Pl. az, hogy a hangkártya elég rendesen késleltet, ezt az mplayernek korrigálnia kell! Az összes audio késleltetés másodpercben ezek összege: - az utolsó timestamp (PTS) óta beolvasott byte-ok: t1 = d_audio->pts_bytes/sh_audio->i_bps - Win32/ACM esetén az audio input bufferben tárolt byte-ok: t2 = a_in_buffer_len/sh_audio->i_bps - az audio out bufferben tárolt tömörítetlen byte-ok: t3 = a_buffer_len/sh_audio->o_bps - a hangkártya bufferében (vagy DMA bufferben) tárolt, még nem lejátszott byte-ok: t4 = get_audio_delay()/sh_audio->o_bps Ezekből kiszámolható egészen pontosan, hogy az épp hallható hanghoz milyen PTS tartozik, majd ezt összevetve a video-hoz tartozo PTS-el meg is kapjuk az A-V eltérését! Avi-nál sem egyszerű az élet. Ott a 'hivatalos' időzítési mód a BPS-alapú, azaz a headerben le van tárolva, hány tömörített audio byte vagy chunk tartozik egy másodpercnyi (fps darab) képhez. Az AVI stream headerben van 2 fontos mezo, a dwSampleSize, es a dwRate/dwScale aránypár: - Ha a dwSampleSize 0, akkor VBR stream, tehat nem konstans a bitrate. Ilyenkor 1 chunk tarol 1 sample-t, es a masodpercenkenti chunkok szamat adja a dwRate/dwScale. - Ha a dwSampleSize>0, akkor constant bitrate van, es az ido igy szamolhato: time = (bytepos/dwSampleSize) / (dwRate/dwScale) (tehat a sample sorszamat elosztjuk a samplerate-el) Ilyenkor stream-kent kezelheto az audio, ami tetszolegesen chunk-okra van darabolva, de lehet akar 1 db chunk is az egesz. A másik lehetőség csak az interleaved fileoknál használható: a chunk-ok sorrendjéből számolható egy timestamp (PTS) érték. A video chunkok PTS-e egyszerű: chunk száma * fps Az audio pedig az előtte levő video chunk-éval azonos. Ilyenkor viszont szamolni kell az ugynev. "audio preload"-al is, azaz van egy fix kesleltetes az audio es video stream-ek kozott. Ez altalaban 0.5-1.0 sec, de van amikor egeszen mas. A pontos erteket regen mertuk, most a demux_avi.c kezeli le: az elso video utani audio chunknal kiszamolja az A-V elterest, es ezt veszi az audio preload mertekenek. 3.a. audio playback: pár szó az audio lejátszásról: az egészben nem maga a lejátszás a nehéz, hanem: 1. hogy tudjuk, mikor lehet írni a bufferbe, blocking nélkül 2. hogy tudjuk, mennyit játszott már le abból, amit a bufferbe írtunk Az 1. az audio dekódoláshoz kell, valamint hogy a buffert mindig teli állapotban tudjuk tartani (így sose fog megakadni a hang). A 2. pedig a korrekt időzítéshez szükséges, ugyanis némely hangkártya akár 3-7 másodpercet is késleltet, ami azért nem elhanyagolható! Ezek megvalósítására az OSS többféle lehetőséget is kínál: - ioctl(SNDCTL_DSP_GETODELAY): megmondja, hány lejátszatlan byte várakozik a hangkártya bufferében -> időzítéshez kiváló, de nem minden driver támogatja :( - ioctl(SNDCTL_DSP_GETOSPACE): megmondja, mennyit írhatunk a kártya bufferébe blocking nélkül. Ha a driver nem tudja a GETODELAY-t, akkor ezt hasznalhatjuk arra is, hogy megtudjuk a késleltetést. - select(): meg kéne mondja, hogy írhatunk-e a kártya bufferébe blocking nélkül. Azt, hogy mennyit írhatunk, nem mondja meg :( valamint sok driverrel egyáltalán nem, vagy rosszul működik :(( csak akkor használom, ha egyik fenti ioctl() sem működik. 4. codecek. ezek különböző lib-ek szanaszét mindenfelől. mint pl. libac3, libmpeg2, xa/*, alaw.c, opendivx/*, loader, mp3lib. Az mplayer.c nem kozvetlenul hivja oket, hanem a dec_audio.c es a dec_video.c fileokon keresztul, igy az mplayer.c-nek nem kell semmit sem tudnia a codecrol. 5. libvo: ez végzi a kép kirakását. Az img_format.h-ban definiálva vannak konstansok a különböző pixel- formátumokhoz, ezeket kötelező használni. 1-1 vo drivernek a következőket kell kötelezően implementálnia: query_format() - lekérdezi, hogy egy adott pixelformat támogatott-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 drivernek kötelező támogatnia az YV12 formátumot, és egyiket (vagy mindkettőt) a BGR15 és BGR24 közül, ha kell, konvertálással. Ha ezeket nem támogatja, akkor nem fog minden codec-kel működni! Ennek az az oka, hogy az mpeg codecek csak YV12-t tudnak előállítani, a régebbi Win32 DLL codecek pedig csak 15 és 24bpp-t tudnak. Van egy gyors MMX-es 15->16bpp konvertáló, így az nem okoz különösebb sebességcsökkenést! A BPP táblázat, ha a driver nem tud bpp-t váltani: jelenlegi bpp: ezeket kell elfogadni: 15 15 16 15,16 24 24 24,32 24,32 Ha tud bpp-t váltani (pl. DGA 2, fbdev, svgalib) akkor, ha lehet, be kell váltani a kért bpp-re. Ha azt a hardver nem támogatja, akkor a legközelebbi módra (15 esetén 16-ra, 24 esetén 32-re) kell váltani és konvertálni! init() - ez hívódik meg a legelső frame kirakása előtt - bufferek foglalása stb a célja. van egy flags paraméter is (régen 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 képet rak ki (3 db plane, egy teljes méretű, ami a fényerőt (Y) tartalmazza, és 2 negyedakkora, ami a szín (U,V) infót). ezt használják az mpeg codecek (libmpeg2, opendivx). ez már tud olyat, hogy nem az egész kép kirakása, hanem csak kis részletek update-elése: ilyenkor a sarkának és a darabka méretének megadásával lehet használni. draw_frame(): ez a régebbi interface, ez csak komplett frame-et rak ki, és csak packed formátumot (YUY2 stb, RGB/BGR) tud. ezt használják a win32 codecek (divx, indeo stb). draw_alpha(): ez rakja ki a subtitle-t és az OSD-t. használata kicsit cseles, mivel ez nem a libvo API része, hanem egy callback jellegű cucc. a flip_page() kell meghívja a vo_draw_text()-et úgy, hogy paraméterként átadja a képernyő méreteit és a pixel- formátumnak megfelelő draw_alpha() implementációt (function pointer). Ezután a vo_draw_text() végigmegy a kirajzolandó karaktereken, és egyenként meghívja minden karakterre a draw_alpha()-t. Segítség képpen az osd.c-ben meg van írva a draw_alpha mindenféle pixelformátumhoz, ha lehet ezt használd! flip_page(): ez meghívódik minden frame után, ennek kell ténylegesen meg- jeleníteni a buffert. double buffering esetén ez lesz a 'swapbuffers'. 6. libao2: ez vezérli a hang lejátszást A libvo-hoz (lásd 5.) hasonlóan itt is különböző driverek vannak, amik egy közös API-t (interface) valósítanak meg: static int control(int cmd,int arg); Ez egy általános célú függvény, a driverfüggő és egyéb speciális paraméterek olvasására/beállítására. Egyelőre nem nagyon használt. static int init(int rate,int channels,int format,int flags); Driver initje, ilyenkor kell megnyitni a device-t, beállítani samplerate, channels, sample format paramétereket. Sample format: általában AFMT_S16_LE vagy AFMT_U8, további definíciókért lásd. dec_audio.c ill. linux/soundcard.h file-okat! static void uninit(); Találd ki! Na jó, segítek: lezárja a device-t, kilépéskor (még nem) hívódik meg. static void reset(); Reseteli a device-t. Egész pontosan a bufferek törlésére szolgál, tehát hogy a reset() után már ne szóljon tovább az, amit előtte kapott. (pause ill. seek esetén hívódik meg) static int get_space(); Vissza kell adja, hogy hány byte írható az audio bufferbe anélkül, hogy blockolna (várakoztatná a hívó processt). Amennyiben a buffer (majdnem) tele van, 0-t kell visszaadni! Ha sosem ad vissza 0-t, akkor nem fog működni az MPlayer! static int play(void* data,int len,int flags); Lejátszik egy adag hangot, amit a data című memóriaterületen kap és len a mérete. a flags még nem használt. Az adatokat át kell másolnia, mert a hívás után felülíródhatnak! Nem kell feltétlen minden byte-ot felhasználni, hanem azt kell visszaadnia, mennyit használt fel (másolt a bufferbe). static int get_delay(); Vissza kell adja, hogy hány byte várakozik az audio bufferben. lehetőleg minél pontosabban, mert ettől függ az egész időzítés! Legrosszabb esetben adja vissza a buffer méretét! !!! Mivel a kép a hanghoz (hangkártyához) van szinkronizálva, így nagyon fontos, !!! hogy a get_space ill. get_delay függvények korrektül legyenek megírva!