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