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