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