]> icculus.org git repositories - taylor/freespace2.git/blob - src/movie/mveplayer.cpp
fix audio buffer cleanup
[taylor/freespace2.git] / src / movie / mveplayer.cpp
1 /*
2  * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * MVE movie playing routines
8  *
9  * $Log$
10  * Revision 1.7  2005/10/01 21:48:01  taylor
11  * various cleanups
12  * fix decoder to swap opcode 0xb since it screws up on PPC
13  * the previous opcode 0xc change was wrong since we had already determined that it messes up FS1 movies
14  *
15  * Revision 1.6  2005/08/12 08:47:24  taylor
16  * use new audiostr code rather than old windows/unix version
17  * update all OpenAL commands with new error checking macros
18  * fix play_position to properly account for real position, fixes the talking heads and message text cutting out early
19  * movies will now use better filtering when scaled
20  *
21  * Revision 1.5  2005/03/31 21:26:02  taylor
22  * s/alGetSourceiv/alGetSourcei/
23  *
24  * Revision 1.4  2005/03/31 00:06:20  taylor
25  * go back to more accurate timer and allow video scaling for movies
26  *
27  * Revision 1.3  2005/03/29 07:50:34  taylor
28  * Update to newest movie code with much better video support and audio support from
29  *   Pierre Willenbrock.  Movies are enabled always now (no longer a build option)
30  *   and but can be skipped with the "--nomovies" or "-n" cmdline options.
31  *
32  *
33  * $NoKeywords: $
34  */
35  
36 #include "pstypes.h"
37 #include "mvelib.h"
38 #include "movie.h"
39 #include "2d.h"
40 #include "key.h"
41 #include "osapi.h"
42 #include "timer.h"
43 #include "sound.h"
44 #include "bmpman.h"
45 #include "osregistry.h"
46 #include "oal.h"
47 #include "gropengl.h"
48 #include <vector>
49
50 static int mve_playing;
51
52
53 // timer variables
54 static int micro_frame_delay = 0;
55 static int timer_started = 0;
56 static int timer_created = 0;
57 static int timer_expire;
58 static Uint64 micro_timer_start = 0;
59 static Uint64 micro_timer_freq = 0;
60
61 // audio variables
62 #define MVE_AUDIO_BUFFERS 8  // total buffers to interact with stream
63
64 static std::vector<ALuint> mve_audio_bufl_free;
65 static ubyte *mve_audio_buf = NULL;
66 static size_t mve_audio_buf_size = 0;
67 static size_t mve_audio_buf_offset = 0;
68
69 static int mve_audio_playing = 0;
70 static int mve_audio_canplay = 0;
71 static int mve_audio_compressed = 0;
72 static int audiobuf_created = 0;
73
74 // struct for the audio stream information
75 struct mve_audio_t {
76         sound_channel *chan;
77         ALenum format;
78         int sample_rate;
79         int bytes_per_sec;
80         int channels;
81         int bitsize;
82         ALuint buffers[MVE_AUDIO_BUFFERS];
83 };
84
85 mve_audio_t *mas = NULL;  // mve_audio_stream
86
87
88
89 // video variables
90 int g_width, g_height;
91 void *g_vBuffers = NULL;
92 void *g_vBackBuf1, *g_vBackBuf2;
93 ushort *pixelbuf = NULL;
94 static int g_screenWidth, g_screenHeight;
95 static ubyte g_palette[768];
96 static ubyte *g_pCurMap=NULL;
97 static int g_nMapLength=0;
98 static int videobuf_created, video_inited;
99 static int hp2, wp2;
100 static uint mve_video_skiptimer = 0;
101 static int mve_scale_video = 0;
102 static GLuint tex = 0;
103
104 // the decoder
105 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
106
107 /*************************
108  * general handlers
109  *************************/
110 void mve_end_movie()
111 {
112         mve_playing = 0;
113 }
114
115 /*************************
116  * timer handlers
117  *************************/
118
119 int mve_timer_create(ubyte *data)
120 {
121         micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
122
123         micro_timer_start = SDL_GetPerformanceCounter();
124         micro_timer_freq = SDL_GetPerformanceFrequency();
125
126         if (micro_timer_freq < 1000) {
127                 micro_timer_freq = 1000;
128         }
129
130         timer_created = 1;
131
132         return 1;
133 }
134
135 static int mve_timer_get_microseconds()
136 {
137
138         Uint64 us = SDL_GetPerformanceCounter() - micro_timer_start;
139
140         if (micro_timer_freq >= 1000000) {
141                 us /= (micro_timer_freq / 1000000);
142         } else {
143                 us *= (1000000 / micro_timer_freq);
144         }
145
146         return (int)us;
147 }
148
149 static void mve_timer_start(void)
150 {
151         if (!timer_created)
152                 return;
153
154         timer_expire = mve_timer_get_microseconds();
155         timer_expire += micro_frame_delay;
156
157         timer_started = 1;
158 }
159
160 static int mve_do_timer_wait(void)
161 {
162         if (!timer_started)
163                 return 0;
164
165         int tv, ts;
166
167         tv = mve_timer_get_microseconds();
168
169         if (tv > timer_expire)
170                 goto end;
171
172         ts = timer_expire - tv;
173
174         SDL_Delay(ts / 1000);
175
176         // try and burn off excess in attempt to keep sync
177         if (ts % 1000) {
178                 for (int i = 0; i < 10; i++) {
179                         SDL_Delay(0);
180                 }
181         }
182 end:
183         timer_expire += micro_frame_delay;
184
185         return 0;
186 }
187
188 static void mve_timer_stop()
189 {
190         timer_expire = 0;
191         timer_started = 0;
192         timer_created = 0;
193
194         micro_frame_delay = 0;
195
196         micro_timer_start = 0;
197         micro_timer_freq = 0;
198 }
199
200 /*************************
201  * audio handlers
202  *************************/
203
204 // setup the audio information from the data stream
205 void mve_audio_createbuf(ubyte minor, ubyte *data)
206 {
207         if (audiobuf_created)
208                 return;
209
210         // if game sound disabled don't try and play movie audio
211         if ( !Sound_enabled ) {
212                 mve_audio_canplay = 0;
213                 audiobuf_created = 1;
214                 return;
215         }
216
217         int flags, desired_buffer, sample_rate;
218
219         mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
220
221         if (mas == NULL) {
222                 mve_audio_canplay = 0;
223                 audiobuf_created = 1;
224                 return;
225         }
226
227         memset(mas, 0, sizeof(mve_audio_t));
228
229         mas->format = AL_INVALID;
230
231         flags = mve_get_ushort(data + 2);
232         sample_rate = mve_get_ushort(data + 4);
233         desired_buffer = mve_get_int(data + 6);
234
235         if (desired_buffer > 0) {
236                 mve_audio_buf = (ubyte*) malloc (desired_buffer);
237
238                 if (mve_audio_buf == NULL) {
239                         mve_audio_canplay = 0;
240                         audiobuf_created = 1;
241                         return;
242                 }
243
244                 mve_audio_buf_size = desired_buffer;
245                 mve_audio_buf_offset = 0;
246         } else {
247                 mve_audio_canplay = 0;
248                 audiobuf_created = 1;
249                 return;
250         }
251
252         mas->channels = (flags & 0x0001) ? 2 : 1;
253         mas->bitsize = (flags & 0x0002) ? 16 : 8;
254
255         mas->sample_rate = sample_rate;
256
257         if (minor > 0) {
258                 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
259         } else {
260                 mve_audio_compressed = 0;
261         }
262
263         if (mas->bitsize == 16) {
264                 if (mas->channels == 2) {
265                         mas->format = AL_FORMAT_STEREO16;
266                 } else if (mas->channels == 1) {
267                         mas->format = AL_FORMAT_MONO16;
268                 }
269         } else if (mas->bitsize == 8) {
270                 if (mas->channels == 2) {
271                         mas->format = AL_FORMAT_STEREO8;
272                 } else if (mas->channels == 1) {
273                         mas->format = AL_FORMAT_MONO8;
274                 }
275         }
276
277         // somethings wrong, bail now
278         if (mas->format == AL_INVALID) {
279                 mve_audio_canplay = 0;
280                 audiobuf_created = 1;
281                 return;
282         }
283
284         oal_check_for_errors("mve_audio_createbuf() begin");
285
286         for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
287                 alGenBuffers(1, &mas->buffers[i]);
288
289                 if ( !mas->buffers[i] ) {
290                         mve_audio_canplay = 0;
291                         audiobuf_created = 1;
292                         return;
293                 }
294         }
295
296         mve_audio_bufl_free.assign(mas->buffers, mas->buffers+MVE_AUDIO_BUFFERS);
297
298         mas->chan = oal_get_free_channel(1.0f, -1, SND_PRIORITY_MUST_PLAY);
299
300         if (mas->chan == NULL) {
301                 mve_audio_canplay = 0;
302                 audiobuf_created = 1;
303                 return;
304         }
305
306         alSourcef(mas->chan->source_id, AL_GAIN, 1.0f);
307         alSource3f(mas->chan->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
308         alSource3f(mas->chan->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
309         alSource3f(mas->chan->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
310         alSourcef(mas->chan->source_id, AL_ROLLOFF_FACTOR, 0.0f);
311         alSourcei(mas->chan->source_id, AL_SOURCE_RELATIVE, AL_TRUE);
312
313         oal_check_for_errors("mve_audio_createbuf() end");
314
315         audiobuf_created = 1;
316         mve_audio_canplay = 1;
317 }
318
319 // play and stream the audio
320 void mve_audio_play()
321 {
322         if (mve_audio_canplay) {
323                 ALint queued = 0;
324                 ALint status = AL_INVALID;
325
326                 oal_check_for_errors("mve_audio_play() begin");
327
328                 alGetSourcei(mas->chan->source_id, AL_BUFFERS_QUEUED, &queued);
329                 alGetSourcei(mas->chan->source_id, AL_SOURCE_STATE, &status);
330
331                 if ( (status != AL_PLAYING) && (queued > 0) ) {
332                         alSourcePlay(mas->chan->source_id);
333                         mve_audio_playing = 1;
334                 }
335
336                 oal_check_for_errors("mve_audio_play() end");
337         }
338 }
339
340 // call this in shutdown to stop and close audio
341 static void mve_audio_stop()
342 {
343         if (!audiobuf_created)
344                 return;
345
346         oal_check_for_errors("mve_audio_stop() begin");
347
348         mve_audio_playing = 0;
349         mve_audio_canplay = 0;
350         mve_audio_compressed = 0;
351
352         audiobuf_created = 0;
353
354         mve_audio_bufl_free.clear();
355
356         if (mas) {
357                 if (mas->chan) {
358                         alSourceStop(mas->chan->source_id);
359
360                         // detach buffers from source so that we can delete them
361                         alSourcei(mas->chan->source_id, AL_BUFFER, 0);
362                 }
363
364                 for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
365                         if ( alIsBuffer(mas->buffers[i]) ) {
366                                 alDeleteBuffers(1, &mas->buffers[i]);
367                         }
368                 }
369         }
370
371         if (mas != NULL) {
372                 free(mas);
373                 mas = NULL;
374         }
375
376         if (mve_audio_buf != NULL) {
377                 free(mve_audio_buf);
378                 mve_audio_buf = NULL;
379         }
380
381         mve_audio_buf_size = 0;
382         mve_audio_buf_offset = 0;
383
384         oal_check_for_errors("mve_audio_stop() end");
385 }
386
387 int mve_audio_data(ubyte major, ubyte *data)
388 {
389         static const int selected_chan = 1;
390         int chan;
391         int nsamp;
392         ALint processed = 0;
393         ALuint bid;
394
395         if (mve_audio_canplay) {
396                 chan = mve_get_ushort(data + 2);
397                 nsamp = mve_get_ushort(data + 4);
398
399                 if (chan & selected_chan) {
400                         oal_check_for_errors("mve_audio_data() begin");
401
402                         if ( (mve_audio_buf_offset+nsamp+4) <= mve_audio_buf_size ) {
403                                 if (major == 8) {
404                                         if (mve_audio_compressed) {
405                                                 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
406                                                 nsamp += 4;
407
408                                                 mveaudio_uncompress(mve_audio_buf+mve_audio_buf_offset, data, -1);
409                                         } else {
410                                                 nsamp -= 8;
411                                                 data += 8;
412
413                                                 memcpy(mve_audio_buf+mve_audio_buf_offset, data, nsamp);
414                                         }
415                                 } else {
416                                         // silence
417                                         memset(mve_audio_buf+mve_audio_buf_offset, 0, nsamp);
418                                 }
419
420                                 mve_audio_buf_offset += nsamp;
421                         } else {
422                                 mprintf(("MVE audio_buf overrun!!\n"));
423                         }
424
425                         alGetSourcei(mas->chan->source_id, AL_BUFFERS_PROCESSED, &processed);
426
427                         while (processed) {
428                                 alSourceUnqueueBuffers(mas->chan->source_id, 1, &bid);
429
430                                 mve_audio_bufl_free.push_back(bid);
431                                 --processed;
432                         }
433
434                         if ( !mve_audio_bufl_free.empty() ) {
435                                 bid = mve_audio_bufl_free.back();
436
437                                 alBufferData(bid, mas->format, mve_audio_buf, mve_audio_buf_offset, mas->sample_rate);
438                                 alSourceQueueBuffers(mas->chan->source_id, 1, &bid);
439
440                                 mve_audio_buf_offset = 0;
441                                 mve_audio_bufl_free.pop_back();
442                         }
443
444                         if ( !mve_audio_playing ) {
445                                 mve_audio_play();
446                         }
447
448                         oal_check_for_errors("mve_audio_data() end");
449                 }
450         }
451
452         return 1;
453 }
454
455 /*************************
456  * video handlers
457  *************************/
458
459 int mve_video_createbuf(ubyte minor, ubyte *data)
460 {
461         if (videobuf_created)
462                 return 1;
463
464         short w, h;
465         short count, truecolor;
466         w = mve_get_short(data);
467         h = mve_get_short(data+2);
468         
469         if (minor > 0) {
470                 count = mve_get_short(data+4);
471         } else {
472                 count = 1;
473         }
474         
475         if (minor > 1) {
476                 truecolor = mve_get_short(data+6);
477         } else {
478                 truecolor = 0;
479         }
480
481         g_width = w << 3;
482         g_height = h << 3;
483
484         // with Pierre's decoder16 fix in opcode 0xc, 8 should no longer be needed
485         g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 4);
486
487         if (g_vBackBuf1 == NULL) {
488                 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
489                 videobuf_created = 1;
490                 return 0;
491         }
492
493         g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
494                 
495         memset(g_vBackBuf1, 0, g_width * g_height * 4);
496
497         videobuf_created = 1;
498
499         return 1;
500 }
501
502 static void mve_convert_and_draw()
503 {
504         ushort *pDests;
505         ushort *pSrcs;
506         ushort *pixels = (ushort *)g_vBackBuf1;
507         int x, y;
508
509         pSrcs = pixels;
510
511         pDests = pixelbuf;
512
513         if (g_screenWidth > g_width) {
514                 pDests += ((g_screenWidth - g_width) / 2) / 2;
515         }
516         if (g_screenHeight > g_height) {
517                 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
518         }
519
520         for (y=0; y<g_height; y++) {
521                 for (x = 0; x < g_width; x++) {
522                         pDests[x] = (1<<15)|*pSrcs;
523
524                         pSrcs++;
525                 }
526                 pDests += g_screenWidth;
527         }
528 }
529
530 void mve_video_display()
531 {
532         fix t1 = timer_get_fixed_seconds();
533         mve_convert_and_draw();
534
535         int x, y;
536         int h = g_screenHeight;
537         int w = g_screenWidth;
538
539 #ifdef PLAT_UNIX
540         if (mve_scale_video) {
541                 x = y = 0;
542         } else {
543                 // centers on 1024x768, fills on 640x480
544                 x = ((gr_screen.max_w - g_screenWidth) / 2);
545                 y = ((gr_screen.max_h - g_screenHeight) / 2);
546         }
547
548         // micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
549         if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
550                 // we are running slow so subtract desired time from actual and skip this frame
551                 mve_video_skiptimer -= (micro_frame_delay/10);
552                 return;
553         } else {
554                 // zero out so we can get a new count
555                 mve_video_skiptimer = 0;
556         }
557
558         glBindTexture(GL_TEXTURE_2D, tex);
559         
560         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
561
562         // 0, 0
563         glBegin(GL_QUADS);
564                 glTexCoord2f(0,0);                                                                              glVertex2i(x,y);
565                 glTexCoord2f(0,i2fl(256)/i2fl(hp2));                                    glVertex2i(x,y+256);
566                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
567                 glTexCoord2f(i2fl(256)/i2fl(wp2),0);                                    glVertex2i(x+256,y);
568         glEnd();
569
570         // 0, 256
571         glBegin(GL_QUADS);
572                 glTexCoord2f(0,i2fl(256)/i2fl(hp2));                                    glVertex2i(x,y+256);
573                 glTexCoord2f(0,i2fl(h)/i2fl(hp2));                                              glVertex2i(x,y+h);
574                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+256,y+h);
575                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
576         glEnd();
577
578         // 256, 0
579         glBegin(GL_QUADS);
580                 glTexCoord2f(i2fl(256)/i2fl(wp2),0);                                    glVertex2i(x+256,y);
581                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
582                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
583                 glTexCoord2f(i2fl(512)/i2fl(wp2),0);                                    glVertex2i(x+512,y);
584         glEnd();
585
586         // 256, 256
587         glBegin(GL_QUADS);
588                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
589                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+256,y+h);
590                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+512,y+h);
591                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
592         glEnd();
593
594         // 512, 0
595         glBegin(GL_QUADS);
596                 glTexCoord2f(i2fl(512)/i2fl(wp2),0);                                    glVertex2i(x+512,y);
597                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
598                 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2));    glVertex2i(x+w,y+256);
599                 glTexCoord2f(i2fl(w)/i2fl(wp2),0);                                              glVertex2i(x+w,y);
600         glEnd();
601
602         // 512, 256
603         glBegin(GL_QUADS);
604                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
605                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+512,y+h);
606                 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(h)/i2fl(hp2));              glVertex2i(x+w,y+h);
607                 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2));    glVertex2i(x+w,y+256);
608         glEnd();
609 #else
610         // centers on 1024x768, fills on 640x480
611         x = ((gr_screen.max_w - g_screenWidth) / 2);
612         y = ((gr_screen.max_h - g_screenHeight) / 2);
613
614         // DDOI - This is probably really fricking slow
615         int bitmap = bm_create (16, w, h, pixelbuf, 0);
616         gr_set_bitmap (bitmap);
617         gr_bitmap (x, y);
618         bm_release (bitmap);
619 #endif
620
621         gr_flip ();
622 #ifdef PLAT_UNIX
623         os_poll ();     /* DDOI - run event loop(s) */
624 #endif
625
626         fix t2 = timer_get_fixed_seconds();
627
628         // only get a new count if we are definitely through with old count
629         if ( mve_video_skiptimer == 0 ) {
630                 // for a more accurate count convert the frame rate to a float and multiply
631                 // by one-hundred-thousand before converting to an uint.
632                 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
633         }
634
635         int k = key_inkey();
636         if ( k == SDLK_ESCAPE ) {
637                 mve_playing = 0;
638         }
639
640 //      fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
641 }
642
643 int mve_video_init(ubyte *data)
644 {
645         if (video_inited)
646                 return 1;
647
648         short width, height;
649
650         width = mve_get_short(data);
651         height = mve_get_short(data+2);
652
653         // DDOI - Allocate RGB565 pixel buffer
654         pixelbuf = (ushort *)malloc (width * height * 2);
655
656         if (pixelbuf == NULL) {
657                 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
658                 video_inited = 1;
659                 return 0;
660         }
661
662         memset(pixelbuf, 0, width * height * 2);
663
664         g_screenWidth = width;
665         g_screenHeight = height;
666
667 #ifdef PLAT_UNIX
668         int i, tex_w, tex_h;
669
670         tex_w = g_screenWidth;
671         tex_h = g_screenHeight;
672
673         // set height and width to a power of 2
674         for (i=0; i<16; i++ )   {
675                 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) )        {
676                         tex_w = 1 << (i+1);
677                         break;
678                 }
679         }
680
681         for (i=0; i<16; i++ )   {
682                 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) )        {
683                         tex_h = 1 << (i+1);
684                         break;
685                 }
686         }
687
688         // try to keep an 8:1 size ratio
689         if (tex_w/tex_h > 8)
690                 tex_h = tex_w/8;
691         if (tex_h/tex_w > 8)
692                 tex_w = tex_h/8;
693
694         wp2 = tex_w;
695         hp2 = tex_h;
696
697         glGenTextures(1, &tex);
698
699         SDL_assert(tex != 0);
700
701         if ( tex == 0 ) {
702                 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
703                 video_inited = 1;
704                 return 0;
705         }
706
707         glBindTexture(GL_TEXTURE_2D, tex);
708         
709         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
710         glDepthFunc(GL_ALWAYS);
711         glDepthMask(GL_FALSE);
712         glDisable(GL_DEPTH_TEST);
713         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
714         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
715
716         if ( os_config_read_uint(NULL, NOX("ScaleMovies"), 0) == 1 ) {
717                 float scale_by = (float)gr_screen.max_w / (float)g_screenWidth;
718
719                 // don't bother setting anything if we aren't going to need it
720                 if (scale_by != 1.0f) {
721                         glMatrixMode(GL_MODELVIEW);
722                         glPushMatrix();
723                         glLoadIdentity();
724
725                         glScalef( scale_by, scale_by, 1.0f );
726                         mve_scale_video = 1;
727                 }
728         }
729
730         // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf so less of two evils...
731         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, wp2, hp2, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
732 #endif
733
734         memset(g_palette, 0, 768);
735         
736         video_inited = 1;
737         
738         return 1;
739 }
740
741 void mve_video_palette(ubyte *data)
742 {
743         short start, count;
744         start = mve_get_short(data);
745         count = mve_get_short(data+2);
746         memcpy(g_palette + 3*start, data+4, 3*count);
747 }
748
749 void mve_video_codemap(ubyte *data, int len)
750 {
751         g_pCurMap = data;
752         g_nMapLength = len;
753 }
754
755 void mve_video_data(ubyte *data, int len)
756 {
757         short nFrameHot, nFrameCold;
758         short nXoffset, nYoffset;
759         short nXsize, nYsize;
760         ushort nFlags;
761         ubyte *temp;
762
763         nFrameHot = mve_get_short(data);
764         nFrameCold = mve_get_short(data+2);
765         nXoffset = mve_get_short(data+4);
766         nYoffset = mve_get_short(data+6);
767         nXsize = mve_get_short(data+8);
768         nYsize = mve_get_short(data+10);
769         nFlags = mve_get_ushort(data+12);
770
771         if (nFlags & 1) {
772                 temp = (ubyte *)g_vBackBuf1;
773                 g_vBackBuf1 = g_vBackBuf2;
774                 g_vBackBuf2 = temp;
775         }
776
777         decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
778 }
779
780 void mve_end_chunk()
781 {
782         g_pCurMap = NULL;
783 }
784
785 void mve_init(MVESTREAM *mve)
786 {
787         // reset to default values
788         mve_audio_playing = 0;
789         mve_audio_canplay = 0;
790         mve_audio_compressed = 0;
791         mve_audio_buf_offset = 0;
792         audiobuf_created = 0;
793
794         videobuf_created = 0;
795         video_inited = 0;
796         mve_scale_video = 0;
797
798         mve_playing = 1;
799 }
800
801 void mve_play(MVESTREAM *mve)
802 {
803         int init_timer = 0, timer_error = 0;
804         int cont = 1;
805
806         if (!timer_started)
807                 mve_timer_start();
808
809         while (cont && mve_playing && !timer_error) {
810                 cont = mve_play_next_chunk(mve);
811
812                 if (micro_frame_delay && !init_timer) {
813                         mve_timer_start();
814                         init_timer = 1;
815                 }
816
817                 timer_error = mve_do_timer_wait();
818         }
819 }
820
821 void mve_shutdown()
822 {
823         mve_audio_stop();
824
825         mve_timer_stop();
826
827         if (pixelbuf != NULL) {
828                 free(pixelbuf);
829                 pixelbuf = NULL;
830         }
831
832         if (g_vBuffers != NULL) {
833                 free(g_vBuffers);
834                 g_vBuffers = NULL;
835         }
836
837 #ifdef PLAT_UNIX
838         if (mve_scale_video) {
839                 glMatrixMode(GL_MODELVIEW);
840                 glPopMatrix();
841                 glLoadIdentity();
842         }
843
844         glDeleteTextures(1, &tex);
845         tex = 0;
846
847         glEnable(GL_DEPTH_TEST);
848 #endif
849 }