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