2 * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
7 * MVE movie playing routines
10 * Revision 1.7 2005/10/01 21:48:01 taylor
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
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
21 * Revision 1.5 2005/03/31 21:26:02 taylor
22 * s/alGetSourceiv/alGetSourcei/
24 * Revision 1.4 2005/03/31 00:06:20 taylor
25 * go back to more accurate timer and allow video scaling for movies
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.
51 #include "osregistry.h"
56 static int mve_playing;
60 static int micro_frame_delay = 0;
61 static int timer_started = 0;
62 static int timer_created = 0;
63 static int timer_expire;
64 static Uint64 micro_timer_start = 0;
65 static Uint64 micro_timer_freq = 0;
68 #define MVE_AUDIO_BUFFERS 64 // total buffers to interact with stream
69 static int mve_audio_curbuf_curpos = 0;
70 static int mve_audio_bufhead = 0;
71 static int mve_audio_buftail = 0;
72 static int mve_audio_playing = 0;
73 static int mve_audio_canplay = 0;
74 static int mve_audio_compressed = 0;
75 static int audiobuf_created;
79 // struct for the audio stream information
80 typedef struct MVE_AUDIO_T {
86 ALuint audio_data[MVE_AUDIO_BUFFERS];
88 ALuint audio_buffer[MVE_AUDIO_BUFFERS];
91 mve_audio_t *mas; // mve_audio_stream
97 int g_width, g_height;
98 void *g_vBuffers = NULL;
99 void *g_vBackBuf1, *g_vBackBuf2;
100 ushort *pixelbuf = NULL;
101 static int g_screenWidth, g_screenHeight;
102 static ubyte g_palette[768];
103 static ubyte *g_pCurMap=NULL;
104 static int g_nMapLength=0;
105 static int videobuf_created, video_inited;
107 static uint mve_video_skiptimer = 0;
108 static int mve_scale_video = 0;
110 static GLuint tex = 0;
114 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
116 /*************************
118 *************************/
124 /*************************
126 *************************/
128 int mve_timer_create(ubyte *data)
130 micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
132 micro_timer_start = SDL_GetPerformanceCounter();
133 micro_timer_freq = SDL_GetPerformanceFrequency();
135 if (micro_timer_freq < 1000) {
136 micro_timer_freq = 1000;
144 static int mve_timer_get_microseconds()
147 Uint64 us = SDL_GetPerformanceCounter() - micro_timer_start;
149 if (micro_timer_freq >= 1000000) {
150 us /= (micro_timer_freq / 1000000);
152 us *= (1000000 / micro_timer_freq);
158 static void mve_timer_start(void)
163 timer_expire = mve_timer_get_microseconds();
164 timer_expire += micro_frame_delay;
169 static int mve_do_timer_wait(void)
176 tv = mve_timer_get_microseconds();
178 if (tv > timer_expire)
181 ts = timer_expire - tv;
185 // try and burn off excess in attempt to keep sync
187 for (int i = 0; i < 10; i++) {
192 timer_expire += micro_frame_delay;
197 static void mve_timer_stop()
203 micro_timer_start = 0;
204 micro_timer_freq = 0;
207 /*************************
209 *************************/
211 // setup the audio information from the data stream
212 void mve_audio_createbuf(ubyte minor, ubyte *data)
214 if (audiobuf_created)
217 // if game sound disabled don't try and play movie audio
218 if (!Sound_enabled) {
219 mve_audio_canplay = 0;
224 int flags, desired_buffer, sample_rate;
226 mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
227 memset(mas, 0, sizeof(mve_audio_t));
229 mas->format = AL_INVALID;
231 flags = mve_get_ushort(data + 2);
232 sample_rate = mve_get_ushort(data + 4);
233 desired_buffer = mve_get_int(data + 6);
235 mas->channels = (flags & 0x0001) ? 2 : 1;
236 mas->bitsize = (flags & 0x0002) ? 16 : 8;
238 mas->sample_rate = sample_rate;
241 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
243 mve_audio_compressed = 0;
246 if (mas->bitsize == 16) {
247 if (mas->channels == 2) {
248 mas->format = AL_FORMAT_STEREO16;
249 } else if (mas->channels == 1) {
250 mas->format = AL_FORMAT_MONO16;
252 } else if (mas->bitsize == 8) {
253 if (mas->channels == 2) {
254 mas->format = AL_FORMAT_STEREO8;
255 } else if (mas->channels == 1) {
256 mas->format = AL_FORMAT_MONO8;
260 // somethings wrong, bail now
261 if (mas->format == AL_INVALID) {
262 mve_audio_canplay = 0;
263 audiobuf_created = 1;
267 OpenAL_ErrorCheck( alGenSources(1, &mas->source_id), { mve_audio_canplay = 0; return; } );
269 mve_audio_canplay = 1;
271 OpenAL_ErrorPrint( alSourcef(mas->source_id, AL_GAIN, 1.0f) );
272 OpenAL_ErrorPrint( alSource3f(mas->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f) );
273 OpenAL_ErrorPrint( alSource3f(mas->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f) );
274 OpenAL_ErrorPrint( alSource3f(mas->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f) );
275 OpenAL_ErrorPrint( alSourcef(mas->source_id, AL_ROLLOFF_FACTOR, 0.0f ) );
276 OpenAL_ErrorPrint( alSourcei(mas->source_id, AL_SOURCE_RELATIVE, AL_TRUE ) );
278 memset(mas->audio_buffer, 0, MVE_AUDIO_BUFFERS * sizeof(ALuint));
280 mve_audio_bufhead = 0;
281 mve_audio_buftail = 0;
283 audiobuf_created = 1;
287 // play and stream the audio
288 void mve_audio_play()
291 if (mve_audio_canplay) {
292 ALint status, bqueued;
294 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status), return );
296 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued), return );
298 mve_audio_playing = 1;
300 if (status != AL_PLAYING && bqueued > 0) {
301 OpenAL_ErrorPrint( alSourcePlay(mas->source_id) );
307 // call this in shutdown to stop and close audio
308 static void mve_audio_stop()
310 if (!audiobuf_created)
316 mve_audio_playing = 0;
318 OpenAL_ErrorPrint( alSourceStop(mas->source_id) );
319 OpenAL_ErrorPrint( alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &p) );
320 OpenAL_ErrorPrint( alSourceUnqueueBuffers(mas->source_id, p, mas->audio_buffer) );
321 OpenAL_ErrorPrint( alDeleteBuffers(MVE_AUDIO_BUFFERS, mas->audio_buffer) );
322 OpenAL_ErrorPrint( alDeleteSources(1, &mas->source_id) );
331 int mve_audio_data(ubyte major, ubyte *data)
334 static const int selected_chan=1;
338 if (mve_audio_canplay) {
339 chan = mve_get_ushort(data + 2);
340 nsamp = mve_get_ushort(data + 4);
342 if (chan & selected_chan) {
343 ALint bprocessed, bqueued, status;
346 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &bprocessed), return 0 );
348 while (bprocessed-- > 2) {
349 OpenAL_ErrorPrint( alSourceUnqueueBuffers(mas->source_id, 1, &bid) );
350 // fprintf(stderr,"Unqueued buffer %d(%d)\n", mve_audio_bufhead, bid);
352 if (++mve_audio_bufhead == MVE_AUDIO_BUFFERS)
353 mve_audio_bufhead = 0;
356 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued), return 0 );
359 mprintf(("MVE: Buffer underun (First is normal)\n"));
361 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status), return 0 );
363 if (mve_audio_playing && status != AL_PLAYING && bqueued > 0) {
364 OpenAL_ErrorCheck( alSourcePlay(mas->source_id), return 0 );
367 if (bqueued < MVE_AUDIO_BUFFERS) {
370 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
372 if (mve_audio_compressed) {
375 buf = (short *)malloc(nsamp);
376 mveaudio_uncompress(buf, data, -1); /* XXX */
381 buf = (short *)malloc(nsamp);
382 memcpy(buf, data, nsamp);
385 buf = (short *)malloc(nsamp);
387 memset(buf, 0, nsamp); /* XXX */
391 if (!mas->audio_buffer[mve_audio_buftail]) {
392 OpenAL_ErrorCheck( alGenBuffers(1,&mas->audio_buffer[mve_audio_buftail]), { free(buf); return 0; } );
395 OpenAL_ErrorCheck( alBufferData(mas->audio_buffer[mve_audio_buftail], mas->format, buf, nsamp, mas->sample_rate), { free(buf); return 0; } );
397 OpenAL_ErrorCheck( alSourceQueueBuffers(mas->source_id, 1, &mas->audio_buffer[mve_audio_buftail]), { free(buf); return 0;} );
399 //fprintf(stderr,"Queued buffer %d(%d)\n", mve_audio_buftail, mas->audio_buffer[mve_audio_buftail]);
401 if (++mve_audio_buftail == MVE_AUDIO_BUFFERS)
402 mve_audio_buftail = 0;
407 mprintf(("MVE: Buffer overrun: Queue full\n"));
410 //fprintf(stderr,"Buffers queued: %d\n", bqueued);
418 /*************************
420 *************************/
422 int mve_video_createbuf(ubyte minor, ubyte *data)
424 if (videobuf_created)
428 short count, truecolor;
429 w = mve_get_short(data);
430 h = mve_get_short(data+2);
433 count = mve_get_short(data+4);
439 truecolor = mve_get_short(data+6);
447 // with Pierre's decoder16 fix in opcode 0xc, 8 should no longer be needed
448 g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 4);
450 if (g_vBackBuf1 == NULL) {
451 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
452 videobuf_created = 1;
456 g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
458 memset(g_vBackBuf1, 0, g_width * g_height * 4);
460 videobuf_created = 1;
465 static void mve_convert_and_draw()
469 ushort *pixels = (ushort *)g_vBackBuf1;
476 if (g_screenWidth > g_width) {
477 pDests += ((g_screenWidth - g_width) / 2) / 2;
479 if (g_screenHeight > g_height) {
480 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
483 for (y=0; y<g_height; y++) {
484 for (x = 0; x < g_width; x++) {
485 pDests[x] = (1<<15)|*pSrcs;
489 pDests += g_screenWidth;
493 void mve_video_display()
495 fix t1 = timer_get_fixed_seconds();
496 mve_convert_and_draw();
499 int h = g_screenHeight;
500 int w = g_screenWidth;
503 if (mve_scale_video) {
506 // centers on 1024x768, fills on 640x480
507 x = ((gr_screen.max_w - g_screenWidth) / 2);
508 y = ((gr_screen.max_h - g_screenHeight) / 2);
511 // micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
512 if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
513 // we are running slow so subtract desired time from actual and skip this frame
514 mve_video_skiptimer -= (micro_frame_delay/10);
517 // zero out so we can get a new count
518 mve_video_skiptimer = 0;
521 glBindTexture(GL_TEXTURE_2D, tex);
523 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
527 glTexCoord2f(0,0); glVertex2i(x,y);
528 glTexCoord2f(0,i2fl(256)/i2fl(hp2)); glVertex2i(x,y+256);
529 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
530 glTexCoord2f(i2fl(256)/i2fl(wp2),0); glVertex2i(x+256,y);
535 glTexCoord2f(0,i2fl(256)/i2fl(hp2)); glVertex2i(x,y+256);
536 glTexCoord2f(0,i2fl(h)/i2fl(hp2)); glVertex2i(x,y+h);
537 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+256,y+h);
538 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
543 glTexCoord2f(i2fl(256)/i2fl(wp2),0); glVertex2i(x+256,y);
544 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
545 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
546 glTexCoord2f(i2fl(512)/i2fl(wp2),0); glVertex2i(x+512,y);
551 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
552 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+256,y+h);
553 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+512,y+h);
554 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
559 glTexCoord2f(i2fl(512)/i2fl(wp2),0); glVertex2i(x+512,y);
560 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
561 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+w,y+256);
562 glTexCoord2f(i2fl(w)/i2fl(wp2),0); glVertex2i(x+w,y);
567 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
568 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+512,y+h);
569 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+w,y+h);
570 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+w,y+256);
573 // centers on 1024x768, fills on 640x480
574 x = ((gr_screen.max_w - g_screenWidth) / 2);
575 y = ((gr_screen.max_h - g_screenHeight) / 2);
577 // DDOI - This is probably really fricking slow
578 int bitmap = bm_create (16, w, h, pixelbuf, 0);
579 gr_set_bitmap (bitmap);
586 os_poll (); /* DDOI - run event loop(s) */
589 fix t2 = timer_get_fixed_seconds();
591 // only get a new count if we are definitely through with old count
592 if ( mve_video_skiptimer == 0 ) {
593 // for a more accurate count convert the frame rate to a float and multiply
594 // by one-hundred-thousand before converting to an uint.
595 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
599 if ( k == SDLK_ESCAPE ) {
603 // fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
606 int mve_video_init(ubyte *data)
613 width = mve_get_short(data);
614 height = mve_get_short(data+2);
616 // DDOI - Allocate RGB565 pixel buffer
617 pixelbuf = (ushort *)malloc (width * height * 2);
619 if (pixelbuf == NULL) {
620 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
625 memset(pixelbuf, 0, width * height * 2);
627 g_screenWidth = width;
628 g_screenHeight = height;
633 tex_w = g_screenWidth;
634 tex_h = g_screenHeight;
636 // set height and width to a power of 2
637 for (i=0; i<16; i++ ) {
638 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) ) {
644 for (i=0; i<16; i++ ) {
645 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
651 // try to keep an 8:1 size ratio
660 glGenTextures(1, &tex);
665 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
670 glBindTexture(GL_TEXTURE_2D, tex);
672 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
673 glDepthFunc(GL_ALWAYS);
674 glDepthMask(GL_FALSE);
675 glDisable(GL_DEPTH_TEST);
676 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
677 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
679 if ( os_config_read_uint(NULL, NOX("ScaleMovies"), 0) == 1 ) {
680 float scale_by = (float)gr_screen.max_w / (float)g_screenWidth;
682 // don't bother setting anything if we aren't going to need it
683 if (scale_by != 1.0f) {
684 glMatrixMode(GL_MODELVIEW);
688 glScalef( scale_by, scale_by, 1.0f );
693 // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf so less of two evils...
694 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, wp2, hp2, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
697 memset(g_palette, 0, 768);
704 void mve_video_palette(ubyte *data)
707 start = mve_get_short(data);
708 count = mve_get_short(data+2);
709 memcpy(g_palette + 3*start, data+4, 3*count);
712 void mve_video_codemap(ubyte *data, int len)
718 void mve_video_data(ubyte *data, int len)
720 short nFrameHot, nFrameCold;
721 short nXoffset, nYoffset;
722 short nXsize, nYsize;
726 nFrameHot = mve_get_short(data);
727 nFrameCold = mve_get_short(data+2);
728 nXoffset = mve_get_short(data+4);
729 nYoffset = mve_get_short(data+6);
730 nXsize = mve_get_short(data+8);
731 nYsize = mve_get_short(data+10);
732 nFlags = mve_get_ushort(data+12);
735 temp = (ubyte *)g_vBackBuf1;
736 g_vBackBuf1 = g_vBackBuf2;
740 decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
748 void mve_init(MVESTREAM *mve)
750 // reset to default values
751 mve_audio_curbuf_curpos = 0;
752 mve_audio_bufhead = 0;
753 mve_audio_buftail = 0;
754 mve_audio_playing = 0;
755 mve_audio_canplay = 0;
756 mve_audio_compressed = 0;
757 audiobuf_created = 0;
759 videobuf_created = 0;
766 void mve_play(MVESTREAM *mve)
768 int init_timer = 0, timer_error = 0;
774 while (cont && mve_playing && !timer_error) {
775 cont = mve_play_next_chunk(mve);
777 if (micro_frame_delay && !init_timer) {
782 timer_error = mve_do_timer_wait();
792 if (pixelbuf != NULL) {
797 if (g_vBuffers != NULL) {
803 if (mve_scale_video) {
804 glMatrixMode(GL_MODELVIEW);
809 glDeleteTextures(1, &tex);
812 glEnable(GL_DEPTH_TEST);