1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module zmplay
/*is aliced*/;
19 import std
.stdio
: File
;
29 ////////////////////////////////////////////////////////////////////////////////
30 mixin(MyException
!"PlayerError");
33 ////////////////////////////////////////////////////////////////////////////////
47 void loadHeader(ST
) (auto ref ST st
) if (isReadableStream
!ST
&& isSeekableStream
!ST
) {
49 st
.rawReadExact(sign
[]);
50 if (sign
!= "RIFF") throw new PlayerError("not a RIFF");
51 uint riffsize
= st
.readNum
!uint();
52 st
.rawReadExact(sign
[]);
53 if (sign
!= "AVI ") throw new PlayerError("not an AVI");
54 st
.rawReadExact(sign
[]);
55 if (sign
!= "LIST") throw new PlayerError("not an AVI");
56 uint listsize
= st
.readNum
!uint();
57 if (listsize
> 512) throw new PlayerError("AVI header too big");
58 ulong hepos
= st
.tell
+listsize
;
60 st
.rawReadExact(sign
[]);
61 if (sign
!= "hdrl") throw new PlayerError("invalid AVI");
62 st
.rawReadExact(sign
[]);
63 if (sign
!= "avih") throw new PlayerError("no AVI header");
64 uint sz
= st
.readNum
!uint();
65 if (sz
< 56) throw new PlayerError("invalid AVI header");
66 ulong npos
= st
.tell
+sz
;
67 fps
= 1000000/st
.readNum
!uint(); // microseconds per frame --> FPS
68 if (fps
< 1 || fps
> 255) throw new PlayerError("invalid FPS");
71 uint flags
= st
.readNum
!uint();
72 if ((flags
&0x100) == 0) throw new PlayerError("non-interleaved AVI");
73 hasIndex
= ((flags
&0x10) != 0);
74 framesTotal
= st
.readNum
!uint();
75 st
.readNum
!uint(); // initial frames
76 streamCount
= st
.readNum
!uint();
77 if (streamCount
< 1 || streamCount
> 2) throw new PlayerError("invalid number of streams");
79 videoW
= st
.readNum
!uint();
80 videoH
= st
.readNum
!uint();
81 if (videoW
< 32 || videoH
< 32 || videoW
> 4096 || videoH
> 4096) throw new PlayerError("invalid video dimensions");
82 // now load stream list
84 st
.rawReadExact(sign
[]);
85 if (sign
!= "LIST") throw new PlayerError("not an AVI");
87 st
.rawReadExact(sign
[]);
88 if (sign
!= "strl") throw new PlayerError("no stream list found");
89 st
.rawReadExact(sign
[]);
90 if (sign
!= "strh") throw new PlayerError("no stream list found");
91 sz
= st
.readNum
!uint();
92 if (sz
< 56) throw new PlayerError("invalid AVI header");
94 st
.rawReadExact(sign
[]);
95 if (sign
!= "vids") throw new PlayerError("first stream is not video");
96 st
.rawReadExact(sign
[]);
97 if (sign
!= "ZMBV") throw new PlayerError("first stream is ZMBV");
98 // now look for actual data
101 st
.rawReadExact(sign
[]);
102 sz
= st
.readNum
!uint();
104 if (sz
>= 3*4 && sign
== "LIST") {
105 st
.rawReadExact(sign
[]);
106 if (sign
== "movi") {
115 writeln("FPS: ", fps
);
116 writeln("hasIndex: ", hasIndex
);
117 writeln("framesTotal: ", framesTotal
);
118 writeln(videoW
, "x", videoH
);
120 zmd
= new Decoder(videoW
, videoH
);
127 bool videoDone
= false;
131 bool loadNextFrame(ST
) (auto ref ST st
) if (isReadableStream
!ST
&& isSeekableStream
!ST
) {
132 //writeln(moviePos, " ", movieOfs+movieSize);
133 while (moviePos
< movieOfs
+movieSize
) {
136 st
.rawReadExact(sign
[]);
137 uint sz
= st
.readNum
!uint();
138 //writeln(sz, " ", sign);
140 if (moviePos
&0x01) ++moviePos
;
141 if (sign
== "00dc") {
142 if (encframe
.length
< sz
) encframe
.length
= sz
;
143 st
.rawReadExact(encframe
[0..sz
]);
144 zmd
.decodeFrame(encframe
[0..sz
]);
145 //writeln(zmd.width, "x", zmd.height, " : ", zmd.format);
146 if (zmd
.format
!= Codec
.Format
.bpp8
&& zmd
.format
!= Codec
.Format
.bpp32
) throw new PlayerError("invalid frame format");
147 //if (zmd.width != videoW || zmd.height != videoH) throw new PlayerError("frame size changes are not supported yet");
148 if (zmd
.paletteChanged
) {
149 auto pal
= zmd
.palette
;
150 foreach (immutable idx
; 0..256) framepal
[idx
] = rgb2col(pal
[idx
*3+0], pal
[idx
*3+1], pal
[idx
*3+2]);
160 ////////////////////////////////////////////////////////////////////////////////
161 void realizeFrame () {
162 foreach (immutable y
; 0..zmd
.height
) {
163 auto src
= zmd
.line(y
).ptr
;
164 auto dst
= vlVScr
+y
*vlWidth
;
165 uint wdt
= zmd
.width
;
166 if (wdt
> vlWidth
) wdt
= vlWidth
;
167 if (zmd
.format
== Codec
.Format
.bpp8
) {
168 foreach (immutable x
; 0..wdt
) *dst
++ = framepal
[*src
++];
170 import core
.stdc
.string
: memcpy
;
171 memcpy(dst
, src
, wdt
*4);
177 private void updateCB (int elapsedTicks
) {
178 if (!videoDone
&& !paused
) videoDone
= !loadNextFrame(aviFl
);
179 vlsOvl
.fillRect(0, 0, vlsOvl
.width
, vlsOvl
.height
, 0);
182 if (curFrame
) vlsOvl
.hline(0, 0, cast(uint)(cast(ulong)vlsOvl
.width
*curFrame
/framesTotal
), rgb2col(0, 255, 0));
183 if (paused
) vlsOvl
.drawStrPropOut(3, 3, "paused", rgb2col(255, 127, 0), 0);
185 vlsOvl
.drawStrPropOut(3, 3, "DONE!", rgb2col(255, 0, 0), 0);
191 ////////////////////////////////////////////////////////////////////////////////
192 private void keyDownCB (in ref SDL_KeyboardEvent ev
) {
193 if (ev
.keysym
.sym
== SDLK_RETURN
&& (ev
.keysym
.mod
&KMOD_ALT
)) { vlSwitchFullscreen(); return; }
194 if (ev
.keysym
.sym
== SDLK_ESCAPE
) { vlPostQuitMessage(); return; }
195 switch (ev
.keysym
.sym
) {
196 case SDLK_SPACE
: paused
= !paused
; break;
198 case SDLK_UP: case SDLK_KP_8:
199 case SDLK_LEFT: case SDLK_KP_4:
201 if (num > 0) num -= 2;
203 if (findByNum(--num) == uint.max) ++num;
206 case SDLK_DOWN: case SDLK_KP_2:
207 case SDLK_RIGHT: case SDLK_KP_6:
209 if (findByNum(num+1) != uint.max && findByNum(num+2) != uint.max) {
212 if (findByNum(num+1) != uint.max) { import iv.writer; writeln("extra sprite!"); }
215 if (findByNum(++num) == uint.max) --num;
224 ////////////////////////////////////////////////////////////////////////////////
225 void main (string
[] args
) {
227 if (args
.length
!= 2) {
232 aviFl
= File(args
[1]);
236 if (videoW
> 320 || videoH
> 400) vlMag2x
= false;
238 vlInit("ZMBV Player/SDL");
239 } catch (Throwable e
) {
241 writeln("FATAL: ", e
.msg
);
245 vlOnUpdate
= &updateCB
;
246 vlOnKeyDown
= &keyDownCB
;