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.
45 #include "osregistry.h"
50 static int mve_playing;
54 static int micro_frame_delay = 0;
55 static int timer_started = 0;
56 static int timer_created = 0;
57 static int timer_expire;
58 static Uint64 micro_timer_start = 0;
59 static Uint64 micro_timer_freq = 0;
62 #define MVE_AUDIO_BUFFERS 8 // total buffers to interact with stream
64 static std::vector<ALuint> mve_audio_bufl_free;
65 static ubyte *mve_audio_buf = NULL;
66 static size_t mve_audio_buf_size = 0;
67 static size_t mve_audio_buf_offset = 0;
69 static int mve_audio_playing = 0;
70 static int mve_audio_canplay = 0;
71 static int mve_audio_compressed = 0;
72 static int audiobuf_created;
74 // struct for the audio stream information
82 ALuint buffers[MVE_AUDIO_BUFFERS];
85 mve_audio_t *mas = NULL; // mve_audio_stream
90 int g_width, g_height;
91 void *g_vBuffers = NULL;
92 void *g_vBackBuf1, *g_vBackBuf2;
93 ushort *pixelbuf = NULL;
94 static int g_screenWidth, g_screenHeight;
95 static ubyte g_palette[768];
96 static ubyte *g_pCurMap=NULL;
97 static int g_nMapLength=0;
98 static int videobuf_created, video_inited;
100 static uint mve_video_skiptimer = 0;
101 static int mve_scale_video = 0;
102 static GLuint tex = 0;
105 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
107 /*************************
109 *************************/
115 /*************************
117 *************************/
119 int mve_timer_create(ubyte *data)
121 micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
123 micro_timer_start = SDL_GetPerformanceCounter();
124 micro_timer_freq = SDL_GetPerformanceFrequency();
126 if (micro_timer_freq < 1000) {
127 micro_timer_freq = 1000;
135 static int mve_timer_get_microseconds()
138 Uint64 us = SDL_GetPerformanceCounter() - micro_timer_start;
140 if (micro_timer_freq >= 1000000) {
141 us /= (micro_timer_freq / 1000000);
143 us *= (1000000 / micro_timer_freq);
149 static void mve_timer_start(void)
154 timer_expire = mve_timer_get_microseconds();
155 timer_expire += micro_frame_delay;
160 static int mve_do_timer_wait(void)
167 tv = mve_timer_get_microseconds();
169 if (tv > timer_expire)
172 ts = timer_expire - tv;
176 // try and burn off excess in attempt to keep sync
178 for (int i = 0; i < 10; i++) {
183 timer_expire += micro_frame_delay;
188 static void mve_timer_stop()
194 micro_timer_start = 0;
195 micro_timer_freq = 0;
198 /*************************
200 *************************/
202 // setup the audio information from the data stream
203 void mve_audio_createbuf(ubyte minor, ubyte *data)
205 if (audiobuf_created)
208 // if game sound disabled don't try and play movie audio
209 if ( !Sound_enabled ) {
210 mve_audio_canplay = 0;
211 audiobuf_created = 1;
215 int flags, desired_buffer, sample_rate;
217 mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
220 mve_audio_canplay = 0;
221 audiobuf_created = 1;
225 memset(mas, 0, sizeof(mve_audio_t));
227 mas->format = AL_INVALID;
229 flags = mve_get_ushort(data + 2);
230 sample_rate = mve_get_ushort(data + 4);
231 desired_buffer = mve_get_int(data + 6);
233 if (desired_buffer > 0) {
234 mve_audio_buf = (ubyte*) malloc (desired_buffer);
236 if (mve_audio_buf == NULL) {
237 mve_audio_canplay = 0;
238 audiobuf_created = 1;
242 mve_audio_buf_size = desired_buffer;
243 mve_audio_buf_offset = 0;
245 mve_audio_canplay = 0;
246 audiobuf_created = 1;
250 mas->channels = (flags & 0x0001) ? 2 : 1;
251 mas->bitsize = (flags & 0x0002) ? 16 : 8;
253 mas->sample_rate = sample_rate;
256 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
258 mve_audio_compressed = 0;
261 if (mas->bitsize == 16) {
262 if (mas->channels == 2) {
263 mas->format = AL_FORMAT_STEREO16;
264 } else if (mas->channels == 1) {
265 mas->format = AL_FORMAT_MONO16;
267 } else if (mas->bitsize == 8) {
268 if (mas->channels == 2) {
269 mas->format = AL_FORMAT_STEREO8;
270 } else if (mas->channels == 1) {
271 mas->format = AL_FORMAT_MONO8;
275 // somethings wrong, bail now
276 if (mas->format == AL_INVALID) {
277 mve_audio_canplay = 0;
278 audiobuf_created = 1;
282 mas->chan = oal_get_free_channel(1.0f, -1, SND_PRIORITY_MUST_PLAY);
284 if (mas->chan == NULL) {
285 mve_audio_canplay = 0;
286 audiobuf_created = 1;
290 alSourcef(mas->chan->source_id, AL_GAIN, 1.0f);
291 alSource3f(mas->chan->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
292 alSource3f(mas->chan->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
293 alSource3f(mas->chan->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
294 alSourcef(mas->chan->source_id, AL_ROLLOFF_FACTOR, 0.0f);
295 alSourcei(mas->chan->source_id, AL_SOURCE_RELATIVE, AL_TRUE);
297 for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
298 alGenBuffers(1, &mas->buffers[i]);
301 mve_audio_bufl_free.assign(mas->buffers, mas->buffers+MVE_AUDIO_BUFFERS);
303 audiobuf_created = 1;
304 mve_audio_canplay = 1;
307 // play and stream the audio
308 void mve_audio_play()
310 if (mve_audio_canplay) {
312 ALint status = AL_INVALID;
314 alGetSourcei(mas->chan->source_id, AL_BUFFERS_QUEUED, &queued);
315 alGetSourcei(mas->chan->source_id, AL_SOURCE_STATE, &status);
317 if ( (status != AL_PLAYING) && (queued > 0) ) {
318 alSourcePlay(mas->chan->source_id);
319 mve_audio_playing = 1;
324 // call this in shutdown to stop and close audio
325 static void mve_audio_stop()
327 if (!audiobuf_created)
333 mve_audio_playing = 0;
334 audiobuf_created = 0;
336 mve_audio_bufl_free.clear();
338 if (mas && mas->chan->source_id) {
339 alSourceStop(mas->chan->source_id);
341 alGetSourcei(mas->chan->source_id, AL_BUFFERS_PROCESSED, &processed);
343 while (processed > 0) {
344 alSourceUnqueueBuffers(mas->chan->source_id, 1, &bid);
348 alDeleteBuffers(MVE_AUDIO_BUFFERS, mas->buffers);
356 if (mve_audio_buf != NULL) {
358 mve_audio_buf = NULL;
361 mve_audio_buf_size = 0;
362 mve_audio_buf_offset = 0;
365 int mve_audio_data(ubyte major, ubyte *data)
367 static const int selected_chan = 1;
373 if (mve_audio_canplay) {
374 chan = mve_get_ushort(data + 2);
375 nsamp = mve_get_ushort(data + 4);
377 if (chan & selected_chan) {
378 if ( (mve_audio_buf_offset+nsamp+4) <= mve_audio_buf_size ) {
380 if (mve_audio_compressed) {
381 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
384 mveaudio_uncompress(mve_audio_buf+mve_audio_buf_offset, data, -1);
389 memcpy(mve_audio_buf+mve_audio_buf_offset, data, nsamp);
393 memset(mve_audio_buf+mve_audio_buf_offset, 0, nsamp);
396 mve_audio_buf_offset += nsamp;
398 mprintf(("MVE audio_buf overrun!!\n"));
401 alGetSourcei(mas->chan->source_id, AL_BUFFERS_PROCESSED, &processed);
404 alSourceUnqueueBuffers(mas->chan->source_id, 1, &bid);
405 mve_audio_bufl_free.push_back(bid);
409 if ( !mve_audio_bufl_free.empty() ) {
410 bid = mve_audio_bufl_free.back();
412 alBufferData(bid, mas->format, mve_audio_buf, mve_audio_buf_offset, mas->sample_rate);
413 alSourceQueueBuffers(mas->chan->source_id, 1, &bid);
415 mve_audio_buf_offset = 0;
416 mve_audio_bufl_free.pop_back();
419 if ( !mve_audio_playing ) {
428 /*************************
430 *************************/
432 int mve_video_createbuf(ubyte minor, ubyte *data)
434 if (videobuf_created)
438 short count, truecolor;
439 w = mve_get_short(data);
440 h = mve_get_short(data+2);
443 count = mve_get_short(data+4);
449 truecolor = mve_get_short(data+6);
457 // with Pierre's decoder16 fix in opcode 0xc, 8 should no longer be needed
458 g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 4);
460 if (g_vBackBuf1 == NULL) {
461 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
462 videobuf_created = 1;
466 g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
468 memset(g_vBackBuf1, 0, g_width * g_height * 4);
470 videobuf_created = 1;
475 static void mve_convert_and_draw()
479 ushort *pixels = (ushort *)g_vBackBuf1;
486 if (g_screenWidth > g_width) {
487 pDests += ((g_screenWidth - g_width) / 2) / 2;
489 if (g_screenHeight > g_height) {
490 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
493 for (y=0; y<g_height; y++) {
494 for (x = 0; x < g_width; x++) {
495 pDests[x] = (1<<15)|*pSrcs;
499 pDests += g_screenWidth;
503 void mve_video_display()
505 fix t1 = timer_get_fixed_seconds();
506 mve_convert_and_draw();
509 int h = g_screenHeight;
510 int w = g_screenWidth;
513 if (mve_scale_video) {
516 // centers on 1024x768, fills on 640x480
517 x = ((gr_screen.max_w - g_screenWidth) / 2);
518 y = ((gr_screen.max_h - g_screenHeight) / 2);
521 // micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
522 if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
523 // we are running slow so subtract desired time from actual and skip this frame
524 mve_video_skiptimer -= (micro_frame_delay/10);
527 // zero out so we can get a new count
528 mve_video_skiptimer = 0;
531 glBindTexture(GL_TEXTURE_2D, tex);
533 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
537 glTexCoord2f(0,0); glVertex2i(x,y);
538 glTexCoord2f(0,i2fl(256)/i2fl(hp2)); glVertex2i(x,y+256);
539 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
540 glTexCoord2f(i2fl(256)/i2fl(wp2),0); glVertex2i(x+256,y);
545 glTexCoord2f(0,i2fl(256)/i2fl(hp2)); glVertex2i(x,y+256);
546 glTexCoord2f(0,i2fl(h)/i2fl(hp2)); glVertex2i(x,y+h);
547 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+256,y+h);
548 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
553 glTexCoord2f(i2fl(256)/i2fl(wp2),0); glVertex2i(x+256,y);
554 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
555 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
556 glTexCoord2f(i2fl(512)/i2fl(wp2),0); glVertex2i(x+512,y);
561 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+256,y+256);
562 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+256,y+h);
563 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+512,y+h);
564 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
569 glTexCoord2f(i2fl(512)/i2fl(wp2),0); glVertex2i(x+512,y);
570 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
571 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+w,y+256);
572 glTexCoord2f(i2fl(w)/i2fl(wp2),0); glVertex2i(x+w,y);
577 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+512,y+256);
578 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+512,y+h);
579 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(h)/i2fl(hp2)); glVertex2i(x+w,y+h);
580 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2)); glVertex2i(x+w,y+256);
583 // centers on 1024x768, fills on 640x480
584 x = ((gr_screen.max_w - g_screenWidth) / 2);
585 y = ((gr_screen.max_h - g_screenHeight) / 2);
587 // DDOI - This is probably really fricking slow
588 int bitmap = bm_create (16, w, h, pixelbuf, 0);
589 gr_set_bitmap (bitmap);
596 os_poll (); /* DDOI - run event loop(s) */
599 fix t2 = timer_get_fixed_seconds();
601 // only get a new count if we are definitely through with old count
602 if ( mve_video_skiptimer == 0 ) {
603 // for a more accurate count convert the frame rate to a float and multiply
604 // by one-hundred-thousand before converting to an uint.
605 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
609 if ( k == SDLK_ESCAPE ) {
613 // fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
616 int mve_video_init(ubyte *data)
623 width = mve_get_short(data);
624 height = mve_get_short(data+2);
626 // DDOI - Allocate RGB565 pixel buffer
627 pixelbuf = (ushort *)malloc (width * height * 2);
629 if (pixelbuf == NULL) {
630 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
635 memset(pixelbuf, 0, width * height * 2);
637 g_screenWidth = width;
638 g_screenHeight = height;
643 tex_w = g_screenWidth;
644 tex_h = g_screenHeight;
646 // set height and width to a power of 2
647 for (i=0; i<16; i++ ) {
648 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) ) {
654 for (i=0; i<16; i++ ) {
655 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) ) {
661 // try to keep an 8:1 size ratio
670 glGenTextures(1, &tex);
675 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
680 glBindTexture(GL_TEXTURE_2D, tex);
682 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
683 glDepthFunc(GL_ALWAYS);
684 glDepthMask(GL_FALSE);
685 glDisable(GL_DEPTH_TEST);
686 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
687 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
689 if ( os_config_read_uint(NULL, NOX("ScaleMovies"), 0) == 1 ) {
690 float scale_by = (float)gr_screen.max_w / (float)g_screenWidth;
692 // don't bother setting anything if we aren't going to need it
693 if (scale_by != 1.0f) {
694 glMatrixMode(GL_MODELVIEW);
698 glScalef( scale_by, scale_by, 1.0f );
703 // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf so less of two evils...
704 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, wp2, hp2, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
707 memset(g_palette, 0, 768);
714 void mve_video_palette(ubyte *data)
717 start = mve_get_short(data);
718 count = mve_get_short(data+2);
719 memcpy(g_palette + 3*start, data+4, 3*count);
722 void mve_video_codemap(ubyte *data, int len)
728 void mve_video_data(ubyte *data, int len)
730 short nFrameHot, nFrameCold;
731 short nXoffset, nYoffset;
732 short nXsize, nYsize;
736 nFrameHot = mve_get_short(data);
737 nFrameCold = mve_get_short(data+2);
738 nXoffset = mve_get_short(data+4);
739 nYoffset = mve_get_short(data+6);
740 nXsize = mve_get_short(data+8);
741 nYsize = mve_get_short(data+10);
742 nFlags = mve_get_ushort(data+12);
745 temp = (ubyte *)g_vBackBuf1;
746 g_vBackBuf1 = g_vBackBuf2;
750 decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
758 void mve_init(MVESTREAM *mve)
760 // reset to default values
761 mve_audio_playing = 0;
762 mve_audio_canplay = 0;
763 mve_audio_compressed = 0;
764 mve_audio_buf_offset = 0;
765 audiobuf_created = 0;
767 videobuf_created = 0;
774 void mve_play(MVESTREAM *mve)
776 int init_timer = 0, timer_error = 0;
782 while (cont && mve_playing && !timer_error) {
783 cont = mve_play_next_chunk(mve);
785 if (micro_frame_delay && !init_timer) {
790 timer_error = mve_do_timer_wait();
800 if (pixelbuf != NULL) {
805 if (g_vBuffers != NULL) {
811 if (mve_scale_video) {
812 glMatrixMode(GL_MODELVIEW);
817 glDeleteTextures(1, &tex);
820 glEnable(GL_DEPTH_TEST);