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