2 * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
7 * MVE movie playing routines
10 * Revision 1.5 2005/03/31 21:26:02 taylor
11 * s/alGetSourceiv/alGetSourcei/
13 * Revision 1.4 2005/03/31 00:06:20 taylor
14 * go back to more accurate timer and allow video scaling for movies
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.
31 #include <OpenGL/gl.h>
52 #include "osregistry.h"
55 static int mve_playing;
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;
63 static struct timeval timer_expire = {0, 0};
65 static fix timer_expire;
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;
80 // struct for the audio stream information
81 typedef struct MVE_AUDIO_T {
87 ALuint audio_data[MVE_AUDIO_BUFFERS];
89 ALuint audio_buffer[MVE_AUDIO_BUFFERS];
92 mve_audio_t *mas; // mve_audio_stream
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)); \
106 #define OpenAL_ErrorCheck(errocode)
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;
123 static uint mve_video_skiptimer = 0;
124 static int mve_scale_video = 0;
126 static GLuint tex = 0;
130 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
132 /*************************
134 *************************/
140 /*************************
142 *************************/
144 int mve_timer_create(ubyte *data)
148 micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
150 if (g_spdFactorNum != 0) {
151 temp = micro_frame_delay;
152 temp *= g_spdFactorNum;
153 temp /= g_spdFactorDenom;
154 micro_frame_delay = (int)temp;
160 static void mve_timer_start(void)
165 gettimeofday(&timer_expire, NULL);
167 timer_expire.tv_usec += micro_frame_delay;
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;
175 timer_expire = timer_get_microseconds();
176 timer_expire += micro_frame_delay;
182 static int mve_do_timer_wait(void)
189 struct timespec ts, tsRem;
192 gettimeofday(&tv, NULL);
194 if (tv.tv_sec > timer_expire.tv_sec)
197 if ( (tv.tv_sec == timer_expire.tv_sec) && (tv.tv_usec >= timer_expire.tv_usec) )
200 ts.tv_sec = timer_expire.tv_sec - tv.tv_sec;
201 ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec);
203 if (ts.tv_nsec < 0) {
204 ts.tv_nsec += 1000000000UL;
208 if ( (nanosleep(&ts, &tsRem) == -1) && (errno == EINTR) ) {
209 mprintf(("MVE: Timer error! Aborting movie playback!\n"));
214 timer_expire.tv_usec += micro_frame_delay;
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;
224 tv = timer_get_microseconds();
226 if (tv > timer_expire)
229 ts = timer_expire - tv;
236 timer_expire += micro_frame_delay;
242 static void mve_timer_stop()
244 timer_expire.tv_sec = 0;
245 timer_expire.tv_usec = 0;
249 /*************************
251 *************************/
253 // setup the audio information from the data stream
254 void mve_audio_createbuf(ubyte minor, ubyte *data)
256 if (audiobuf_created)
259 // if game sound disabled don't try and play movie audio
260 if (!Sound_enabled) {
261 mve_audio_canplay = 0;
266 int i, flags, desired_buffer, sample_rate;
268 mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
269 memset(mas, 0, sizeof(mve_audio_t));
271 mas->format = AL_INVALID;
273 flags = mve_get_ushort(data + 2);
274 sample_rate = mve_get_ushort(data + 4);
275 desired_buffer = mve_get_int(data + 6);
277 mas->channels = (flags & 0x0001) ? 2 : 1;
278 mas->bitsize = (flags & 0x0002) ? 16 : 8;
280 mas->sample_rate = sample_rate;
283 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
285 mve_audio_compressed = 0;
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;
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;
302 // somethings wrong, bail now
303 if (mas->format == AL_INVALID) {
304 mve_audio_canplay = 0;
305 audiobuf_created = 1;
309 alGenSources(1, &mas->source_id);
311 if ((i = alGetError()) == AL_NO_ERROR) {
312 mve_audio_canplay = 1;
314 mve_audio_canplay = 0;
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 );
324 memset(mas->audio_buffer, 0, MVE_AUDIO_BUFFERS * sizeof(ALuint));
326 mve_audio_bufhead = 0;
327 mve_audio_buftail = 0;
329 audiobuf_created = 1;
333 // play and stream the audio
334 void mve_audio_play()
337 if (mve_audio_canplay) {
338 ALint status, bqueued;
340 alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status);
342 OpenAL_ErrorCheck(return);
344 alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued);
346 OpenAL_ErrorCheck(return);
348 mve_audio_playing = 1;
350 if (status != AL_PLAYING && bqueued > 0) {
351 alSourcePlay(mas->source_id);
353 OpenAL_ErrorCheck(return);
359 // call this in shutdown to stop and close audio
360 static void mve_audio_stop()
362 if (!audiobuf_created)
366 mve_audio_playing = 0;
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);
380 int mve_audio_data(ubyte major, ubyte *data)
383 static const int selected_chan=1;
387 if (mve_audio_canplay) {
388 chan = mve_get_ushort(data + 2);
389 nsamp = mve_get_ushort(data + 4);
391 if (chan & selected_chan) {
392 ALint bprocessed, bqueued, status;
395 alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &bprocessed);
397 OpenAL_ErrorCheck(return 0);
399 while (bprocessed-- > 2) {
400 alSourceUnqueueBuffers(mas->source_id, 1, &bid);
401 // fprintf(stderr,"Unqueued buffer %d(%d)\n", mve_audio_bufhead, bid);
403 if (++mve_audio_bufhead == MVE_AUDIO_BUFFERS)
404 mve_audio_bufhead = 0;
407 alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued);
409 OpenAL_ErrorCheck(return 0);
412 mprintf(("MVE: Buffer underun (First is normal)\n"));
414 alGetSourcei(mas->source_id, AL_SOURCE_STATE, &status);
416 OpenAL_ErrorCheck(return 0);
418 if (mve_audio_playing && status != AL_PLAYING && bqueued > 0) {
419 alSourcePlay(mas->source_id);
421 OpenAL_ErrorCheck(return 0);
424 if (bqueued < MVE_AUDIO_BUFFERS) {
427 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
429 if (mve_audio_compressed) {
432 buf = (short *)malloc(nsamp);
433 mveaudio_uncompress(buf, data, -1); /* XXX */
438 buf = (short *)malloc(nsamp);
439 memcpy(buf, data, nsamp);
442 buf = (short *)malloc(nsamp);
444 memset(buf, 0, nsamp); /* XXX */
448 if (!mas->audio_buffer[mve_audio_buftail]) {
449 alGenBuffers(1,&mas->audio_buffer[mve_audio_buftail]);
451 OpenAL_ErrorCheck( {free(buf); return 0;} );
454 alBufferData(mas->audio_buffer[mve_audio_buftail], mas->format, buf, nsamp, mas->sample_rate);
456 OpenAL_ErrorCheck( {free(buf); return 0;} );
458 alSourceQueueBuffers(mas->source_id, 1, &mas->audio_buffer[mve_audio_buftail]);
460 // fprintf(stderr,"Queued buffer %d(%d)\n", mve_audio_buftail, mas->audio_buffer[mve_audio_buftail]);
461 OpenAL_ErrorCheck( {free(buf); return 0;} );
463 if (++mve_audio_buftail == MVE_AUDIO_BUFFERS)
464 mve_audio_buftail = 0;
469 mprintf(("MVE: Buffer overrun: Queue full\n"));
472 // fprintf(stderr,"Buffers queued: %d\n", bqueued);
480 /*************************
482 *************************/
484 int mve_video_createbuf(ubyte minor, ubyte *data)
486 if (videobuf_created)
490 short count, truecolor;
491 w = mve_get_short(data);
492 h = mve_get_short(data+2);
495 count = mve_get_short(data+4);
501 truecolor = mve_get_short(data+6);
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);
512 if (g_vBackBuf1 == NULL) {
513 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
514 videobuf_created = 1;
518 g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
520 memset(g_vBackBuf1, 0, g_width * g_height * 4);
522 videobuf_created = 1;
527 static void mve_convert_and_draw()
531 ushort *pixels = (ushort *)g_vBackBuf1;
538 if (g_screenWidth > g_width) {
539 pDests += ((g_screenWidth - g_width) / 2) / 2;
541 if (g_screenHeight > g_height) {
542 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
545 for (y=0; y<g_height; y++) {
546 for (x = 0; x < g_width; x++) {
547 pDests[x] = (1<<15)|*pSrcs;
551 pDests += g_screenWidth;
555 void mve_video_display()
557 fix t1 = timer_get_fixed_seconds();
558 mve_convert_and_draw();
561 int h = g_screenHeight;
562 int w = g_screenWidth;
565 if (mve_scale_video) {
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);
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);
579 // zero out so we can get a new count
580 mve_video_skiptimer = 0;
583 glBindTexture(GL_TEXTURE_2D, tex);
585 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
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);
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);
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);
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);
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);
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);
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);
639 // DDOI - This is probably really fricking slow
640 int bitmap = bm_create (16, w, h, pixelbuf, 0);
641 gr_set_bitmap (bitmap);
648 os_poll (); /* DDOI - run event loop(s) */
651 fix t2 = timer_get_fixed_seconds();
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);
661 if ( k == KEY_ESC ) {
665 // fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
668 int mve_video_init(ubyte *data)
675 width = mve_get_short(data);
676 height = mve_get_short(data+2);
678 // DDOI - Allocate RGB565 pixel buffer
679 pixelbuf = (ushort *)malloc (width * height * 2);
681 if (pixelbuf == NULL) {
682 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
687 memset(pixelbuf, 0, width * height * 2);
689 g_screenWidth = width;
690 g_screenHeight = height;
695 tex_w = g_screenWidth;
696 tex_h = g_screenHeight;
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))) ) {
706 for (i=0; i<16; i++ ) {
707 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
713 // try to keep an 8:1 size ratio
722 glGenTextures(1, &tex);
727 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
732 glBindTexture(GL_TEXTURE_2D, tex);
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);
741 if ( os_config_read_uint(NULL, NOX("ScaleMovies"), 0) == 1 ) {
742 float scale_by = (float)gr_screen.max_w / (float)g_screenWidth;
744 // don't bother setting anything if we aren't going to need it
745 if (scale_by != 1.0f) {
746 glMatrixMode(GL_MODELVIEW);
750 glScalef( scale_by, scale_by, 1.0f );
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);
759 memset(g_palette, 0, 768);
766 void mve_video_palette(ubyte *data)
769 start = mve_get_short(data);
770 count = mve_get_short(data+2);
771 memcpy(g_palette + 3*start, data+4, 3*count);
774 void mve_video_codemap(ubyte *data, int len)
780 void mve_video_data(ubyte *data, int len)
782 short nFrameHot, nFrameCold;
783 short nXoffset, nYoffset;
784 short nXsize, nYsize;
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);
797 temp = (ubyte *)g_vBackBuf1;
798 g_vBackBuf1 = g_vBackBuf2;
802 decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
810 void mve_init(MVESTREAM *mve)
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;
821 videobuf_created = 0;
828 void mve_play(MVESTREAM *mve)
830 int init_timer = 0, timer_error = 0;
836 while (cont && mve_playing && !timer_error) {
837 cont = mve_play_next_chunk(mve);
839 if (micro_frame_delay && !init_timer) {
844 timer_error = mve_do_timer_wait();
854 if (pixelbuf != NULL) {
859 if (g_vBuffers != NULL) {
865 if (mve_scale_video) {
866 glMatrixMode(GL_MODELVIEW);
871 glDeleteTextures(1, &tex);
874 glEnable(GL_DEPTH_TEST);