]> icculus.org git repositories - taylor/freespace2.git/blob - src/movie/mveplayer.cpp
fix crash in debug builds
[taylor/freespace2.git] / src / movie / mveplayer.cpp
1 /*
2  * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * MVE movie playing routines
8  *
9  * $Log$
10  * Revision 1.7  2005/10/01 21:48:01  taylor
11  * various cleanups
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
14  *
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
20  *
21  * Revision 1.5  2005/03/31 21:26:02  taylor
22  * s/alGetSourceiv/alGetSourcei/
23  *
24  * Revision 1.4  2005/03/31 00:06:20  taylor
25  * go back to more accurate timer and allow video scaling for movies
26  *
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.
31  *
32  *
33  * $NoKeywords: $
34  */
35
36 #include "pstypes.h"
37 #include "mvelib.h"
38 #include "movie.h"
39 #include "2d.h"
40 #include "key.h"
41 #include "osapi.h"
42 #include "timer.h"
43 #include "sound.h"
44 #include "bmpman.h"
45 #include "osregistry.h"
46 #include "oal.h"
47 #include <vector>
48
49 static int mve_playing;
50
51
52 // timer variables
53 static int micro_frame_delay = 0;
54 static int timer_started = 0;
55 static int timer_created = 0;
56 static unsigned int timer_expire;
57 static Uint64 micro_timer_start = 0;
58 static Uint64 micro_timer_freq = 0;
59
60 // audio variables
61 #define MVE_AUDIO_BUFFERS 8  // total buffers to interact with stream
62
63 static std::vector<ALuint> mve_audio_bufl_free;
64 static ubyte *mve_audio_buf = NULL;
65 static size_t mve_audio_buf_size = 0;
66 static size_t mve_audio_buf_offset = 0;
67
68 static int mve_audio_playing = 0;
69 static int mve_audio_canplay = 0;
70 static int mve_audio_compressed = 0;
71 static int audiobuf_created = 0;
72
73 // struct for the audio stream information
74 struct mve_audio_t {
75         sound_channel *chan;
76         ALenum format;
77         int sample_rate;
78         int bytes_per_sec;
79         int channels;
80         int bitsize;
81         ALuint buffers[MVE_AUDIO_BUFFERS];
82 };
83
84 mve_audio_t *mas = NULL;  // mve_audio_stream
85
86
87
88 // video variables
89 int g_width, g_height;
90 void *g_vBuffers = NULL;
91 void *g_vBackBuf1, *g_vBackBuf2;
92 ushort *pixelbuf = NULL;
93 static ubyte *g_pCurMap=NULL;
94 static int g_nMapLength=0;
95 static int videobuf_created;
96
97
98 // the decoder
99 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
100
101 /*************************
102  * general handlers
103  *************************/
104 void mve_end_movie()
105 {
106         mve_playing = 0;
107 }
108
109 /*************************
110  * timer handlers
111  *************************/
112
113 int mve_timer_create(ubyte *data)
114 {
115         micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
116
117         micro_timer_start = SDL_GetPerformanceCounter();
118         micro_timer_freq = SDL_GetPerformanceFrequency();
119
120         timer_created = 1;
121
122         return 1;
123 }
124
125 static unsigned int mve_timer_get_microseconds()
126 {
127         Uint64 us = SDL_GetPerformanceCounter() - micro_timer_start;
128
129         us *= 1000000;
130         us /= micro_timer_freq;
131
132         return (unsigned int)us;
133 }
134
135 static void mve_timer_start(void)
136 {
137         if (!timer_created)
138                 return;
139
140         timer_expire = mve_timer_get_microseconds();
141         timer_expire += micro_frame_delay;
142
143         timer_started = 1;
144 }
145
146 static int mve_do_timer_wait(void)
147 {
148         if (!timer_started)
149                 return 0;
150
151         unsigned int tv, ts;
152
153         tv = mve_timer_get_microseconds();
154
155         if (tv > timer_expire)
156                 goto end;
157
158         ts = timer_expire - tv;
159
160         SDL_Delay(ts / 1000);
161
162 end:
163         timer_expire += micro_frame_delay;
164
165         return 0;
166 }
167
168 static void mve_timer_stop()
169 {
170         timer_expire = 0;
171         timer_started = 0;
172         timer_created = 0;
173
174         micro_frame_delay = 0;
175
176         micro_timer_start = 0;
177         micro_timer_freq = 0;
178 }
179
180 /*************************
181  * audio handlers
182  *************************/
183
184 // setup the audio information from the data stream
185 void mve_audio_createbuf(ubyte minor, ubyte *data)
186 {
187         if (audiobuf_created)
188                 return;
189
190         // if game sound disabled don't try and play movie audio
191         if ( !Sound_enabled ) {
192                 mve_audio_canplay = 0;
193                 audiobuf_created = 1;
194                 return;
195         }
196
197         int flags, desired_buffer, sample_rate;
198
199         mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
200
201         if (mas == NULL) {
202                 mve_audio_canplay = 0;
203                 audiobuf_created = 1;
204                 return;
205         }
206
207         memset(mas, 0, sizeof(mve_audio_t));
208
209         mas->format = AL_INVALID;
210
211         flags = mve_get_ushort(data + 2);
212         sample_rate = mve_get_ushort(data + 4);
213         desired_buffer = mve_get_int(data + 6);
214
215         if (desired_buffer > 0) {
216                 mve_audio_buf = (ubyte*) malloc (desired_buffer);
217
218                 if (mve_audio_buf == NULL) {
219                         mve_audio_canplay = 0;
220                         audiobuf_created = 1;
221                         return;
222                 }
223
224                 mve_audio_buf_size = desired_buffer;
225                 mve_audio_buf_offset = 0;
226         } else {
227                 mve_audio_canplay = 0;
228                 audiobuf_created = 1;
229                 return;
230         }
231
232         mas->channels = (flags & 0x0001) ? 2 : 1;
233         mas->bitsize = (flags & 0x0002) ? 16 : 8;
234
235         mas->sample_rate = sample_rate;
236
237         if (minor > 0) {
238                 mve_audio_compressed = flags & 0x0004 ? 1 : 0;
239         } else {
240                 mve_audio_compressed = 0;
241         }
242
243         if (mas->bitsize == 16) {
244                 if (mas->channels == 2) {
245                         mas->format = AL_FORMAT_STEREO16;
246                 } else if (mas->channels == 1) {
247                         mas->format = AL_FORMAT_MONO16;
248                 }
249         } else if (mas->bitsize == 8) {
250                 if (mas->channels == 2) {
251                         mas->format = AL_FORMAT_STEREO8;
252                 } else if (mas->channels == 1) {
253                         mas->format = AL_FORMAT_MONO8;
254                 }
255         }
256
257         // somethings wrong, bail now
258         if (mas->format == AL_INVALID) {
259                 mve_audio_canplay = 0;
260                 audiobuf_created = 1;
261                 return;
262         }
263
264         oal_check_for_errors("mve_audio_createbuf() begin");
265
266         for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
267                 alGenBuffers(1, &mas->buffers[i]);
268
269                 if ( !mas->buffers[i] ) {
270                         mve_audio_canplay = 0;
271                         audiobuf_created = 1;
272                         return;
273                 }
274         }
275
276         mve_audio_bufl_free.assign(mas->buffers, mas->buffers+MVE_AUDIO_BUFFERS);
277
278         mas->chan = oal_get_free_channel(1.0f, -1, SND_PRIORITY_MUST_PLAY);
279
280         if (mas->chan == NULL) {
281                 mve_audio_canplay = 0;
282                 audiobuf_created = 1;
283                 return;
284         }
285
286         alSourcef(mas->chan->source_id, AL_GAIN, 1.0f);
287         alSource3f(mas->chan->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
288         alSource3f(mas->chan->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
289         alSource3f(mas->chan->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
290         alSourcef(mas->chan->source_id, AL_ROLLOFF_FACTOR, 0.0f);
291         alSourcei(mas->chan->source_id, AL_SOURCE_RELATIVE, AL_TRUE);
292         alSourcei(mas->chan->source_id, AL_LOOPING, AL_FALSE);
293
294         oal_check_for_errors("mve_audio_createbuf() end");
295
296         audiobuf_created = 1;
297         mve_audio_canplay = 1;
298 }
299
300 // play and stream the audio
301 void mve_audio_play()
302 {
303         if (mve_audio_canplay) {
304                 ALint queued = 0;
305                 ALint status = AL_INVALID;
306
307                 oal_check_for_errors("mve_audio_play() begin");
308
309                 alGetSourcei(mas->chan->source_id, AL_BUFFERS_QUEUED, &queued);
310                 alGetSourcei(mas->chan->source_id, AL_SOURCE_STATE, &status);
311
312                 if ( (status != AL_PLAYING) && (queued > 0) ) {
313                         alSourcePlay(mas->chan->source_id);
314                         mve_audio_playing = 1;
315                 }
316
317                 oal_check_for_errors("mve_audio_play() end");
318         }
319 }
320
321 // call this in shutdown to stop and close audio
322 static void mve_audio_stop()
323 {
324         if (!audiobuf_created)
325                 return;
326
327         oal_check_for_errors("mve_audio_stop() begin");
328
329         mve_audio_playing = 0;
330         mve_audio_canplay = 0;
331         mve_audio_compressed = 0;
332
333         audiobuf_created = 0;
334
335         mve_audio_bufl_free.clear();
336
337         if (mas) {
338                 if (mas->chan) {
339                         alSourceStop(mas->chan->source_id);
340
341                         // detach buffers from source so that we can delete them
342                         alSourcei(mas->chan->source_id, AL_BUFFER, 0);
343                 }
344
345                 for (int i = 0; i < MVE_AUDIO_BUFFERS; i++) {
346                         if ( alIsBuffer(mas->buffers[i]) ) {
347                                 alDeleteBuffers(1, &mas->buffers[i]);
348                         }
349                 }
350         }
351
352         if (mas != NULL) {
353                 free(mas);
354                 mas = NULL;
355         }
356
357         if (mve_audio_buf != NULL) {
358                 free(mve_audio_buf);
359                 mve_audio_buf = NULL;
360         }
361
362         mve_audio_buf_size = 0;
363         mve_audio_buf_offset = 0;
364
365         oal_check_for_errors("mve_audio_stop() end");
366 }
367
368 int mve_audio_data(ubyte major, ubyte *data)
369 {
370         static const int selected_chan = 1;
371         int chan;
372         int nsamp;
373         ALint processed = 0;
374         ALuint bid;
375
376         if (mve_audio_canplay) {
377                 chan = mve_get_ushort(data + 2);
378                 nsamp = mve_get_ushort(data + 4);
379
380                 if (chan & selected_chan) {
381                         oal_check_for_errors("mve_audio_data() begin");
382
383                         if ( (mve_audio_buf_offset+nsamp+4) <= mve_audio_buf_size ) {
384                                 if (major == 8) {
385                                         if (mve_audio_compressed) {
386                                                 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
387                                                 nsamp += 4;
388
389                                                 mveaudio_uncompress(mve_audio_buf+mve_audio_buf_offset, data, -1);
390                                         } else {
391                                                 nsamp -= 8;
392                                                 data += 8;
393
394                                                 memcpy(mve_audio_buf+mve_audio_buf_offset, data, nsamp);
395                                         }
396                                 } else {
397                                         // silence
398                                         memset(mve_audio_buf+mve_audio_buf_offset, 0, nsamp);
399                                 }
400
401                                 mve_audio_buf_offset += nsamp;
402                         } else {
403                                 mprintf(("MVE audio_buf overrun!!\n"));
404                         }
405
406                         alGetSourcei(mas->chan->source_id, AL_BUFFERS_PROCESSED, &processed);
407
408                         while (processed) {
409                                 alSourceUnqueueBuffers(mas->chan->source_id, 1, &bid);
410
411                                 mve_audio_bufl_free.push_back(bid);
412                                 --processed;
413                         }
414
415                         if ( !mve_audio_bufl_free.empty() ) {
416                                 bid = mve_audio_bufl_free.back();
417
418                                 alBufferData(bid, mas->format, mve_audio_buf, mve_audio_buf_offset, mas->sample_rate);
419                                 alSourceQueueBuffers(mas->chan->source_id, 1, &bid);
420
421                                 mve_audio_buf_offset = 0;
422                                 mve_audio_bufl_free.pop_back();
423                         }
424
425                         if ( !mve_audio_playing ) {
426                                 mve_audio_play();
427                         }
428
429                         oal_check_for_errors("mve_audio_data() end");
430                 }
431         }
432
433         return 1;
434 }
435
436 /*************************
437  * video handlers
438  *************************/
439
440 int mve_video_createbuf(ubyte minor, ubyte *data)
441 {
442         if (videobuf_created)
443                 return 1;
444
445         short w, h;
446
447         w = mve_get_short(data);
448         h = mve_get_short(data+2);
449
450         g_width = w << 3;
451         g_height = h << 3;
452
453         // with Pierre's decoder16 fix in opcode 0xc, 8 should no longer be needed
454         g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 4);
455
456         if (g_vBackBuf1 == NULL) {
457                 mprintf(("MVE-ERROR: Can't allocate video buffer\n"));
458                 videobuf_created = 1;
459                 return 0;
460         }
461
462         g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
463
464         memset(g_vBackBuf1, 0, g_width * g_height * 4);
465
466         // DDOI - Allocate RGB565 pixel buffer
467         pixelbuf = (ushort *)malloc (g_width * g_height * 2);
468
469         if (pixelbuf == NULL) {
470                 mprintf(("MVE-ERROR: Can't allocate memory for pixelbuf\n"));
471                 videobuf_created = 1;
472                 return 0;
473         }
474
475         memset(pixelbuf, 0, g_width * g_height * 2);
476
477         gr_stream_start(-1, -1, g_width, g_height);
478
479         videobuf_created = 1;
480
481         return 1;
482 }
483
484 static void mve_convert_and_draw()
485 {
486         ushort *pDests;
487         ushort *pSrcs;
488         ushort *pixels = (ushort *)g_vBackBuf1;
489         ushort px;
490         int x, y;
491         ubyte r, g, b, a;
492
493         pSrcs = pixels;
494
495         pDests = pixelbuf;
496
497         for (y=0; y<g_height; y++) {
498                 for (x = 0; x < g_width; x++) {
499                         // convert from abgr to rgba
500                         px = (1<<15)|*pSrcs;
501
502                         r = ubyte((px & 0x7C00) >> 10);
503                         g = ubyte((px & 0x3E0) >> 5);
504                         b = ubyte((px & 0x1F) >> 0);
505                         a = ubyte((px & 0x8000) >> 15);
506
507                         pDests[x] = (r << 11) | (g << 6) | (b << 1) | (a << 0);
508
509                         pSrcs++;
510                 }
511                 pDests += g_width;
512         }
513 }
514
515 void mve_video_display()
516 {
517         static uint mve_video_skiptimer = 0;
518
519         fix t1 = timer_get_fixed_seconds();
520
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);
525                 return;
526         } else {
527                 // zero out so we can get a new count
528                 mve_video_skiptimer = 0;
529         }
530
531         mve_convert_and_draw();
532
533         gr_stream_frame( (ubyte*)pixelbuf );
534
535         gr_flip();
536
537         fix t2 = timer_get_fixed_seconds();
538
539         // only get a new count if we are definitely through with old count
540         if ( mve_video_skiptimer == 0 ) {
541                 // for a more accurate count convert the frame rate to a float and multiply
542                 // by one-hundred-thousand before converting to an uint.
543                 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
544         }
545 }
546
547 void mve_video_codemap(ubyte *data, int len)
548 {
549         g_pCurMap = data;
550         g_nMapLength = len;
551 }
552
553 void mve_video_data(ubyte *data, int len)
554 {
555         ushort nFlags;
556         ubyte *temp;
557
558         nFlags = mve_get_ushort(data+12);
559
560         if (nFlags & 1) {
561                 temp = (ubyte *)g_vBackBuf1;
562                 g_vBackBuf1 = g_vBackBuf2;
563                 g_vBackBuf2 = temp;
564         }
565
566         decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
567 }
568
569 void mve_end_chunk()
570 {
571         g_pCurMap = NULL;
572 }
573
574 void mve_init(MVESTREAM *mve)
575 {
576         // reset to default values
577         mve_audio_playing = 0;
578         mve_audio_canplay = 0;
579         mve_audio_compressed = 0;
580         mve_audio_buf_offset = 0;
581         audiobuf_created = 0;
582
583         videobuf_created = 0;
584
585         mve_playing = 1;
586 }
587
588 void mve_play(MVESTREAM *mve)
589 {
590         int init_timer = 0, timer_error = 0;
591         int cont = 1;
592
593         if (!timer_started)
594                 mve_timer_start();
595
596         while (cont && mve_playing && !timer_error) {
597                 cont = mve_play_next_chunk(mve);
598
599                 if (micro_frame_delay && !init_timer) {
600                         mve_timer_start();
601                         init_timer = 1;
602                 }
603
604                 timer_error = mve_do_timer_wait();
605
606                 os_poll();
607
608                 if (key_inkey() == SDLK_ESCAPE) {
609                         mve_playing = 0;
610                 }
611         }
612 }
613
614 void mve_shutdown()
615 {
616         mve_audio_stop();
617
618         mve_timer_stop();
619
620         if (pixelbuf != NULL) {
621                 free(pixelbuf);
622                 pixelbuf = NULL;
623         }
624
625         if (g_vBuffers != NULL) {
626                 free(g_vBuffers);
627                 g_vBuffers = NULL;
628         }
629
630         gr_stream_stop();
631 }