1 /* Ogg Theora/Vorbis video backend
2 *
3 * TODO:
4 * - seeking
5 * - generate video frame events
6 * - better ycbcr->rgb
7 * - improve frame skipping
8 * - Ogg Skeleton support
9 * - pass Theora test suite
10 *
11 * NOTE: we treat Ogg timestamps as the beginning of frames. In reality they
12 * are end timestamps. Normally this should not be noticeable, but can make a
13 * difference.
14 */
15
16 #include <stdio.h>
17 #include "allegro5/allegro5.h"
18 #include "allegro5/allegro_audio.h"
19 #include "allegro5/allegro_video.h"
20 #include "allegro5/internal/aintern_vector.h"
21 #include "allegro5/internal/aintern_video.h"
22
23 #include <ogg/ogg.h>
24 #include <theora/theora.h>
25 #include <theora/theoradec.h>
26 #include <vorbis/codec.h>
27
28 ALLEGRO_DEBUG_CHANNEL("video")
29
30
31 /* XXX probably should be based on stream parameters */
32 static const int NUM_FRAGS = 2;
33 static const int FRAG_SAMPLES = 4096;
34 static const int RGB_PIXEL_FORMAT = ALLEGRO_PIXEL_FORMAT_ABGR_8888;
35
36
37 typedef struct OGG_VIDEO OGG_VIDEO;
38 typedef struct STREAM STREAM;
39 typedef struct THEORA_STREAM THEORA_STREAM;
40 typedef struct VORBIS_STREAM VORBIS_STREAM;
41 typedef struct PACKET_NODE PACKET_NODE;
42
43 enum {
44 STREAM_TYPE_UNKNOWN = 0,
45 STREAM_TYPE_THEORA,
46 STREAM_TYPE_VORBIS
47 };
48
49 struct PACKET_NODE {
50 PACKET_NODE *next;
51 ogg_packet pkt;
52 };
53
54 struct THEORA_STREAM {
55 th_info info;
56 th_comment comment;
57 th_setup_info *setup;
58 th_dec_ctx *ctx;
59 ogg_int64_t prev_framenum;
60 double frame_duration;
61 };
62
63 struct VORBIS_STREAM {
64 vorbis_info info;
65 vorbis_comment comment;
66 bool inited_for_data;
67 vorbis_dsp_state dsp;
68 vorbis_block block;
69 int channels;
70 float *next_fragment; /* channels * FRAG_SAMPLES elements */
71 int next_fragment_pos;
72 };
73
74 struct STREAM {
75 int stream_type;
76 bool active;
77 bool headers_done;
78 ogg_stream_state state;
79 PACKET_NODE *packet_queue;
80 union {
81 THEORA_STREAM theora;
82 VORBIS_STREAM vorbis;
83 } u;
84 };
85
86 struct OGG_VIDEO {
87 ALLEGRO_FILE *fp;
88 bool reached_eof;
89 ogg_sync_state sync_state;
90 _AL_VECTOR streams; /* vector of STREAM pointers */
91 STREAM *selected_video_stream; /* one of the streams */
92 STREAM *selected_audio_stream; /* one of the streams */
93 int seek_counter;
94
95 /* Video output. */
96 th_pixel_fmt pixel_fmt;
97 th_ycbcr_buffer buffer;
98 bool buffer_dirty;
99 unsigned char* rgb_data;
100 ALLEGRO_BITMAP *frame_bmp;
101 ALLEGRO_BITMAP *pic_bmp; /* frame_bmp, or subbitmap thereof */
102
103 ALLEGRO_EVENT_SOURCE evtsrc;
104 ALLEGRO_EVENT_QUEUE *queue;
105 ALLEGRO_MUTEX *mutex;
106 ALLEGRO_COND *cond;
107 ALLEGRO_THREAD *thread;
108 };
109
110
111 /* forward declarations */
112 static bool ogv_close_video(ALLEGRO_VIDEO *video);
113
114
115 /* Packet queue. */
116
create_packet_node(ogg_packet * packet)117 static PACKET_NODE *create_packet_node(ogg_packet *packet)
118 {
119 PACKET_NODE *node = al_malloc(sizeof(PACKET_NODE));
120
121 node->next = NULL;
122 node->pkt = *packet;
123 node->pkt.packet = al_malloc(packet->bytes);
124 memcpy(node->pkt.packet, packet->packet, packet->bytes);
125
126 return node;
127 }
128
free_packet_node(PACKET_NODE * node)129 static void free_packet_node(PACKET_NODE *node)
130 {
131 ASSERT(node->next == NULL);
132
133 al_free(node->pkt.packet);
134 al_free(node);
135 }
136
add_tail_packet(STREAM * stream,PACKET_NODE * node)137 static void add_tail_packet(STREAM *stream, PACKET_NODE *node)
138 {
139 PACKET_NODE *cur;
140
141 ASSERT(node->next == NULL);
142
143 for (cur = stream->packet_queue; cur != NULL; cur = cur->next) {
144 if (cur->next == NULL) {
145 cur->next = node;
146 ASSERT(cur->pkt.packetno < node->pkt.packetno);
147 return;
148 }
149 }
150
151 ASSERT(stream->packet_queue == NULL);
152 stream->packet_queue = node;
153 }
154
add_head_packet(STREAM * stream,PACKET_NODE * node)155 static void add_head_packet(STREAM *stream, PACKET_NODE *node)
156 {
157 ASSERT(node->next == NULL);
158
159 node->next = stream->packet_queue;
160 stream->packet_queue = node;
161
162 if (node->next) {
163 ASSERT(node->pkt.packetno < node->next->pkt.packetno);
164 }
165 }
166
take_head_packet(STREAM * stream)167 static PACKET_NODE *take_head_packet(STREAM *stream)
168 {
169 PACKET_NODE *cur;
170
171 cur = stream->packet_queue;
172 if (!cur) {
173 return NULL;
174 }
175 if (cur->next) {
176 ASSERT(cur->pkt.packetno < cur->next->pkt.packetno);
177 }
178 stream->packet_queue = cur->next;
179 cur->next = NULL;
180 return cur;
181 }
182
free_packet_queue(STREAM * stream)183 static void free_packet_queue(STREAM *stream)
184 {
185 while (stream->packet_queue) {
186 PACKET_NODE *node = stream->packet_queue;
187 stream->packet_queue = node->next;
188 node->next = NULL;
189 free_packet_node(node);
190 }
191 }
192
deactivate_stream(STREAM * stream)193 static void deactivate_stream(STREAM *stream)
194 {
195 stream->active = false;
196 free_packet_queue(stream);
197 }
198
199
200 /* Logical streams. */
201
create_stream(OGG_VIDEO * ogv,int serial)202 static STREAM *create_stream(OGG_VIDEO *ogv, int serial)
203 {
204 STREAM *stream;
205 STREAM **slot;
206
207 stream = al_calloc(1, sizeof(STREAM));
208 stream->stream_type = STREAM_TYPE_UNKNOWN;
209 stream->active = true;
210 stream->headers_done = false;
211 ogg_stream_init(&stream->state, serial);
212 stream->packet_queue = NULL;
213
214 slot = _al_vector_alloc_back(&ogv->streams);
215 (*slot) = stream;
216
217 return stream;
218 }
219
find_stream(OGG_VIDEO * ogv,int serial)220 static STREAM *find_stream(OGG_VIDEO *ogv, int serial)
221 {
222 unsigned i;
223
224 for (i = 0; i < _al_vector_size(&ogv->streams); i++) {
225 STREAM **slot = _al_vector_ref(&ogv->streams, i);
226 STREAM *stream = *slot;
227
228 if (stream->state.serialno == serial) {
229 return stream;
230 }
231 }
232
233 return NULL;
234 }
235
free_stream(STREAM * stream)236 static void free_stream(STREAM *stream)
237 {
238 ASSERT(stream);
239
240 ogg_stream_clear(&stream->state);
241
242 free_packet_queue(stream);
243
244 switch (stream->stream_type) {
245 case STREAM_TYPE_UNKNOWN:
246 break;
247
248 case STREAM_TYPE_THEORA:
249 {
250 THEORA_STREAM *tstream = &stream->u.theora;
251
252 ALLEGRO_DEBUG("Clean up Theora.\n");
253 th_info_clear(&tstream->info);
254 th_comment_clear(&tstream->comment);
255 if (tstream->setup) {
256 th_setup_free(tstream->setup);
257 }
258 if (tstream->ctx) {
259 th_decode_free(tstream->ctx);
260 }
261 }
262 break;
263
264 case STREAM_TYPE_VORBIS:
265 {
266 VORBIS_STREAM *vstream = &stream->u.vorbis;
267
268 ALLEGRO_DEBUG("Clean up Vorbis.\n");
269 vorbis_info_clear(&vstream->info);
270 vorbis_comment_clear(&vstream->comment);
271 if (vstream->inited_for_data) {
272 vorbis_block_clear(&vstream->block);
273 vorbis_dsp_clear(&vstream->dsp);
274 }
275 al_free(vstream->next_fragment);
276 }
277 break;
278 }
279
280 al_free(stream);
281 }
282
283 /* Returns true if got a page. */
read_page(OGG_VIDEO * ogv,ogg_page * page)284 static bool read_page(OGG_VIDEO *ogv, ogg_page *page)
285 {
286 const int buffer_size = 4096;
287
288 if (al_feof(ogv->fp) || al_ferror(ogv->fp)) {
289 ogv->reached_eof = true;
290 return ogg_sync_pageout(&ogv->sync_state, page) == 1;
291 }
292
293 while (ogg_sync_pageout(&ogv->sync_state, page) != 1) {
294 char *buffer;
295 size_t bytes;
296 int rc;
297
298 buffer = ogg_sync_buffer(&ogv->sync_state, buffer_size);
299 bytes = al_fread(ogv->fp, buffer, buffer_size);
300 if (bytes == 0) {
301 ALLEGRO_DEBUG("End of file.\n");
302 return false;
303 }
304
305 rc = ogg_sync_wrote(&ogv->sync_state, bytes);
306 ASSERT(rc == 0);
307 }
308
309 return true;
310 }
311
312 /* Return true if got a packet for the stream. */
read_packet(OGG_VIDEO * ogv,STREAM * stream,ogg_packet * packet)313 static bool read_packet(OGG_VIDEO *ogv, STREAM *stream, ogg_packet *packet)
314 {
315 ogg_page page;
316 int rc;
317
318 for (;;) {
319 rc = ogg_stream_packetout(&stream->state, packet);
320 if (rc == 1) {
321 /* Got a packet for stream. */
322 return true;
323 }
324
325 if (read_page(ogv, &page)) {
326 STREAM *page_stream = find_stream(ogv, ogg_page_serialno(&page));
327
328 if (page_stream && page_stream->active) {
329 rc = ogg_stream_pagein(&page_stream->state, &page);
330 ASSERT(rc == 0);
331 }
332 }
333 else {
334 return false;
335 }
336 }
337 }
338
339
340 /* Header decoding. */
341
try_decode_theora_header(STREAM * stream,ogg_packet * packet)342 static bool try_decode_theora_header(STREAM *stream, ogg_packet *packet)
343 {
344 int rc;
345
346 switch (stream->stream_type) {
347 case STREAM_TYPE_UNKNOWN:
348 th_info_init(&stream->u.theora.info);
349 th_comment_init(&stream->u.theora.comment);
350 break;
351 case STREAM_TYPE_THEORA:
352 break;
353 case STREAM_TYPE_VORBIS:
354 return false;
355 }
356
357 if (stream->headers_done) {
358 add_tail_packet(stream, create_packet_node(packet));
359 return true;
360 }
361
362 rc = th_decode_headerin(&stream->u.theora.info, &stream->u.theora.comment,
363 &stream->u.theora.setup, packet);
364
365 if (rc > 0) {
366 /* Successfully parsed a Theora header. */
367 if (stream->stream_type == STREAM_TYPE_UNKNOWN) {
368 ALLEGRO_DEBUG("Found Theora stream.\n");
369 stream->stream_type = STREAM_TYPE_THEORA;
370 }
371 return true;
372 }
373
374 if (rc == 0) {
375 /* First Theora data packet. */
376 add_tail_packet(stream, create_packet_node(packet));
377 stream->headers_done = true;
378 return true;
379 }
380
381 th_info_clear(&stream->u.theora.info);
382 th_comment_clear(&stream->u.theora.comment);
383 return false;
384 }
385
try_decode_vorbis_header(STREAM * stream,ogg_packet * packet)386 static int try_decode_vorbis_header(STREAM *stream, ogg_packet *packet)
387 {
388 int rc;
389
390 switch (stream->stream_type) {
391 case STREAM_TYPE_UNKNOWN:
392 vorbis_info_init(&stream->u.vorbis.info);
393 vorbis_comment_init(&stream->u.vorbis.comment);
394 break;
395 case STREAM_TYPE_VORBIS:
396 break;
397 case STREAM_TYPE_THEORA:
398 return false;
399 }
400
401 rc = vorbis_synthesis_headerin(&stream->u.vorbis.info,
402 &stream->u.vorbis.comment, packet);
403
404 if (rc == 0) {
405 /* Successfully parsed a Vorbis header. */
406 if (stream->stream_type == STREAM_TYPE_UNKNOWN) {
407 ALLEGRO_INFO("Found Vorbis stream.\n");
408 stream->stream_type = STREAM_TYPE_VORBIS;
409 stream->u.vorbis.inited_for_data = false;
410 }
411 return true;
412 }
413
414 if (stream->stream_type == STREAM_TYPE_VORBIS && rc == OV_ENOTVORBIS) {
415 /* First data packet. */
416 add_tail_packet(stream, create_packet_node(packet));
417 stream->headers_done = true;
418 return true;
419 }
420
421 vorbis_info_clear(&stream->u.vorbis.info);
422 vorbis_comment_clear(&stream->u.vorbis.comment);
423 return false;
424 }
425
all_headers_done(OGG_VIDEO * ogv)426 static bool all_headers_done(OGG_VIDEO *ogv)
427 {
428 bool have_something = false;
429 unsigned i;
430
431 for (i = 0; i < _al_vector_size(&ogv->streams); i++) {
432 STREAM **slot = _al_vector_ref(&ogv->streams, i);
433 STREAM *stream = *slot;
434
435 switch (stream->stream_type) {
436 case STREAM_TYPE_THEORA:
437 case STREAM_TYPE_VORBIS:
438 have_something = true;
439 if (!stream->headers_done)
440 return false;
441 break;
442 case STREAM_TYPE_UNKNOWN:
443 break;
444 }
445 }
446
447 return have_something;
448 }
449
read_headers(OGG_VIDEO * ogv)450 static void read_headers(OGG_VIDEO *ogv)
451 {
452 ogg_page page;
453 ogg_packet packet;
454 STREAM *stream;
455 int serial;
456 int rc;
457
458 ALLEGRO_DEBUG("Begin reading headers.\n");
459
460 do {
461 /* Read a page of data. */
462 if (!read_page(ogv, &page)) {
463 break;
464 }
465
466 /* Which stream is this page for? */
467 serial = ogg_page_serialno(&page);
468
469 /* Start a new stream or find an existing one. */
470 if (ogg_page_bos(&page)) {
471 stream = create_stream(ogv, serial);
472 }
473 else {
474 stream = find_stream(ogv, serial);
475 }
476
477 if (!stream) {
478 ALLEGRO_WARN("No stream for serial: %x\n", serial);
479 continue;
480 }
481
482 if (!stream->active) {
483 continue;
484 }
485
486 /* Add the page to the stream. */
487 rc = ogg_stream_pagein(&stream->state, &page);
488 ASSERT(rc == 0);
489
490 /* Look for a complete packet. */
491 rc = ogg_stream_packetpeek(&stream->state, &packet);
492 if (rc == 0) {
493 continue;
494 }
495 if (rc == -1) {
496 ALLEGRO_WARN("No packet due to lost sync or hole in data.\n");
497 continue;
498 }
499
500 /* Try to decode the packet as a Theora or Vorbis header. */
501 if (!try_decode_theora_header(stream, &packet)) {
502 if (!try_decode_vorbis_header(stream, &packet)) {
503 ALLEGRO_DEBUG("Unknown packet type ignored.\n");
504 }
505 }
506
507 /* Consume the packet. */
508 rc = ogg_stream_packetout(&stream->state, &packet);
509 ASSERT(rc == 1);
510
511 } while (!all_headers_done(ogv));
512
513 ALLEGRO_DEBUG("End reading headers.\n");
514 }
515
516
517 /* Vorbis streams. */
518
setup_vorbis_stream_decode(ALLEGRO_VIDEO * video,STREAM * stream)519 static void setup_vorbis_stream_decode(ALLEGRO_VIDEO *video, STREAM *stream)
520 {
521 VORBIS_STREAM * const vstream = &stream->u.vorbis;
522 int rc;
523
524 rc = vorbis_synthesis_init(&vstream->dsp, &vstream->info);
525 ASSERT(rc == 0);
526
527 rc = vorbis_block_init(&vstream->dsp, &vstream->block);
528 ASSERT(rc == 0);
529
530 vstream->inited_for_data = true;
531
532 video->audio_rate = vstream->info.rate;
533 vstream->channels = vstream->info.channels;
534
535 vstream->next_fragment =
536 al_calloc(vstream->channels * FRAG_SAMPLES, sizeof(float));
537
538 ALLEGRO_INFO("Audio rate: %f\n", video->audio_rate);
539 ALLEGRO_INFO("Audio channels: %d\n", vstream->channels);
540 }
541
handle_vorbis_data(VORBIS_STREAM * vstream,ogg_packet * packet)542 static void handle_vorbis_data(VORBIS_STREAM *vstream, ogg_packet *packet)
543 {
544 int rc;
545
546 rc = vorbis_synthesis(&vstream->block, packet);
547 if (rc != 0) {
548 ALLEGRO_ERROR("vorbis_synthesis returned %d\n", rc);
549 return;
550 }
551
552 rc = vorbis_synthesis_blockin(&vstream->dsp, &vstream->block);
553 if (rc != 0) {
554 ALLEGRO_ERROR("vorbis_synthesis_blockin returned %d\n", rc);
555 return;
556 }
557 }
558
generate_next_audio_fragment(VORBIS_STREAM * vstream)559 static bool generate_next_audio_fragment(VORBIS_STREAM *vstream)
560 {
561 float **pcm = NULL;
562 float *p;
563 int samples;
564 int i, ch;
565 int rc;
566
567 samples = vorbis_synthesis_pcmout(&vstream->dsp, &pcm);
568 if (samples == 0) {
569 return false;
570 }
571
572 if (samples > FRAG_SAMPLES - vstream->next_fragment_pos) {
573 samples = FRAG_SAMPLES - vstream->next_fragment_pos;
574 }
575
576 ASSERT(vstream->next_fragment);
577 p = &vstream->next_fragment[
578 vstream->channels * vstream->next_fragment_pos];
579
580 if (vstream->channels == 2) {
581 for (i = 0; i < samples; i++) {
582 *p++ = pcm[0][i];
583 *p++ = pcm[1][i];
584 }
585 }
586 else if (vstream->channels == 1) {
587 for (i = 0; i < samples; i++) {
588 *p++ = pcm[0][i];
589 }
590 }
591 else {
592 for (i = 0; i < samples; i++) {
593 for (ch = 0; ch < vstream->channels; ch++) {
594 *p++ = pcm[ch][i];
595 }
596 }
597 }
598
599 vstream->next_fragment_pos += samples;
600
601 rc = vorbis_synthesis_read(&vstream->dsp, samples);
602 ASSERT(rc == 0);
603 return true;
604 }
605
poll_vorbis_decode(OGG_VIDEO * ogv,STREAM * vstream_outer)606 static void poll_vorbis_decode(OGG_VIDEO *ogv, STREAM *vstream_outer)
607 {
608 VORBIS_STREAM * const vstream = &vstream_outer->u.vorbis;
609
610 while (vstream->next_fragment_pos < FRAG_SAMPLES
611 && generate_next_audio_fragment(vstream))
612 {
613 }
614
615 while (vstream->next_fragment_pos < FRAG_SAMPLES) {
616 PACKET_NODE *node;
617 ogg_packet packet;
618
619 node = take_head_packet(vstream_outer);
620 if (node) {
621 handle_vorbis_data(vstream, &node->pkt);
622 generate_next_audio_fragment(vstream);
623 free_packet_node(node);
624 }
625 else if (read_packet(ogv, vstream_outer, &packet)) {
626 handle_vorbis_data(vstream, &packet);
627 generate_next_audio_fragment(vstream);
628 }
629 else {
630 break;
631 }
632 }
633 }
634
create_audio_stream(const ALLEGRO_VIDEO * video,const STREAM * vstream_outer)635 static ALLEGRO_AUDIO_STREAM *create_audio_stream(const ALLEGRO_VIDEO *video,
636 const STREAM *vstream_outer)
637 {
638 const VORBIS_STREAM *vstream = &vstream_outer->u.vorbis;
639 ALLEGRO_AUDIO_STREAM *audio;
640 int chanconf;
641 int rc;
642
643 switch (vstream->channels) {
644 case 1: chanconf = ALLEGRO_CHANNEL_CONF_1; break;
645 case 2: chanconf = ALLEGRO_CHANNEL_CONF_2; break;
646 case 3: chanconf = ALLEGRO_CHANNEL_CONF_3; break;
647 case 4: chanconf = ALLEGRO_CHANNEL_CONF_4; break;
648 case 6: chanconf = ALLEGRO_CHANNEL_CONF_5_1; break;
649 case 7: chanconf = ALLEGRO_CHANNEL_CONF_6_1; break;
650 case 8: chanconf = ALLEGRO_CHANNEL_CONF_7_1; break;
651 default:
652 ALLEGRO_WARN("Unsupported number of channels: %d\n",
653 vstream->channels);
654 return NULL;
655 }
656
657 audio = al_create_audio_stream(NUM_FRAGS, FRAG_SAMPLES,
658 vstream->info.rate, ALLEGRO_AUDIO_DEPTH_FLOAT32, chanconf);
659 if (!audio) {
660 ALLEGRO_ERROR("Could not create audio stream.\n");
661 return NULL;
662 }
663
664 if (video->mixer) {
665 rc = al_attach_audio_stream_to_mixer(audio, video->mixer);
666 }
667 else if (video->voice) {
668 rc = al_attach_audio_stream_to_voice(audio, video->voice);
669 }
670 else {
671 rc = al_attach_audio_stream_to_mixer(audio, al_get_default_mixer());
672 }
673
674 if (rc) {
675 ALLEGRO_DEBUG("Audio stream ready.\n");
676 }
677 else {
678 ALLEGRO_ERROR("Could not attach audio stream.\n");
679 al_destroy_audio_stream(audio);
680 audio = NULL;
681 }
682
683 return audio;
684 }
685
update_audio_fragment(ALLEGRO_AUDIO_STREAM * audio_stream,VORBIS_STREAM * vstream,bool paused,bool reached_eof)686 static void update_audio_fragment(ALLEGRO_AUDIO_STREAM *audio_stream,
687 VORBIS_STREAM *vstream, bool paused, bool reached_eof)
688 {
689 float *frag;
690
691 frag = al_get_audio_stream_fragment(audio_stream);
692 if (!frag)
693 return;
694
695 if (paused || vstream->next_fragment_pos < FRAG_SAMPLES) {
696 if (!paused && !reached_eof) {
697 ALLEGRO_WARN("Next fragment not ready.\n");
698 }
699 memset(frag, 0, vstream->channels * FRAG_SAMPLES * sizeof(float));
700 }
701 else {
702 memcpy(frag, vstream->next_fragment,
703 vstream->channels * FRAG_SAMPLES * sizeof(float));
704 vstream->next_fragment_pos = 0;
705 }
706
707 al_set_audio_stream_fragment(audio_stream, frag);
708 }
709
710
711 /* Theora streams. */
712
setup_theora_stream_decode(ALLEGRO_VIDEO * video,OGG_VIDEO * ogv,STREAM * tstream_outer)713 static void setup_theora_stream_decode(ALLEGRO_VIDEO *video, OGG_VIDEO *ogv,
714 STREAM *tstream_outer)
715 {
716 THEORA_STREAM * const tstream = &tstream_outer->u.theora;
717 int frame_w = tstream->info.frame_width;
718 int frame_h = tstream->info.frame_height;
719 int pic_x = tstream->info.pic_x;
720 int pic_y = tstream->info.pic_y;
721 int pic_w = tstream->info.pic_width;
722 int pic_h = tstream->info.pic_height;
723 float aspect_ratio = 1.0;
724
725 tstream->ctx = th_decode_alloc(&tstream->info, tstream->setup);
726 ASSERT(tstream->ctx);
727 th_setup_free(tstream->setup);
728 tstream->setup = NULL;
729
730 ogv->pixel_fmt = tstream->info.pixel_fmt;
731 ogv->frame_bmp = al_create_bitmap(frame_w, frame_h);
732 if (pic_x == 0 && pic_y == 0 && pic_w == frame_w && pic_h == frame_h) {
733 ogv->pic_bmp = ogv->frame_bmp;
734 }
735 else {
736 ogv->pic_bmp = al_create_sub_bitmap(ogv->frame_bmp,
737 pic_x, pic_y, pic_w, pic_h);
738 }
739 ogv->rgb_data =
740 al_malloc(al_get_pixel_size(RGB_PIXEL_FORMAT) * frame_w * frame_h);
741
742 video->fps =
743 (double)tstream->info.fps_numerator /
744 (double)tstream->info.fps_denominator;
745 tstream->frame_duration =
746 (double)tstream->info.fps_denominator /
747 (double)tstream->info.fps_numerator;
748
749 if (tstream->info.aspect_denominator != 0) {
750 aspect_ratio =
751 (double)(pic_w * tstream->info.aspect_numerator) /
752 (double)(pic_h * tstream->info.aspect_denominator);
753 }
754
755 _al_compute_scaled_dimensions(pic_w, pic_h, aspect_ratio, &video->scaled_width,
756 &video->scaled_height);
757
758 tstream->prev_framenum = -1;
759
760 ALLEGRO_INFO("Frame size: %dx%d\n", frame_w, frame_h);
761 ALLEGRO_INFO("Picture size: %dx%d\n", pic_w, pic_h);
762 ALLEGRO_INFO("Scaled size: %fx%f\n", video->scaled_width, video->scaled_height);
763 ALLEGRO_INFO("FPS: %f\n", video->fps);
764 ALLEGRO_INFO("Frame_duration: %f\n", tstream->frame_duration);
765 }
766
get_theora_framenum(THEORA_STREAM * tstream,ogg_packet * packet)767 static int64_t get_theora_framenum(THEORA_STREAM *tstream, ogg_packet *packet)
768 {
769 if (packet->granulepos > 0) {
770 return th_granule_frame(&tstream->info, packet->granulepos);
771 }
772
773 return tstream->prev_framenum + 1;
774 }
775
handle_theora_data(ALLEGRO_VIDEO * video,THEORA_STREAM * tstream,ogg_packet * packet,bool * ret_new_frame)776 static bool handle_theora_data(ALLEGRO_VIDEO *video, THEORA_STREAM *tstream,
777 ogg_packet *packet, bool *ret_new_frame)
778 {
779 int64_t expected_framenum;
780 int64_t framenum;
781 int rc;
782
783 expected_framenum = tstream->prev_framenum + 1;
784 framenum = get_theora_framenum(tstream, packet);
785
786 if (framenum > expected_framenum) {
787 /* Packet is for a later frame, don't decode it yet. */
788 ALLEGRO_DEBUG("Expected frame %ld, got %ld\n",
789 (long)expected_framenum, (long)framenum);
790 video->video_position += tstream->frame_duration;
791 tstream->prev_framenum++;
792 return false;
793 }
794
795 if (framenum < expected_framenum) {
796 ALLEGRO_DEBUG("Expected frame %ld, got %ld (decoding anyway)\n",
797 (long)expected_framenum, (long)framenum);
798 }
799
800 rc = th_decode_packetin(tstream->ctx, packet, NULL);
801 if (rc != TH_EBADPACKET) {
802 /* HACK: When we seek to beginning, the first few packets are actually
803 * headers. To properly fix this, those packets should be ignored when we
804 * do the seeking (see the XXX there). */
805 ASSERT(rc == 0 || rc == TH_DUPFRAME);
806 }
807
808 if (rc == 0) {
809 *ret_new_frame = true;
810
811 video->video_position = framenum * tstream->frame_duration;
812 tstream->prev_framenum = framenum;
813 }
814
815 return true;
816 }
817
818 /* Y'CrCb to RGB conversion. */
819 /* XXX simple slow implementation */
820
clamp(int x)821 static unsigned char clamp(int x)
822 {
823 if (x < 0)
824 return 0;
825 if (x > 255)
826 return 255;
827 return x;
828 }
829
ycbcr_to_rgb(th_ycbcr_buffer buffer,unsigned char * rgb_data,int pixel_size,int pitch,int xshift,int yshift)830 static INLINE void ycbcr_to_rgb(th_ycbcr_buffer buffer,
831 unsigned char* rgb_data, int pixel_size, int pitch, int xshift, int yshift)
832 {
833 const int w = buffer[0].width;
834 const int h = buffer[0].height;
835 int x, y;
836
837 for (y = 0; y < h; y++) {
838 const int y2 = y >> yshift;
839 for (x = 0; x < w; x++) {
840 const int x2 = x >> xshift;
841 unsigned char * const data = rgb_data + y * pitch + x * pixel_size;
842 const int yp = buffer[0].data[y * buffer[0].stride + x ];
843 const int cb = buffer[1].data[y2 * buffer[1].stride + x2];
844 const int cr = buffer[2].data[y2 * buffer[2].stride + x2];
845 const int C = yp - 16;
846 const int D = cb - 128;
847 const int E = cr - 128;
848
849 data[0] = clamp((298*C + 409*E + 128) >> 8);
850 data[1] = clamp((298*C - 100*D - 208*E + 128) >> 8);
851 data[2] = clamp((298*C + 516*D + 128) >> 8);
852 data[3] = 0xff;
853 }
854 }
855 }
856
convert_buffer_to_rgba(OGG_VIDEO * ogv)857 static void convert_buffer_to_rgba(OGG_VIDEO *ogv)
858 {
859 const int pixel_size = al_get_pixel_size(RGB_PIXEL_FORMAT);
860 const int pitch = pixel_size * al_get_bitmap_width(ogv->frame_bmp);
861
862 switch (ogv->pixel_fmt) {
863 case TH_PF_420:
864 ycbcr_to_rgb(ogv->buffer, ogv->rgb_data, pixel_size, pitch, 1, 1);
865 break;
866 case TH_PF_422:
867 ycbcr_to_rgb(ogv->buffer, ogv->rgb_data, pixel_size, pitch, 1, 0);
868 break;
869 case TH_PF_444:
870 ycbcr_to_rgb(ogv->buffer, ogv->rgb_data, pixel_size, pitch, 0, 0);
871 break;
872 default:
873 ALLEGRO_ERROR("Unsupported pixel format.\n");
874 break;
875 }
876 }
877
poll_theora_decode(ALLEGRO_VIDEO * video,STREAM * tstream_outer)878 static int poll_theora_decode(ALLEGRO_VIDEO *video, STREAM *tstream_outer)
879 {
880 OGG_VIDEO * const ogv = video->data;
881 THEORA_STREAM * const tstream = &tstream_outer->u.theora;
882 bool new_frame = false;
883 int num_frames = 0;
884 int rc;
885
886 while (video->video_position < video->position) {
887 PACKET_NODE *node;
888 ogg_packet packet;
889
890 node = take_head_packet(tstream_outer);
891 if (node) {
892 if (handle_theora_data(video, tstream, &node->pkt, &new_frame)) {
893 free_packet_node(node);
894 num_frames++;
895 }
896 else {
897 add_head_packet(tstream_outer, node);
898 }
899 }
900 else if (read_packet(ogv, tstream_outer, &packet)) {
901 if (handle_theora_data(video, tstream, &packet, &new_frame)) {
902 num_frames++;
903 }
904 else {
905 add_head_packet(tstream_outer, create_packet_node(&packet));
906 }
907 }
908 else {
909 break;
910 }
911
912 /* Only skip frames if we are really falling behind, not just slightly
913 * ahead of the target position.
914 * XXX improve frame skipping algorithm
915 */
916 if (video->video_position
917 >= video->position - 3.0*tstream->frame_duration) {
918 break;
919 }
920 }
921
922 if (new_frame) {
923 ALLEGRO_EVENT event;
924 al_lock_mutex(ogv->mutex);
925
926 rc = th_decode_ycbcr_out(tstream->ctx, ogv->buffer);
927 ASSERT(rc == 0);
928
929 convert_buffer_to_rgba(ogv);
930
931 ogv->buffer_dirty = true;
932
933 event.type = ALLEGRO_EVENT_VIDEO_FRAME_SHOW;
934 event.user.data1 = (intptr_t)video;
935 al_emit_user_event(&video->es, &event, NULL);
936
937 al_unlock_mutex(ogv->mutex);
938 }
939
940 return num_frames;
941 }
942
943
944 /* Seeking. */
945
seek_to_beginning(ALLEGRO_VIDEO * video,OGG_VIDEO * ogv,THEORA_STREAM * tstream)946 static void seek_to_beginning(ALLEGRO_VIDEO *video, OGG_VIDEO *ogv,
947 THEORA_STREAM *tstream)
948 {
949 unsigned i;
950 int rc;
951 bool seeked;
952
953 for (i = 0; i < _al_vector_size(&ogv->streams); i++) {
954 STREAM **slot = _al_vector_ref(&ogv->streams, i);
955 STREAM *stream = *slot;
956
957 ogg_stream_reset(&stream->state);
958 free_packet_queue(stream);
959 }
960
961 if (tstream) {
962 ogg_int64_t granpos = 0;
963
964 rc = th_decode_ctl(tstream->ctx, TH_DECCTL_SET_GRANPOS, &granpos,
965 sizeof(granpos));
966 ASSERT(rc == 0);
967
968 tstream->prev_framenum = -1;
969 }
970
971 rc = ogg_sync_reset(&ogv->sync_state);
972 ASSERT(rc == 0);
973
974 seeked = al_fseek(ogv->fp, 0, SEEK_SET);
975 ASSERT(seeked);
976 /* XXX read enough file data to get into position */
977
978 ogv->reached_eof = false;
979 video->audio_position = 0.0;
980 video->video_position = 0.0;
981 video->position = 0.0;
982
983 /* XXX maybe clear backlog of time and stream fragment events */
984 }
985
986 /* Decode thread. */
987
decode_thread_func(ALLEGRO_THREAD * thread,void * _video)988 static void *decode_thread_func(ALLEGRO_THREAD *thread, void *_video)
989 {
990 ALLEGRO_VIDEO * const video = _video;
991 OGG_VIDEO * const ogv = video->data;
992 STREAM *tstream_outer;
993 THEORA_STREAM *tstream = NULL;
994 STREAM *vstream_outer;
995 VORBIS_STREAM *vstream = NULL;
996 ALLEGRO_TIMER *timer;
997 double audio_pos_step = 0.0;
998 double timer_dur;
999
1000 ALLEGRO_DEBUG("Thread started.\n");
1001
1002 tstream_outer = ogv->selected_video_stream;
1003 if (tstream_outer) {
1004 ASSERT(tstream_outer->stream_type == STREAM_TYPE_THEORA);
1005 tstream = &tstream_outer->u.theora;
1006 }
1007
1008 vstream_outer = ogv->selected_audio_stream;
1009 if (vstream_outer) {
1010 ASSERT(vstream_outer->stream_type == STREAM_TYPE_VORBIS);
1011
1012 video->audio = create_audio_stream(video, vstream_outer);
1013 if (video->audio) {
1014 vstream = &vstream_outer->u.vorbis;
1015 audio_pos_step = (double)FRAG_SAMPLES / vstream->info.rate;
1016 }
1017 else {
1018 deactivate_stream(vstream_outer);
1019 vstream_outer = NULL;
1020 }
1021 }
1022
1023 if (!tstream_outer && !vstream_outer) {
1024 ALLEGRO_WARN("No audio or video stream found.\n");
1025 return NULL;
1026 }
1027
1028 timer_dur = 1.0;
1029 if (audio_pos_step != 0.0) {
1030 timer_dur = audio_pos_step / NUM_FRAGS;
1031 }
1032 if (tstream && tstream->frame_duration < timer_dur) {
1033 timer_dur = tstream->frame_duration;
1034 }
1035 timer = al_create_timer(timer_dur);
1036 al_register_event_source(ogv->queue, al_get_timer_event_source(timer));
1037
1038 if (video->audio) {
1039 al_register_event_source(ogv->queue,
1040 al_get_audio_stream_event_source(video->audio));
1041 }
1042
1043 ALLEGRO_DEBUG("Begin decode loop.\n");
1044
1045 al_start_timer(timer);
1046
1047 while (!al_get_thread_should_stop(thread)) {
1048 ALLEGRO_EVENT ev;
1049
1050 al_wait_for_event(ogv->queue, &ev);
1051
1052 if (ev.type == _ALLEGRO_EVENT_VIDEO_SEEK) {
1053 double seek_to = ev.user.data1 / 1.0e6;
1054 /* XXX we only know how to seek to start of video */
1055 ASSERT(seek_to <= 0.0);
1056 al_lock_mutex(ogv->mutex);
1057 seek_to_beginning(video, ogv, tstream);
1058 ogv->seek_counter++;
1059 al_broadcast_cond(ogv->cond);
1060 al_unlock_mutex(ogv->mutex);
1061 continue;
1062 }
1063
1064 if (ev.type == ALLEGRO_EVENT_TIMER) {
1065 if (vstream_outer && video->playing) {
1066 poll_vorbis_decode(ogv, vstream_outer);
1067 }
1068
1069 /* If no audio then video is master. */
1070 if (!video->audio && video->playing && !ogv->reached_eof) {
1071 video->position += tstream->frame_duration;
1072 }
1073
1074 if (tstream_outer) {
1075 poll_theora_decode(video, tstream_outer);
1076 }
1077
1078 if (video->playing && ogv->reached_eof) {
1079 ALLEGRO_EVENT event;
1080 video->playing = false;
1081
1082 event.type = ALLEGRO_EVENT_VIDEO_FINISHED;
1083 event.user.data1 = (intptr_t)video;
1084 al_emit_user_event(&video->es, &event, NULL);
1085 }
1086 }
1087
1088 if (ev.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) {
1089 /* Audio clock is master when it exists. */
1090 /* XXX This doesn't work well when the process is paused then resumed,
1091 * due to a problem with the audio addon. We get a flood of
1092 * fragment events which pushes the position field ahead of the
1093 * real audio position.
1094 */
1095 if (video->playing && !ogv->reached_eof) {
1096 video->audio_position += audio_pos_step;
1097 video->position = video->audio_position - NUM_FRAGS * audio_pos_step;
1098 }
1099 update_audio_fragment(video->audio, vstream, !video->playing,
1100 ogv->reached_eof);
1101 }
1102 }
1103
1104 ALLEGRO_DEBUG("End decode loop.\n");
1105
1106 if (video->audio) {
1107 al_drain_audio_stream(video->audio);
1108 al_destroy_audio_stream(video->audio);
1109 video->audio = NULL;
1110 }
1111 al_destroy_timer(timer);
1112
1113 ALLEGRO_DEBUG("Thread exit.\n");
1114
1115 return NULL;
1116 }
1117
1118
update_frame_bmp(OGG_VIDEO * ogv)1119 static bool update_frame_bmp(OGG_VIDEO *ogv)
1120 {
1121 ALLEGRO_LOCKED_REGION *lr;
1122 int y;
1123 int pitch = al_get_pixel_size(RGB_PIXEL_FORMAT) * al_get_bitmap_width(ogv->frame_bmp);
1124
1125 lr = al_lock_bitmap(ogv->frame_bmp, RGB_PIXEL_FORMAT,
1126 ALLEGRO_LOCK_WRITEONLY);
1127 if (!lr) {
1128 ALLEGRO_ERROR("Failed to lock bitmap.\n");
1129 return false;
1130 }
1131
1132 for (y = 0; y < al_get_bitmap_height(ogv->frame_bmp); y++) {
1133 memcpy((unsigned char*)lr->data + y * lr->pitch, ogv->rgb_data + y * pitch, pitch);
1134 }
1135
1136 al_unlock_bitmap(ogv->frame_bmp);
1137 return true;
1138 }
1139
1140
1141 /* Video interface. */
1142
do_open_video(ALLEGRO_VIDEO * video,OGG_VIDEO * ogv)1143 static bool do_open_video(ALLEGRO_VIDEO *video, OGG_VIDEO *ogv)
1144 {
1145 unsigned i;
1146
1147 read_headers(ogv);
1148
1149 /* Select the first Theora and Vorbis tracks. */
1150 for (i = 0; i < _al_vector_size(&ogv->streams); i++) {
1151 STREAM **slot = _al_vector_ref(&ogv->streams, i);
1152 STREAM *stream = *slot;
1153
1154 if (stream->stream_type == STREAM_TYPE_THEORA &&
1155 !ogv->selected_video_stream)
1156 {
1157 setup_theora_stream_decode(video, ogv, stream);
1158 ogv->selected_video_stream = stream;
1159 }
1160 else if (stream->stream_type == STREAM_TYPE_VORBIS &&
1161 !ogv->selected_audio_stream)
1162 {
1163 setup_vorbis_stream_decode(video, stream);
1164 ogv->selected_audio_stream = stream;
1165 }
1166 else {
1167 deactivate_stream(stream);
1168 }
1169 }
1170
1171 return ogv->selected_video_stream || ogv->selected_audio_stream;
1172 }
1173
ogv_open_video(ALLEGRO_VIDEO * video)1174 static bool ogv_open_video(ALLEGRO_VIDEO *video)
1175 {
1176 const char *filename;
1177 ALLEGRO_FILE *fp;
1178 OGG_VIDEO *ogv;
1179 int rc;
1180
1181 filename = al_path_cstr(video->filename, ALLEGRO_NATIVE_PATH_SEP);
1182 fp = al_fopen(filename, "rb");
1183 if (!fp) {
1184 ALLEGRO_WARN("Failed to open %s.\n", filename);
1185 return false;
1186 }
1187
1188 ogv = al_calloc(1, sizeof(OGG_VIDEO));
1189 if (!ogv) {
1190 ALLEGRO_ERROR("Out of memory.\n");
1191 al_fclose(fp);
1192 return false;
1193 }
1194 ogv->fp = fp;
1195 rc = ogg_sync_init(&ogv->sync_state);
1196 ASSERT(rc == 0);
1197 _al_vector_init(&ogv->streams, sizeof(STREAM *));
1198
1199 if (!do_open_video(video, ogv)) {
1200 ALLEGRO_ERROR("No audio or video stream found.\n");
1201 ogv_close_video(video);
1202 return false;
1203 }
1204
1205 /* ogv->mutex and ogv->thread are created in ogv_start_video. */
1206
1207 video->data = ogv;
1208 return true;
1209 }
1210
ogv_close_video(ALLEGRO_VIDEO * video)1211 static bool ogv_close_video(ALLEGRO_VIDEO *video)
1212 {
1213 OGG_VIDEO *ogv;
1214 unsigned i;
1215
1216 ogv = video->data;
1217 if (ogv) {
1218 if (ogv->thread) {
1219 al_join_thread(ogv->thread, NULL);
1220 al_destroy_user_event_source(&ogv->evtsrc);
1221 al_destroy_event_queue(ogv->queue);
1222 al_destroy_mutex(ogv->mutex);
1223 al_destroy_cond(ogv->cond);
1224 al_destroy_thread(ogv->thread);
1225 }
1226
1227 al_fclose(ogv->fp);
1228 ogg_sync_clear(&ogv->sync_state);
1229 for (i = 0; i < _al_vector_size(&ogv->streams); i++) {
1230 STREAM **slot = _al_vector_ref(&ogv->streams, i);
1231 free_stream(*slot);
1232 }
1233 _al_vector_free(&ogv->streams);
1234 if (ogv->pic_bmp != ogv->frame_bmp) {
1235 al_destroy_bitmap(ogv->pic_bmp);
1236 }
1237 al_destroy_bitmap(ogv->frame_bmp);
1238
1239 al_free(ogv->rgb_data);
1240
1241 al_free(ogv);
1242 }
1243
1244 video->data = NULL;
1245
1246 return true;
1247 }
1248
ogv_start_video(ALLEGRO_VIDEO * video)1249 static bool ogv_start_video(ALLEGRO_VIDEO *video)
1250 {
1251 OGG_VIDEO *ogv = video->data;
1252
1253 if (ogv->thread != NULL) {
1254 ALLEGRO_ERROR("Thread already created.\n");
1255 return false;
1256 }
1257
1258 ogv->thread = al_create_thread(decode_thread_func, video);
1259 if (!ogv->thread) {
1260 ALLEGRO_ERROR("Could not create thread.\n");
1261 return false;
1262 }
1263
1264 al_init_user_event_source(&ogv->evtsrc);
1265 ogv->queue = al_create_event_queue();
1266 ogv->mutex = al_create_mutex();
1267 ogv->cond = al_create_cond();
1268
1269 al_register_event_source(ogv->queue, &ogv->evtsrc);
1270
1271 al_start_thread(ogv->thread);
1272 return true;
1273 }
1274
ogv_set_video_playing(ALLEGRO_VIDEO * video)1275 static bool ogv_set_video_playing(ALLEGRO_VIDEO *video)
1276 {
1277 OGG_VIDEO * const ogv = video->data;
1278 if (ogv->reached_eof) {
1279 video->playing = false;
1280 }
1281 return true;
1282 }
1283
ogv_seek_video(ALLEGRO_VIDEO * video,double seek_to)1284 static bool ogv_seek_video(ALLEGRO_VIDEO *video, double seek_to)
1285 {
1286 OGG_VIDEO *ogv = video->data;
1287 ALLEGRO_EVENT ev;
1288 int seek_counter;
1289
1290 /* XXX we only know how to seek to beginning */
1291 if (seek_to > 0.0) {
1292 return false;
1293 }
1294
1295 al_lock_mutex(ogv->mutex);
1296
1297 seek_counter = ogv->seek_counter;
1298
1299 ev.user.type = _ALLEGRO_EVENT_VIDEO_SEEK;
1300 ev.user.data1 = seek_to * 1.0e6;
1301 ev.user.data2 = 0;
1302 ev.user.data3 = 0;
1303 ev.user.data4 = 0;
1304 al_emit_user_event(&ogv->evtsrc, &ev, NULL);
1305
1306 while (seek_counter == ogv->seek_counter) {
1307 al_wait_cond(ogv->cond, ogv->mutex);
1308 }
1309
1310 al_unlock_mutex(ogv->mutex);
1311
1312 return true;
1313 }
1314
ogv_update_video(ALLEGRO_VIDEO * video)1315 static bool ogv_update_video(ALLEGRO_VIDEO *video)
1316 {
1317 OGG_VIDEO *ogv = video->data;
1318 int w, h;
1319 bool ret;
1320
1321 al_lock_mutex(ogv->mutex);
1322
1323 w = ogv->buffer[0].width;
1324 h = ogv->buffer[0].height;
1325
1326 if (w > 0 && h && h > 0 && ogv->frame_bmp) {
1327 ASSERT(w == al_get_bitmap_width(ogv->frame_bmp));
1328 ASSERT(h == al_get_bitmap_height(ogv->frame_bmp));
1329
1330 if (ogv->buffer_dirty) {
1331 ret = update_frame_bmp(ogv);
1332 ogv->buffer_dirty = false;
1333 }
1334 else {
1335 ret = true;
1336 }
1337
1338 video->current_frame = ogv->pic_bmp;
1339 }
1340 else {
1341 /* No frame ready yet. */
1342 ret = false;
1343 }
1344
1345 al_unlock_mutex(ogv->mutex);
1346
1347 return ret;
1348 }
1349
1350 static ALLEGRO_VIDEO_INTERFACE ogv_vtable = {
1351 ogv_open_video,
1352 ogv_close_video,
1353 ogv_start_video,
1354 ogv_set_video_playing,
1355 ogv_seek_video,
1356 ogv_update_video
1357 };
1358
_al_video_ogv_vtable(void)1359 ALLEGRO_VIDEO_INTERFACE *_al_video_ogv_vtable(void)
1360 {
1361 return &ogv_vtable;
1362 }
1363
1364 /* vim: set sts=3 sw=3 et: */
1365