2 * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
7 * MVE movie playing routines
10 * Revision 1.3 2005/03/29 07:50:34 taylor
11 * Update to newest movie code with much better video support and audio support from
12 * Pierre Willenbrock. Movies are enabled always now (no longer a build option)
13 * and but can be skipped with the "--nomovies" or "-n" cmdline options.
21 #include <OpenGL/gl.h>
44 static int mve_playing;
47 static int g_spdFactorNum = 0;
48 static int g_spdFactorDenom = 10;
49 static int micro_frame_delay = 0;
50 static int timer_started = 0;
51 static fix timer_expire;
54 #define MVE_AUDIO_BUFFERS 64 // total buffers to interact with stream
55 static int mve_audio_curbuf_curpos = 0;
56 static int mve_audio_bufhead = 0;
57 static int mve_audio_buftail = 0;
58 static int mve_audio_playing = 0;
59 static int mve_audio_canplay = 0;
60 static int mve_audio_compressed = 0;
61 static int audiobuf_created;
65 // struct for the audio stream information
66 typedef struct MVE_AUDIO_T {
72 ALuint audio_data[MVE_AUDIO_BUFFERS];
74 ALuint audio_buffer[MVE_AUDIO_BUFFERS];
77 mve_audio_t *mas; // mve_audio_stream
80 #define OpenAL_ErrorCheck(errcode) do { \
81 int i = alGetError(); \
82 if (i != AL_NO_ERROR) { \
83 while(i != AL_NO_ERROR) { \
84 fprintf(stderr, "%s/%s:%d - OpenAL error %s\n", __FUNCTION__, __FILE__, __LINE__, alGetString(i)); \
91 #define OpenAL_ErrorCheck(errocode)
98 int g_width, g_height;
99 void *g_vBuffers = NULL;
100 void *g_vBackBuf1, *g_vBackBuf2;
101 ushort *pixelbuf = NULL;
102 static int g_screenWidth, g_screenHeight;
103 static ubyte g_palette[768];
104 static ubyte *g_pCurMap=NULL;
105 static int g_nMapLength=0;
106 static int videobuf_created, video_inited;
108 static uint mve_video_skiptimer = 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)
132 micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
134 if (g_spdFactorNum != 0) {
135 temp = micro_frame_delay;
136 temp *= g_spdFactorNum;
137 temp /= g_spdFactorDenom;
138 micro_frame_delay = (int)temp;
144 static void timer_start(void)
146 timer_expire = timer_get_microseconds();
147 timer_expire += micro_frame_delay;
152 static void do_timer_wait(void)
156 tv = timer_get_microseconds();
158 if (tv > timer_expire)
161 ts = timer_expire - tv;
168 timer_expire += micro_frame_delay;
171 /*************************
173 *************************/
175 // setup the audio information from the data stream
176 void mve_audio_createbuf(ubyte minor, ubyte *data)
178 if (audiobuf_created)
181 // if game sound disabled don't try and play movie audio
182 if (!Sound_enabled) {
183 mve_audio_canplay = 0;
188 int i, flags, desired_buffer, sample_rate;
190 mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
191 memset(mas, 0, sizeof(mve_audio_t));
193 mas->format = AL_INVALID;
195 flags = mve_get_ushort(data + 2);
196 sample_rate = mve_get_ushort(data + 4);
197 desired_buffer = mve_get_int(data + 6);
199 mas->channels = (flags & 0x0001) ? 2 : 1;
200 mas->bitsize = (flags & 0x0002) ? 16 : 8;
202 mas->sample_rate = sample_rate;
205 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
207 mve_audio_compressed = 0;
210 if (mas->bitsize == 16) {
211 if (mas->channels == 2) {
212 mas->format = AL_FORMAT_STEREO16;
213 } else if (mas->channels == 1) {
214 mas->format = AL_FORMAT_MONO16;
216 } else if (mas->bitsize == 8) {
217 if (mas->channels == 2) {
218 mas->format = AL_FORMAT_STEREO8;
219 } else if (mas->channels == 1) {
220 mas->format = AL_FORMAT_MONO8;
224 // somethings wrong, bail now
225 if (mas->format == AL_INVALID) {
226 mve_audio_canplay = 0;
227 audiobuf_created = 1;
231 alGenSources(1, &mas->source_id);
233 if ((i = alGetError()) == AL_NO_ERROR) {
234 mve_audio_canplay = 1;
236 mve_audio_canplay = 0;
239 alSourcef(mas->source_id, AL_GAIN, 1.0f);
240 alSource3f(mas->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
241 alSource3f(mas->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
242 alSource3f(mas->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
243 alSourcef(mas->source_id, AL_ROLLOFF_FACTOR, 0.0f );
244 alSourcei(mas->source_id, AL_SOURCE_RELATIVE, AL_TRUE );
246 memset(mas->audio_buffer, 0, MVE_AUDIO_BUFFERS * sizeof(ALuint));
248 mve_audio_bufhead = 0;
249 mve_audio_buftail = 0;
251 audiobuf_created = 1;
255 // play and stream the audio
256 void mve_audio_play()
259 if (mve_audio_canplay) {
260 ALint status, bqueued;
262 alGetSourceiv(mas->source_id, AL_SOURCE_STATE, &status);
264 OpenAL_ErrorCheck(return);
266 alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued);
268 OpenAL_ErrorCheck(return);
270 mve_audio_playing = 1;
272 if (status != AL_PLAYING && bqueued > 0) {
273 alSourcePlay(mas->source_id);
275 OpenAL_ErrorCheck(return);
281 // call this in shutdown to stop and close audio
282 static void mve_audio_stop()
284 if (!audiobuf_created)
288 mve_audio_playing = 0;
290 alSourceStop(mas->source_id);
291 alSourceUnqueueBuffers(mas->source_id, MVE_AUDIO_BUFFERS, mas->audio_buffer);
292 alDeleteBuffers(MVE_AUDIO_BUFFERS, mas->audio_buffer);
293 alDeleteSources(1, &mas->source_id);
302 int mve_audio_data(ubyte major, ubyte *data)
305 static const int selected_chan=1;
309 if (mve_audio_canplay) {
310 chan = mve_get_ushort(data + 2);
311 nsamp = mve_get_ushort(data + 4);
313 if (chan & selected_chan) {
314 ALint bprocessed, bqueued, status;
317 alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &bprocessed);
319 OpenAL_ErrorCheck(return 0);
321 while (bprocessed-- > 2) {
322 alSourceUnqueueBuffers(mas->source_id, 1, &bid);
323 // fprintf(stderr,"Unqueued buffer %d(%d)\n", mve_audio_bufhead, bid);
325 if (++mve_audio_bufhead == MVE_AUDIO_BUFFERS)
326 mve_audio_bufhead = 0;
329 alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued);
331 OpenAL_ErrorCheck(return 0);
334 mprintf(("MVE: Buffer underun (First is normal)\n"));
336 alGetSourceiv(mas->source_id, AL_SOURCE_STATE, &status);
338 OpenAL_ErrorCheck(return 0);
340 if (mve_audio_playing && status != AL_PLAYING && bqueued > 0) {
341 alSourcePlay(mas->source_id);
343 OpenAL_ErrorCheck(return 0);
346 if (bqueued < MVE_AUDIO_BUFFERS) {
349 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
351 if (mve_audio_compressed) {
354 buf = (short *)malloc(nsamp);
355 mveaudio_uncompress(buf, data, -1); /* XXX */
360 buf = (short *)malloc(nsamp);
361 memcpy(buf, data, nsamp);
364 buf = (short *)malloc(nsamp);
366 memset(buf, 0, nsamp); /* XXX */
370 if (!mas->audio_buffer[mve_audio_buftail]) {
371 alGenBuffers(1,&mas->audio_buffer[mve_audio_buftail]);
373 OpenAL_ErrorCheck( {free(buf); return 0;} );
376 alBufferData(mas->audio_buffer[mve_audio_buftail], mas->format, buf, nsamp, mas->sample_rate);
378 OpenAL_ErrorCheck( {free(buf); return 0;} );
380 alSourceQueueBuffers(mas->source_id, 1, &mas->audio_buffer[mve_audio_buftail]);
382 // fprintf(stderr,"Queued buffer %d(%d)\n", mve_audio_buftail, mas->audio_buffer[mve_audio_buftail]);
383 OpenAL_ErrorCheck( {free(buf); return 0;} );
385 if (++mve_audio_buftail == MVE_AUDIO_BUFFERS)
386 mve_audio_buftail = 0;
391 mprintf(("MVE: Buffer overrun: Queue full\n"));
394 // fprintf(stderr,"Buffers queued: %d\n", bqueued);
402 /*************************
404 *************************/
406 int mve_video_createbuf(ubyte minor, ubyte *data)
408 if (videobuf_created)
412 short count, truecolor;
413 w = mve_get_short(data);
414 h = mve_get_short(data+2);
417 count = mve_get_short(data+4);
423 truecolor = mve_get_short(data+6);
431 /* TODO: * 4 causes crashes on some files */
432 g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 8);
434 if (g_vBackBuf1 == NULL) {
435 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
436 videobuf_created = 1;
440 g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
442 memset(g_vBackBuf1, 0, g_width * g_height * 8);
444 videobuf_created = 1;
449 static void mve_convert_and_draw()
453 ushort *pixels = (ushort *)g_vBackBuf1;
460 if (g_screenWidth > g_width) {
461 pDests += ((g_screenWidth - g_width) / 2) / 2;
463 if (g_screenHeight > g_height) {
464 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
467 for (y=0; y<g_height; y++) {
468 for (x = 0; x < g_width; x++) {
469 pDests[x] = (1<<15)|*pSrcs;
473 pDests += g_screenWidth;
477 void mve_video_display()
479 fix t1 = timer_get_fixed_seconds();
480 mve_convert_and_draw();
482 // centers on 1024x768, fills on 640x480
483 int x = ((gr_screen.max_w - g_screenWidth) / 2);
484 int y = ((gr_screen.max_h - g_screenHeight) / 2);
485 int h = g_screenHeight;
486 int w = g_screenWidth;
489 // micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
490 if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
491 // we are running slow so subtract desired time from actual and skip this frame
492 mve_video_skiptimer -= (micro_frame_delay/10);
495 // zero out so we can get a new count
496 mve_video_skiptimer = 0;
499 glBindTexture(GL_TEXTURE_2D, tex);
501 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
505 glTexCoord2f(0,0); glVertex2i(x,y);
506 glTexCoord2f(0,i2fl(256)/i2fl(hp2)); glVertex2i(x,y+256);
507 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
508 glTexCoord2f(i2fl(256)/i2fl(wp2),0); glVertex2i(x+256,y);
513 glTexCoord2f(0,i2fl(256)/i2fl(hp2)); glVertex2i(x,y+256);
514 glTexCoord2f(0,i2fl(h)/i2fl(hp2)); glVertex2i(x,y+h);
515 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+256,y+h);
516 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
521 glTexCoord2f(i2fl(256)/i2fl(wp2),0); glVertex2i(x+256,y);
522 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
523 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
524 glTexCoord2f(i2fl(512)/i2fl(wp2),0); glVertex2i(x+512,y);
529 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
530 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+256,y+h);
531 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+512,y+h);
532 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
537 glTexCoord2f(i2fl(512)/i2fl(wp2),0); glVertex2i(x+512,y);
538 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
539 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+w,y+256);
540 glTexCoord2f(i2fl(w)/i2fl(wp2),0); glVertex2i(x+w,y);
545 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
546 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+512,y+h);
547 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+w,y+h);
548 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+w,y+256);
551 // DDOI - This is probably really fricking slow
552 int bitmap = bm_create (16, w, h, pixelbuf, 0);
553 gr_set_bitmap (bitmap);
560 os_poll (); /* DDOI - run event loop(s) */
563 fix t2 = timer_get_fixed_seconds();
565 // only get a new count if we are definitely through with old count
566 if ( mve_video_skiptimer == 0 ) {
567 // for a more accurate count convert the frame rate to a float and multiply
568 // by one-hundred-thousand before converting to an uint.
569 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
573 if ( k == KEY_ESC ) {
577 // fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
580 int mve_video_init(ubyte *data)
587 width = mve_get_short(data);
588 height = mve_get_short(data+2);
590 // DDOI - Allocate RGB565 pixel buffer
591 pixelbuf = (ushort *)malloc (width * height * 2);
593 if (pixelbuf == NULL) {
594 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
599 memset(pixelbuf, 0, width * height * 2);
601 g_screenWidth = width;
602 g_screenHeight = height;
607 tex_w = g_screenWidth;
608 tex_h = g_screenHeight;
610 // set height and width to a power of 2
611 for (i=0; i<16; i++ ) {
612 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) ) {
618 for (i=0; i<16; i++ ) {
619 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
625 // try to keep an 8:1 size ratio
634 glGenTextures(1, &tex);
639 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
644 glBindTexture(GL_TEXTURE_2D, tex);
646 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
647 glDepthFunc(GL_ALWAYS);
648 glDepthMask(GL_FALSE);
649 glDisable(GL_DEPTH_TEST);
650 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
651 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
653 // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf so less of two evils...
654 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, wp2, hp2, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
657 memset(g_palette, 0, 768);
664 void mve_video_palette(ubyte *data)
667 start = mve_get_short(data);
668 count = mve_get_short(data+2);
669 memcpy(g_palette + 3*start, data+4, 3*count);
672 void mve_video_codemap(ubyte *data, int len)
678 void mve_video_data(ubyte *data, int len)
680 short nFrameHot, nFrameCold;
681 short nXoffset, nYoffset;
682 short nXsize, nYsize;
686 nFrameHot = mve_get_short(data);
687 nFrameCold = mve_get_short(data+2);
688 nXoffset = mve_get_short(data+4);
689 nYoffset = mve_get_short(data+6);
690 nXsize = mve_get_short(data+8);
691 nYsize = mve_get_short(data+10);
692 nFlags = mve_get_ushort(data+12);
695 temp = (ubyte *)g_vBackBuf1;
696 g_vBackBuf1 = g_vBackBuf2;
700 decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
708 void mve_init(MVESTREAM *mve)
710 // reset to default values
711 mve_audio_curbuf_curpos = 0;
712 mve_audio_bufhead = 0;
713 mve_audio_buftail = 0;
714 mve_audio_playing = 0;
715 mve_audio_canplay = 0;
716 mve_audio_compressed = 0;
717 audiobuf_created = 0;
719 videobuf_created = 0;
725 void mve_play(MVESTREAM *mve)
730 if (micro_frame_delay && !init_timer) {
735 while (cont && mve_playing) {
736 cont = mve_play_next_chunk(mve);
746 if (pixelbuf != NULL) {
751 if (g_vBuffers != NULL) {
757 glDeleteTextures(1, &tex);
760 glEnable(GL_DEPTH_TEST);