2 * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
7 * MVE movie playing routines
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
16 * Revision 1.5 2005/03/31 21:26:02 taylor
17 * s/alGetSourceiv/alGetSourcei/
19 * Revision 1.4 2005/03/31 00:06:20 taylor
20 * go back to more accurate timer and allow video scaling for movies
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.
37 #include <OpenGL/gl.h>
58 #include "osregistry.h"
62 static int mve_playing;
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;
70 static struct timeval timer_expire = {0, 0};
72 static fix timer_expire;
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;
87 // struct for the audio stream information
88 typedef struct MVE_AUDIO_T {
94 ALuint audio_data[MVE_AUDIO_BUFFERS];
96 ALuint audio_buffer[MVE_AUDIO_BUFFERS];
99 mve_audio_t *mas; // mve_audio_stream
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;
115 static uint mve_video_skiptimer = 0;
116 static int mve_scale_video = 0;
118 static GLuint tex = 0;
122 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
124 /*************************
126 *************************/
132 /*************************
134 *************************/
136 int mve_timer_create(ubyte *data)
140 micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
142 if (g_spdFactorNum != 0) {
143 temp = micro_frame_delay;
144 temp *= g_spdFactorNum;
145 temp /= g_spdFactorDenom;
146 micro_frame_delay = (int)temp;
152 static void mve_timer_start(void)
157 gettimeofday(&timer_expire, NULL);
159 timer_expire.tv_usec += micro_frame_delay;
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;
167 timer_expire = timer_get_microseconds();
168 timer_expire += micro_frame_delay;
174 static int mve_do_timer_wait(void)
181 struct timespec ts, tsRem;
184 gettimeofday(&tv, NULL);
186 if (tv.tv_sec > timer_expire.tv_sec)
189 if ( (tv.tv_sec == timer_expire.tv_sec) && (tv.tv_usec >= timer_expire.tv_usec) )
192 ts.tv_sec = timer_expire.tv_sec - tv.tv_sec;
193 ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec);
195 if (ts.tv_nsec < 0) {
196 ts.tv_nsec += 1000000000UL;
200 if ( (nanosleep(&ts, &tsRem) == -1) && (errno == EINTR) ) {
201 mprintf(("MVE: Timer error! Aborting movie playback!\n"));
206 timer_expire.tv_usec += micro_frame_delay;
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;
216 tv = timer_get_microseconds();
218 if (tv > timer_expire)
221 ts = timer_expire - tv;
228 timer_expire += micro_frame_delay;
234 static void mve_timer_stop()
236 timer_expire.tv_sec = 0;
237 timer_expire.tv_usec = 0;
241 /*************************
243 *************************/
245 // setup the audio information from the data stream
246 void mve_audio_createbuf(ubyte minor, ubyte *data)
248 if (audiobuf_created)
251 // if game sound disabled don't try and play movie audio
252 if (!Sound_enabled) {
253 mve_audio_canplay = 0;
258 int flags, desired_buffer, sample_rate;
260 mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
261 memset(mas, 0, sizeof(mve_audio_t));
263 mas->format = AL_INVALID;
265 flags = mve_get_ushort(data + 2);
266 sample_rate = mve_get_ushort(data + 4);
267 desired_buffer = mve_get_int(data + 6);
269 mas->channels = (flags & 0x0001) ? 2 : 1;
270 mas->bitsize = (flags & 0x0002) ? 16 : 8;
272 mas->sample_rate = sample_rate;
275 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
277 mve_audio_compressed = 0;
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;
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;
294 // somethings wrong, bail now
295 if (mas->format == AL_INVALID) {
296 mve_audio_canplay = 0;
297 audiobuf_created = 1;
301 OpenAL_ErrorCheck( alGenSources(1, &mas->source_id), { mve_audio_canplay = 0; return; } );
303 mve_audio_canplay = 1;
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 ) );
312 memset(mas->audio_buffer, 0, MVE_AUDIO_BUFFERS * sizeof(ALuint));
314 mve_audio_bufhead = 0;
315 mve_audio_buftail = 0;
317 audiobuf_created = 1;
321 // play and stream the audio
322 void mve_audio_play()
325 if (mve_audio_canplay) {
326 ALint status, bqueued;
328 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status), return );
330 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued), return );
332 mve_audio_playing = 1;
334 if (status != AL_PLAYING && bqueued > 0) {
335 OpenAL_ErrorPrint( alSourcePlay(mas->source_id) );
341 // call this in shutdown to stop and close audio
342 static void mve_audio_stop()
344 if (!audiobuf_created)
350 mve_audio_playing = 0;
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) );
365 int mve_audio_data(ubyte major, ubyte *data)
368 static const int selected_chan=1;
372 if (mve_audio_canplay) {
373 chan = mve_get_ushort(data + 2);
374 nsamp = mve_get_ushort(data + 4);
376 if (chan & selected_chan) {
377 ALint bprocessed, bqueued, status;
380 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &bprocessed), return 0 );
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);
386 if (++mve_audio_bufhead == MVE_AUDIO_BUFFERS)
387 mve_audio_bufhead = 0;
390 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued), return 0 );
393 mprintf(("MVE: Buffer underun (First is normal)\n"));
395 OpenAL_ErrorCheck( alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status), return 0 );
397 if (mve_audio_playing && status != AL_PLAYING && bqueued > 0) {
398 OpenAL_ErrorCheck( alSourcePlay(mas->source_id), return 0 );
401 if (bqueued < MVE_AUDIO_BUFFERS) {
404 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
406 if (mve_audio_compressed) {
409 buf = (short *)malloc(nsamp);
410 mveaudio_uncompress(buf, data, -1); /* XXX */
415 buf = (short *)malloc(nsamp);
416 memcpy(buf, data, nsamp);
419 buf = (short *)malloc(nsamp);
421 memset(buf, 0, nsamp); /* XXX */
425 if (!mas->audio_buffer[mve_audio_buftail]) {
426 OpenAL_ErrorCheck( alGenBuffers(1,&mas->audio_buffer[mve_audio_buftail]), { free(buf); return 0; } );
429 OpenAL_ErrorCheck( alBufferData(mas->audio_buffer[mve_audio_buftail], mas->format, buf, nsamp, mas->sample_rate), { free(buf); return 0; } );
431 OpenAL_ErrorCheck( alSourceQueueBuffers(mas->source_id, 1, &mas->audio_buffer[mve_audio_buftail]), { free(buf); return 0;} );
433 //fprintf(stderr,"Queued buffer %d(%d)\n", mve_audio_buftail, mas->audio_buffer[mve_audio_buftail]);
435 if (++mve_audio_buftail == MVE_AUDIO_BUFFERS)
436 mve_audio_buftail = 0;
441 mprintf(("MVE: Buffer overrun: Queue full\n"));
444 //fprintf(stderr,"Buffers queued: %d\n", bqueued);
452 /*************************
454 *************************/
456 int mve_video_createbuf(ubyte minor, ubyte *data)
458 if (videobuf_created)
462 short count, truecolor;
463 w = mve_get_short(data);
464 h = mve_get_short(data+2);
467 count = mve_get_short(data+4);
473 truecolor = mve_get_short(data+6);
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);
484 if (g_vBackBuf1 == NULL) {
485 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
486 videobuf_created = 1;
490 g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
492 memset(g_vBackBuf1, 0, g_width * g_height * 4);
494 videobuf_created = 1;
499 static void mve_convert_and_draw()
503 ushort *pixels = (ushort *)g_vBackBuf1;
510 if (g_screenWidth > g_width) {
511 pDests += ((g_screenWidth - g_width) / 2) / 2;
513 if (g_screenHeight > g_height) {
514 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
517 for (y=0; y<g_height; y++) {
518 for (x = 0; x < g_width; x++) {
519 pDests[x] = (1<<15)|*pSrcs;
523 pDests += g_screenWidth;
527 void mve_video_display()
529 fix t1 = timer_get_fixed_seconds();
530 mve_convert_and_draw();
533 int h = g_screenHeight;
534 int w = g_screenWidth;
537 if (mve_scale_video) {
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);
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);
551 // zero out so we can get a new count
552 mve_video_skiptimer = 0;
555 glBindTexture(GL_TEXTURE_2D, tex);
557 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
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);
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);
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);
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);
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);
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);
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);
611 // DDOI - This is probably really fricking slow
612 int bitmap = bm_create (16, w, h, pixelbuf, 0);
613 gr_set_bitmap (bitmap);
620 os_poll (); /* DDOI - run event loop(s) */
623 fix t2 = timer_get_fixed_seconds();
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);
633 if ( k == KEY_ESC ) {
637 // fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
640 int mve_video_init(ubyte *data)
647 width = mve_get_short(data);
648 height = mve_get_short(data+2);
650 // DDOI - Allocate RGB565 pixel buffer
651 pixelbuf = (ushort *)malloc (width * height * 2);
653 if (pixelbuf == NULL) {
654 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
659 memset(pixelbuf, 0, width * height * 2);
661 g_screenWidth = width;
662 g_screenHeight = height;
667 tex_w = g_screenWidth;
668 tex_h = g_screenHeight;
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))) ) {
678 for (i=0; i<16; i++ ) {
679 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
685 // try to keep an 8:1 size ratio
694 glGenTextures(1, &tex);
699 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
704 glBindTexture(GL_TEXTURE_2D, tex);
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);
713 if ( os_config_read_uint(NULL, NOX("ScaleMovies"), 0) == 1 ) {
714 float scale_by = (float)gr_screen.max_w / (float)g_screenWidth;
716 // don't bother setting anything if we aren't going to need it
717 if (scale_by != 1.0f) {
718 glMatrixMode(GL_MODELVIEW);
722 glScalef( scale_by, scale_by, 1.0f );
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);
731 memset(g_palette, 0, 768);
738 void mve_video_palette(ubyte *data)
741 start = mve_get_short(data);
742 count = mve_get_short(data+2);
743 memcpy(g_palette + 3*start, data+4, 3*count);
746 void mve_video_codemap(ubyte *data, int len)
752 void mve_video_data(ubyte *data, int len)
754 short nFrameHot, nFrameCold;
755 short nXoffset, nYoffset;
756 short nXsize, nYsize;
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);
769 temp = (ubyte *)g_vBackBuf1;
770 g_vBackBuf1 = g_vBackBuf2;
774 decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
782 void mve_init(MVESTREAM *mve)
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;
793 videobuf_created = 0;
800 void mve_play(MVESTREAM *mve)
802 int init_timer = 0, timer_error = 0;
808 while (cont && mve_playing && !timer_error) {
809 cont = mve_play_next_chunk(mve);
811 if (micro_frame_delay && !init_timer) {
816 timer_error = mve_do_timer_wait();
826 if (pixelbuf != NULL) {
831 if (g_vBuffers != NULL) {
837 if (mve_scale_video) {
838 glMatrixMode(GL_MODELVIEW);
843 glDeleteTextures(1, &tex);
846 glEnable(GL_DEPTH_TEST);