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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
[yes, this is hungarian. maybe someone will translate this to russian or
something else...]
Nos, akkor leirom, hogyan is működik ez az egész.
Az ékezetekkel majd lesz valami, nem nagyon vagyok hozzászokva az
ékezetes gépeléshez...
A program felépítése alapjaiban logikus, de eleg gányul van megirva :)
A fő modulok:
1. streamer.c: ez az input, azaz ez olvassa a filet vagy VCD-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-nké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 2 ilyen lehet, egy a hanghoz es egy a kephez.
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: <id> 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 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 megenzi,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 parset meghivja ami olvassa
tovabb a filet, es a talalt csomagokat rakja be a megfelelo bufferbe.
na ha mondjuk audio csomagot szeretennk, 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.
akkor jelenit meg ujabb video frame-t, ha v_frame<a_frame, es akkor
dekodol tovabb hangot ha a_frame<v_frame.
amikor lejatszik (hang/kep) akkor a lejatszott valami idotartamaval
noveli a megfelelo valtozot. videonal ez altalaban 1.0/fps, persze
meg kell jegyeznem hogy videonal nem igazna 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: ezert kell neki az audio buffer merete. amit a
select()-e tud lemerni amit viszont nem tud minden kartya...
ilyenkor kell a -abs opcioval megadni.
aztan van olyan gond is, hogy pl. mpegnel nem framenkent van PTS
hanem szektoronkent, ami tartalmazhat 10 framet is de 0.1-et is.
hogy ez ne csessze el az idozitest, atlagoljuk 5 framenkent a
PTS-t es ezt az atlag erteket vesszuk figyelembe korrekcional.
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...
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.
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'.
|