/
theoraplay.c
999 lines (852 loc) · 31.6 KB
1
2
3
4
5
6
7
8
/**
* TheoraPlay; multithreaded Ogg Theora/Ogg Vorbis decoding.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
9
10
11
12
// I wrote this with a lot of peeking at the Theora example code in
// libtheora-1.1.1/examples/player_example.c, but this is all my own
// code.
13
14
// !!! FIXME: can we move this off malloc to a custom allocator?
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
20
21
22
23
#ifdef _WIN32
#include <windows.h>
#define THEORAPLAY_THREAD_T HANDLE
#define THEORAPLAY_MUTEX_T HANDLE
24
#define sleepms(x) Sleep(x)
25
26
#else
#include <pthread.h>
27
28
#include <unistd.h>
#define sleepms(x) usleep((x) * 1000)
29
30
31
32
#define THEORAPLAY_THREAD_T pthread_t
#define THEORAPLAY_MUTEX_T pthread_mutex_t
#endif
33
#include "theoraplay.h"
34
35
36
#include "theora/theoradec.h"
#include "vorbis/codec.h"
37
38
#define THEORAPLAY_INTERNAL 1
39
40
typedef THEORAPLAY_VideoFrame VideoFrame;
typedef THEORAPLAY_AudioPacket AudioPacket;
41
42
43
// !!! FIXME: these all count on the pixel format being TH_PF_420 for now.
44
45
46
typedef unsigned char *(*ConvertVideoFrameFn)(const th_info *tinfo,
const th_ycbcr_buffer ycbcr);
47
48
49
static unsigned char *ConvertVideoFrame420ToYUVPlanar(
const th_info *tinfo, const th_ycbcr_buffer ycbcr,
const int p0, const int p1, const int p2)
50
51
52
53
54
55
56
{
int i;
const int w = tinfo->pic_width;
const int h = tinfo->pic_height;
const int yoff = (tinfo->pic_x & ~1) + ycbcr[0].stride * (tinfo->pic_y & ~1);
const int uvoff = (tinfo->pic_x / 2) + (ycbcr[1].stride) * (tinfo->pic_y / 2);
unsigned char *yuv = (unsigned char *) malloc(w * h * 2);
57
58
59
60
61
62
63
const unsigned char *p0data = ycbcr[p0].data + yoff;
const int p0stride = ycbcr[p0].stride;
const unsigned char *p1data = ycbcr[p1].data + uvoff;
const int p1stride = ycbcr[p1].stride;
const unsigned char *p2data = ycbcr[p2].data + uvoff;
const int p2stride = ycbcr[p2].stride;
64
65
66
67
if (yuv)
{
unsigned char *dst = yuv;
for (i = 0; i < h; i++, dst += w)
68
memcpy(dst, p0data + (p0stride * i), w);
69
for (i = 0; i < (h / 2); i++, dst += w/2)
70
memcpy(dst, p1data + (p1stride * i), w / 2);
71
for (i = 0; i < (h / 2); i++, dst += w/2)
72
memcpy(dst, p2data + (p2stride * i), w / 2);
73
74
75
} // if
return yuv;
76
77
78
79
80
81
82
} // ConvertVideoFrame420ToYUVPlanar
static unsigned char *ConvertVideoFrame420ToYV12(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
return ConvertVideoFrame420ToYUVPlanar(tinfo, ycbcr, 0, 2, 1);
83
} // ConvertVideoFrame420ToYV12
84
85
86
87
88
89
90
91
92
static unsigned char *ConvertVideoFrame420ToIYUV(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
return ConvertVideoFrame420ToYUVPlanar(tinfo, ycbcr, 0, 1, 2);
} // ConvertVideoFrame420ToIYUV
93
94
95
96
97
98
// RGB
#define THEORAPLAY_CVT_FNNAME_420 ConvertVideoFrame420ToRGB
#define THEORAPLAY_CVT_RGB_ALPHA 0
#include "theoraplay_cvtrgb.h"
#undef THEORAPLAY_CVT_RGB_ALPHA
#undef THEORAPLAY_CVT_FNNAME_420
99
100
101
102
103
104
105
// RGBA
#define THEORAPLAY_CVT_FNNAME_420 ConvertVideoFrame420ToRGBA
#define THEORAPLAY_CVT_RGB_ALPHA 1
#include "theoraplay_cvtrgb.h"
#undef THEORAPLAY_CVT_RGB_ALPHA
#undef THEORAPLAY_CVT_FNNAME_420
106
107
108
// !!! FIXME: these volatiles really need to become atomics.
109
110
111
typedef struct TheoraDecoder
{
// Thread wrangling...
112
int thread_created;
113
THEORAPLAY_MUTEX_T lock;
114
volatile int halt;
115
int thread_done;
116
THEORAPLAY_THREAD_T worker;
117
118
// API state...
119
THEORAPLAY_Io *io;
120
unsigned int maxframes; // Max video frames to buffer.
121
122
123
124
125
126
volatile unsigned int prepped;
volatile unsigned int videocount; // currently buffered frames.
volatile unsigned int audioms; // currently buffered audio samples.
volatile int hasvideo;
volatile int hasaudio;
volatile int decode_error;
127
128
volatile unsigned int seek_generation;
volatile unsigned long new_seek_position_ms;
129
130
131
THEORAPLAY_VideoFormat vidfmt;
ConvertVideoFrameFn vidcvt;
132
133
134
VideoFrame *videolist;
VideoFrame *videolisttail;
135
136
137
AudioPacket *audiolist;
AudioPacket *audiolisttail;
138
139
140
} TheoraDecoder;
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#ifdef _WIN32
static inline int Thread_Create(TheoraDecoder *ctx, void *(*routine) (void*))
{
ctx->worker = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) routine,
(LPVOID) ctx,
0,
NULL
);
return (ctx->worker == NULL);
}
static inline void Thread_Join(THEORAPLAY_THREAD_T thread)
{
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
}
static inline int Mutex_Create(TheoraDecoder *ctx)
{
ctx->lock = CreateMutex(NULL, FALSE, NULL);
return (ctx->lock == NULL);
}
static inline void Mutex_Destroy(THEORAPLAY_MUTEX_T mutex)
{
CloseHandle(mutex);
}
static inline void Mutex_Lock(THEORAPLAY_MUTEX_T mutex)
{
WaitForSingleObject(mutex, INFINITE);
}
static inline void Mutex_Unlock(THEORAPLAY_MUTEX_T mutex)
{
ReleaseMutex(mutex);
}
#else
static inline int Thread_Create(TheoraDecoder *ctx, void *(*routine) (void*))
{
return pthread_create(&ctx->worker, NULL, routine, ctx);
}
static inline void Thread_Join(THEORAPLAY_THREAD_T thread)
{
pthread_join(thread, NULL);
}
static inline int Mutex_Create(TheoraDecoder *ctx)
{
return pthread_mutex_init(&ctx->lock, NULL);
}
static inline void Mutex_Destroy(THEORAPLAY_MUTEX_T mutex)
{
pthread_mutex_destroy(&mutex);
}
static inline void Mutex_Lock(THEORAPLAY_MUTEX_T mutex)
{
pthread_mutex_lock(&mutex);
}
static inline void Mutex_Unlock(THEORAPLAY_MUTEX_T mutex)
{
pthread_mutex_unlock(&mutex);
}
#endif
204
static int FeedMoreOggData(THEORAPLAY_Io *io, ogg_sync_state *sync)
205
{
206
long buflen = 4096;
207
char *buffer = ogg_sync_buffer(sync, buflen);
208
if (buffer == NULL)
209
return -1;
210
211
buflen = io->read(io, buffer, buflen);
212
213
214
if (buflen <= 0)
return 0;
215
return (ogg_sync_wrote(sync, buflen) == 0) ? 1 : -1;
216
217
} // FeedMoreOggData
218
219
220
// This massive function is where all the effort happens.
static void WorkerThread(TheoraDecoder *ctx)
221
222
223
{
// make sure we initialized the stream before using pagein, but the stream
// will know to ignore pages that aren't meant for it, so pass to both.
224
225
226
227
#define queue_ogg_page(ctx) do { \
if (tpackets) ogg_stream_pagein(&tstream, &page); \
if (vpackets) ogg_stream_pagein(&vstream, &page); \
} while (0)
228
229
230
long streamlen = -1;
unsigned int current_seek_generation = 0;
231
double fps = 0.0;
232
233
int was_error = 1; // resets to 0 at the end.
int eos = 0; // end of stream flag.
234
235
236
237
238
239
// Too much Ogg/Vorbis/Theora state...
ogg_packet packet;
ogg_sync_state sync;
ogg_page page;
int vpackets = 0;
240
int vserialno = 0;
241
242
243
244
245
246
vorbis_info vinfo;
vorbis_comment vcomment;
ogg_stream_state vstream;
int vdsp_init = 0;
vorbis_dsp_state vdsp;
int tpackets = 0;
247
int tserialno = 0;
248
249
250
251
252
253
254
th_info tinfo;
th_comment tcomment;
ogg_stream_state tstream;
int vblock_init = 0;
vorbis_block vblock;
th_dec_ctx *tdec = NULL;
th_setup_info *tsetup = NULL;
255
256
257
258
259
ogg_int64_t granulepos = 0;
int resolving_audio_seek = 0;
int resolving_video_seek = 0;
int need_keyframe = 0;
unsigned long seek_target = 0;
260
261
262
263
264
265
ogg_sync_init(&sync);
vorbis_info_init(&vinfo);
vorbis_comment_init(&vcomment);
th_comment_init(&tcomment);
th_info_init(&tinfo);
266
267
268
269
int bos = 1;
while (!ctx->halt && bos)
{
270
if (FeedMoreOggData(ctx->io, &sync) <= 0)
271
goto cleanup;
272
273
// parse out the initial header.
274
while ( (!ctx->halt) && (ogg_sync_pageout(&sync, &page) > 0) )
275
276
{
ogg_stream_state test;
277
int serialno;
278
279
if (!ogg_page_bos(&page)) // not a header.
280
{
281
queue_ogg_page(ctx);
282
283
284
285
bos = 0;
break;
} // if
286
287
serialno = ogg_page_serialno(&page);
ogg_stream_init(&test, serialno);
288
ogg_stream_pagein(&test, &page);
289
290
ogg_stream_packetout(&test, &packet);
291
if (!tpackets && (th_decode_headerin(&tinfo, &tcomment, &tsetup, &packet) >= 0))
292
{
293
294
memcpy(&tstream, &test, sizeof (test));
tpackets = 1;
295
tserialno = serialno;
296
} // if
297
else if (!vpackets && (vorbis_synthesis_headerin(&vinfo, &vcomment, &packet) >= 0))
298
{
299
300
memcpy(&vstream, &test, sizeof (test));
vpackets = 1;
301
vserialno = serialno;
302
303
304
305
306
307
308
309
310
311
} // else if
else
{
// whatever it is, we don't care about it
ogg_stream_clear(&test);
} // else
} // while
} // while
// no audio OR video?
312
313
if (ctx->halt || (!vpackets && !tpackets))
goto cleanup;
314
315
// apparently there are two more theora and two more vorbis headers next.
316
while ((!ctx->halt) && ((tpackets && (tpackets < 3)) || (vpackets && (vpackets < 3))))
317
{
318
while (!ctx->halt && tpackets && (tpackets < 3))
319
{
320
if (ogg_stream_packetout(&tstream, &packet) != 1)
321
break; // get more data?
322
323
324
if (!th_decode_headerin(&tinfo, &tcomment, &tsetup, &packet))
goto cleanup;
tpackets++;
325
326
} // while
327
while (!ctx->halt && vpackets && (vpackets < 3))
328
{
329
if (ogg_stream_packetout(&vstream, &packet) != 1)
330
break; // get more data?
331
332
333
if (vorbis_synthesis_headerin(&vinfo, &vcomment, &packet))
goto cleanup;
vpackets++;
334
335
336
} // while
// get another page, try again?
337
338
if (ogg_sync_pageout(&sync, &page) > 0)
queue_ogg_page(ctx);
339
else if (FeedMoreOggData(ctx->io, &sync) <= 0)
340
goto cleanup;
341
342
343
} // while
// okay, now we have our streams, ready to set up decoding.
344
if (!ctx->halt && tpackets)
345
346
{
// th_decode_alloc() docs say to check for insanely large frames yourself.
347
348
if ((tinfo.frame_width > 99999) || (tinfo.frame_height > 99999))
goto cleanup;
349
350
351
// We treat "unspecified" as NTSC. *shrug*
if ( (tinfo.colorspace != TH_CS_UNSPECIFIED) &&
352
353
(tinfo.colorspace != TH_CS_ITU_REC_470M) &&
(tinfo.colorspace != TH_CS_ITU_REC_470BG) )
354
355
356
357
358
{
assert(0 && "Unsupported colorspace."); // !!! FIXME
goto cleanup;
} // if
359
if (tinfo.pixel_fmt != TH_PF_420) { assert(0); goto cleanup; } // !!! FIXME
360
361
362
if (tinfo.fps_denominator != 0)
fps = ((double) tinfo.fps_numerator) / ((double) tinfo.fps_denominator);
363
364
365
tdec = th_decode_alloc(&tinfo, tsetup);
if (!tdec) goto cleanup;
366
367
368
369
// Set decoder to maximum post-processing level.
// Theoretically we could try dropping this level if we're not keeping up.
int pp_level_max = 0;
370
371
// !!! FIXME: maybe an API to set this?
//th_decode_ctl(tdec, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max, sizeof(pp_level_max));
372
th_decode_ctl(tdec, TH_DECCTL_SET_PPLEVEL, &pp_level_max, sizeof(pp_level_max));
373
374
375
} // if
// Done with this now.
376
if (tsetup != NULL)
377
{
378
379
th_setup_free(tsetup);
tsetup = NULL;
380
381
} // if
382
if (!ctx->halt && vpackets)
383
{
384
385
386
387
388
389
vdsp_init = (vorbis_synthesis_init(&vdsp, &vinfo) == 0);
if (!vdsp_init)
goto cleanup;
vblock_init = (vorbis_block_init(&vdsp, &vblock) == 0);
if (!vblock_init)
goto cleanup;
390
391
392
393
394
} // if
// Now we can start the actual decoding!
// Note that audio and video don't _HAVE_ to start simultaneously.
395
Mutex_Lock(ctx->lock);
396
397
398
ctx->prepped = 1;
ctx->hasvideo = (tpackets != 0);
ctx->hasaudio = (vpackets != 0);
399
Mutex_Unlock(ctx->lock);
400
401
while (!ctx->halt && !eos)
402
403
404
405
{
int need_pages = 0; // need more Ogg pages?
int saw_video_frame = 0;
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
if (current_seek_generation != ctx->seek_generation) // seek requested
{
unsigned long targetms;
long seekpos;
long lo, hi;
if (!ctx->io->seek)
goto cleanup; // seeking unsupported.
if (streamlen == -1) // just check this once in case it's expensive.
{
streamlen = ctx->io->streamlen ? ctx->io->streamlen(ctx->io) : -1;
if (streamlen == -1)
goto cleanup; // i/o error, unsupported, etc.
} // if
// We check ctx->seek_generation without a lock as this goes on, so if they mismatch we
// drop what we're doing and prepare to seek to a new location. But here we hold a lock
// so we can avoid the race condition where the app is halfway through requesting a
// seek while we're reading in these variables.
Mutex_Lock(ctx->lock);
current_seek_generation = ctx->seek_generation;
targetms = ctx->new_seek_position_ms;
Mutex_Unlock(ctx->lock);
lo = 0;
hi = streamlen;
if (targetms == 0)
hi = 0; /* as an optimization, just jump to the start of file if rewinding to start instead of binary searching. */
seekpos = (lo / 2) + (hi / 2);
while ((!ctx->halt) && (current_seek_generation == ctx->seek_generation))
{
//const int max_keyframe_distance = 1 << tinfo.keyframe_granule_shift;
// Do a binary search through the stream to find our starting point.
// This idea came from libtheoraplayer (no relation to theoraplay).
if (ctx->io->seek(ctx->io, seekpos) == -1)
goto cleanup; // oh well.
granulepos = -1;
ogg_sync_reset(&sync);
memset(&page, '\0', sizeof (page));
ogg_sync_pageseek(&sync, &page);
while (!ctx->halt && (current_seek_generation == ctx->seek_generation))
{
if (ogg_sync_pageout(&sync, &page) != 1)
{
if (FeedMoreOggData(ctx->io, &sync) <= 0)
goto cleanup;
continue;
} // if
granulepos = ogg_page_granulepos(&page);
if (granulepos >= 0)
{
const int serialno = ogg_page_serialno(&page);
unsigned long ms;
if (tpackets) // always tee off video frames if possible.
{
if (serialno != tserialno)
continue;
ms = (unsigned long) (th_granule_time(tdec, granulepos) * 1000.0);
} // else
else
{
if (serialno != vserialno)
continue;
ms = (unsigned long) (vorbis_granule_time(&vdsp, granulepos) * 1000.0);
} // else
if ((ms < targetms) && ((targetms - ms) <= 250)) // !!! FIXME: tweak this number?
hi = lo; // found something close enough to the target!
else // adjust binary search position and try again.
{
const long newpos = (lo / 2) + (hi / 2);
if (targetms > ms)
lo = newpos;
else
hi = newpos;
} // else
break;
} // if
} // while
const long newseekpos = (lo / 2) + (hi / 2);
if (seekpos == newseekpos)
break; // we did the best we could, just go from here.
seekpos = newseekpos;
} // while
// at this point, we have seek'd to something reasonably close to our target. Now decode until we're as close as possible to it.
vorbis_synthesis_restart(&vdsp);
resolving_audio_seek = vpackets;
resolving_video_seek = tpackets;
seek_target = targetms;
need_keyframe = tpackets;
} // if
509
510
// Try to read as much audio as we can at once. We limit the outer
// loop to one video frame and as much audio as we can eat.
511
while (!ctx->halt && vpackets)
512
{
513
514
const double audiotime = vorbis_granule_time(&vdsp, vdsp.granulepos);
const unsigned int playms = (unsigned int) (audiotime * 1000.0);
515
float **pcm = NULL;
516
517
518
519
520
521
522
523
524
int frames;
if (current_seek_generation != ctx->seek_generation)
break; // seek requested? Break out of the loop right away so we can handle it; this loop's work would be wasted.
if (resolving_audio_seek && ((playms >= seek_target) || ((seek_target - playms) <= (unsigned long) (1000.0 / fps))))
resolving_audio_seek = 0;
frames = vorbis_synthesis_pcmout(&vdsp, &pcm);
525
526
if (frames > 0)
{
527
if (!resolving_audio_seek)
528
{
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
const int channels = vinfo.channels;
int chanidx, frameidx;
float *samples;
AudioPacket *item = (AudioPacket *) malloc(sizeof (AudioPacket));
if (item == NULL) goto cleanup;
item->seek_generation = current_seek_generation;
item->playms = playms;
item->channels = channels;
item->freq = vinfo.rate;
item->frames = frames;
item->samples = (float *) malloc(sizeof (float) * frames * channels);
item->next = NULL;
if (item->samples == NULL)
{
free(item);
goto cleanup;
} // if
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
// I bet this beats the crap out of the CPU cache...
samples = item->samples;
for (frameidx = 0; frameidx < frames; frameidx++)
{
for (chanidx = 0; chanidx < channels; chanidx++)
*(samples++) = pcm[chanidx][frameidx];
} // for
//printf("Decoded %d frames of audio.\n", (int) frames);
Mutex_Lock(ctx->lock);
ctx->audioms += item->playms;
if (ctx->audiolisttail)
{
assert(ctx->audiolist);
ctx->audiolisttail->next = item;
} // if
else
{
assert(!ctx->audiolist);
ctx->audiolist = item;
} // else
ctx->audiolisttail = item;
Mutex_Unlock(ctx->lock);
} // if
572
573
vorbis_synthesis_read(&vdsp, frames); // we ate everything.
574
575
576
577
} // if
else // no audio available left in current packet?
{
// try to feed another packet to the Vorbis stream...
578
if (ogg_stream_packetout(&vstream, &packet) <= 0)
579
580
581
{
if (!tpackets)
need_pages = 1; // no video, get more pages now.
582
break; // we'll get more pages when the video catches up.
583
} // if
584
585
else
{
586
587
if (vorbis_synthesis(&vblock, &packet) == 0)
vorbis_synthesis_blockin(&vdsp, &vblock);
588
589
590
591
} // else
} // else
} // while
592
if (!ctx->halt && tpackets && (current_seek_generation == ctx->seek_generation))
593
594
595
{
// Theora, according to example_player.c, is
// "one [packet] in, one [frame] out."
596
if (ogg_stream_packetout(&tstream, &packet) <= 0)
597
598
599
need_pages = 1;
else
{
600
601
// you have to guide the Theora decoder to get meaningful timestamps, apparently. :/
if (packet.granulepos >= 0)
602
th_decode_ctl(tdec, TH_DECCTL_SET_GRANPOS, &packet.granulepos, sizeof (packet.granulepos));
603
604
if (th_decode_packetin(tdec, &packet, &granulepos) == 0) // new frame!
605
{
606
607
608
609
610
const double videotime = th_granule_time(tdec, granulepos);
const unsigned int playms = (unsigned int) (videotime * 1000.0);
if (need_keyframe && th_packet_iskeyframe(&packet))
need_keyframe = 0;
611
612
613
614
615
616
617
618
if (resolving_video_seek && !need_keyframe && ((playms >= seek_target) || ((seek_target - playms) <= (unsigned long) (1000.0 / fps))))
resolving_video_seek = 0;
if (!resolving_video_seek)
{
th_ycbcr_buffer ycbcr;
if (th_decode_ycbcr_out(tdec, ycbcr) == 0)
619
{
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
VideoFrame *item = (VideoFrame *) malloc(sizeof (VideoFrame));
if (item == NULL) goto cleanup;
item->seek_generation = current_seek_generation;
item->playms = playms;
item->fps = fps;
item->width = tinfo.pic_width;
item->height = tinfo.pic_height;
item->format = ctx->vidfmt;
item->pixels = ctx->vidcvt(&tinfo, ycbcr);
item->next = NULL;
if (item->pixels == NULL)
{
free(item);
goto cleanup;
} // if
//printf("Decoded another video frame.\n");
Mutex_Lock(ctx->lock);
if (ctx->videolisttail)
{
assert(ctx->videolist);
ctx->videolisttail->next = item;
} // if
else
{
assert(!ctx->videolist);
ctx->videolist = item;
} // else
ctx->videolisttail = item;
ctx->videocount++;
Mutex_Unlock(ctx->lock);
saw_video_frame = 1;
654
655
656
657
658
659
} // if
} // if
} // if
} // else
} // if
660
if (!ctx->halt && need_pages && (current_seek_generation == ctx->seek_generation))
661
{
662
const int rc = FeedMoreOggData(ctx->io, &sync);
663
664
665
666
667
668
669
670
671
if (rc == 0)
eos = 1; // end of stream
else if (rc < 0)
goto cleanup; // i/o error, etc.
else
{
while (!ctx->halt && (ogg_sync_pageout(&sync, &page) > 0))
queue_ogg_page(ctx);
} // else
672
673
674
675
676
677
} // if
// Sleep the process until we have space for more frames.
if (saw_video_frame)
{
int go_on = !ctx->halt;
678
//printf("Sleeping.\n");
679
680
681
while (go_on)
{
// !!! FIXME: This is stupid. I should use a semaphore for this.
682
Mutex_Lock(ctx->lock);
683
go_on = !ctx->halt && (ctx->videocount >= ctx->maxframes);
684
Mutex_Unlock(ctx->lock);
685
if (go_on)
686
sleepms(10);
687
} // while
688
//printf("Awake!\n");
689
690
} // if
} // while
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
was_error = 0;
cleanup:
ctx->decode_error = (!ctx->halt && was_error);
if (tdec != NULL) th_decode_free(tdec);
if (tsetup != NULL) th_setup_free(tsetup);
if (vblock_init) vorbis_block_clear(&vblock);
if (vdsp_init) vorbis_dsp_clear(&vdsp);
if (tpackets) ogg_stream_clear(&tstream);
if (vpackets) ogg_stream_clear(&vstream);
th_info_clear(&tinfo);
th_comment_clear(&tcomment);
vorbis_comment_clear(&vcomment);
vorbis_info_clear(&vinfo);
ogg_sync_clear(&sync);
707
ctx->io->close(ctx->io);
708
ctx->thread_done = 1;
709
710
711
712
713
714
715
} // WorkerThread
static void *WorkerThreadEntry(void *_this)
{
TheoraDecoder *ctx = (TheoraDecoder *) _this;
WorkerThread(ctx);
716
//printf("Worker thread is done.\n");
717
718
719
return NULL;
} // WorkerThreadEntry
720
721
722
723
724
725
726
727
728
static long IoFopenRead(THEORAPLAY_Io *io, void *buf, long buflen)
{
FILE *f = (FILE *) io->userdata;
const size_t br = fread(buf, 1, buflen, f);
if ((br == 0) && ferror(f))
return -1;
return (long) br;
} // IoFopenRead
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
static long IoFopenStreamLen(THEORAPLAY_Io *io)
{
FILE *f = (FILE *) io->userdata;
const long origpos = ftell(f);
long retval = -1;
if (fseek(f, 0, SEEK_END) == 0) {
retval = ftell(f);
}
fseek(f, origpos, SEEK_SET);
return retval;
} // IoFopenStreamLen
static int IoFopenSeek(THEORAPLAY_Io *io, long absolute_offset)
{
FILE *f = (FILE *) io->userdata;
return fseek(f, absolute_offset, SEEK_SET);
} // IoFopenSeek
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
static void IoFopenClose(THEORAPLAY_Io *io)
{
FILE *f = (FILE *) io->userdata;
fclose(f);
free(io);
} // IoFopenClose
THEORAPLAY_Decoder *THEORAPLAY_startDecodeFile(const char *fname,
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt)
{
THEORAPLAY_Io *io = (THEORAPLAY_Io *) malloc(sizeof (THEORAPLAY_Io));
if (io == NULL)
return NULL;
FILE *f = fopen(fname, "rb");
if (f == NULL)
{
free(io);
return NULL;
} // if
io->read = IoFopenRead;
771
772
io->seek = IoFopenSeek;
io->streamlen = IoFopenStreamLen;
773
774
775
776
777
778
779
io->close = IoFopenClose;
io->userdata = f;
return THEORAPLAY_startDecode(io, maxframes, vidfmt);
} // THEORAPLAY_startDecodeFile
THEORAPLAY_Decoder *THEORAPLAY_startDecode(THEORAPLAY_Io *io,
780
781
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt)
782
{
783
784
785
786
787
TheoraDecoder *ctx = NULL;
ConvertVideoFrameFn vidcvt = NULL;
switch (vidfmt)
{
788
789
// !!! FIXME: current expects TH_PF_420.
#define VIDCVT(t) case THEORAPLAY_VIDFMT_##t: vidcvt = ConvertVideoFrame420To##t; break;
790
VIDCVT(YV12)
791
VIDCVT(IYUV)
792
793
794
VIDCVT(RGB)
VIDCVT(RGBA)
#undef VIDCVT
795
default: goto startdecode_failed; // invalid/unsupported format.
796
797
} // switch
798
ctx = (TheoraDecoder *) malloc(sizeof (TheoraDecoder));
799
if (ctx == NULL)
800
goto startdecode_failed;
801
802
803
memset(ctx, '\0', sizeof (TheoraDecoder));
ctx->maxframes = maxframes;
804
805
ctx->vidfmt = vidfmt;
ctx->vidcvt = vidcvt;
806
ctx->io = io;
807
808
if (Mutex_Create(ctx) == 0)
809
{
810
ctx->thread_created = (Thread_Create(ctx, WorkerThreadEntry) == 0);
811
812
if (ctx->thread_created)
return (THEORAPLAY_Decoder *) ctx;
813
814
} // if
815
Mutex_Destroy(ctx->lock);
816
817
818
startdecode_failed:
io->close(io);
819
820
821
822
823
824
825
826
free(ctx);
return NULL;
} // THEORAPLAY_startDecode
void THEORAPLAY_stopDecode(THEORAPLAY_Decoder *decoder)
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
827
828
if (!ctx)
return;
829
830
831
832
if (ctx->thread_created)
{
ctx->halt = 1;
833
834
Thread_Join(ctx->worker);
Mutex_Destroy(ctx->lock);
835
836
} // if
837
VideoFrame *videolist = ctx->videolist;
838
839
while (videolist)
{
840
VideoFrame *next = videolist->next;
841
free(videolist->pixels);
842
843
844
845
free(videolist);
videolist = next;
} // while
846
AudioPacket *audiolist = ctx->audiolist;
847
848
while (audiolist)
{
849
AudioPacket *next = audiolist->next;
850
851
852
853
854
855
856
857
858
859
860
free(audiolist->samples);
free(audiolist);
audiolist = next;
} // while
free(ctx);
} // THEORAPLAY_stopDecode
int THEORAPLAY_isDecoding(THEORAPLAY_Decoder *decoder)
{
861
862
863
864
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
int retval = 0;
if (ctx)
{
865
Mutex_Lock(ctx->lock);
866
867
retval = ( ctx && (ctx->audiolist || ctx->videolist ||
(ctx->thread_created && !ctx->thread_done)) );
868
Mutex_Unlock(ctx->lock);
869
870
} // if
return retval;
871
872
873
} // THEORAPLAY_isDecoding
874
875
876
877
#define GET_SYNCED_VALUE(typ, defval, decoder, member) \
TheoraDecoder *ctx = (TheoraDecoder *) decoder; \
typ retval = defval; \
if (ctx) { \
878
Mutex_Lock(ctx->lock); \
879
retval = ctx->member; \
880
Mutex_Unlock(ctx->lock); \
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
} \
return retval;
int THEORAPLAY_isInitialized(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, prepped);
} // THEORAPLAY_isInitialized
int THEORAPLAY_hasVideoStream(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, hasvideo);
} // THEORAPLAY_hasVideoStream
int THEORAPLAY_hasAudioStream(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(int, 0, decoder, hasaudio);
} // THEORAPLAY_hasAudioStream
unsigned int THEORAPLAY_availableVideo(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(unsigned int, 0, decoder, videocount);
905
} // THEORAPLAY_availableVideo
906
907
908
909
910
unsigned int THEORAPLAY_availableAudio(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(unsigned int, 0, decoder, audioms);
911
} // THEORAPLAY_availableAudio
912
913
914
915
int THEORAPLAY_decodingError(THEORAPLAY_Decoder *decoder)
{
916
GET_SYNCED_VALUE(int, 0, decoder, decode_error);
917
918
919
} // THEORAPLAY_decodingError
920
const THEORAPLAY_AudioPacket *THEORAPLAY_getAudio(THEORAPLAY_Decoder *decoder)
921
922
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
923
AudioPacket *retval;
924
925
Mutex_Lock(ctx->lock);
926
927
928
retval = ctx->audiolist;
if (retval)
{
929
ctx->audioms -= retval->playms;
930
931
932
933
934
ctx->audiolist = retval->next;
retval->next = NULL;
if (ctx->audiolist == NULL)
ctx->audiolisttail = NULL;
} // if
935
Mutex_Unlock(ctx->lock);
936
937
938
939
940
return retval;
} // THEORAPLAY_getAudio
941
void THEORAPLAY_freeAudio(const THEORAPLAY_AudioPacket *_item)
942
{
943
THEORAPLAY_AudioPacket *item = (THEORAPLAY_AudioPacket *) _item;
944
945
946
947
948
949
if (item != NULL)
{
assert(item->next == NULL);
free(item->samples);
free(item);
} // if
950
951
952
} // THEORAPLAY_freeAudio
953
const THEORAPLAY_VideoFrame *THEORAPLAY_getVideo(THEORAPLAY_Decoder *decoder)
954
955
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
956
VideoFrame *retval;
957
958
Mutex_Lock(ctx->lock);
959
960
961
962
963
964
965
966
967
968
retval = ctx->videolist;
if (retval)
{
ctx->videolist = retval->next;
retval->next = NULL;
if (ctx->videolist == NULL)
ctx->videolisttail = NULL;
assert(ctx->videocount > 0);
ctx->videocount--;
} // if
969
Mutex_Unlock(ctx->lock);
970
971
972
973
974
return retval;
} // THEORAPLAY_getVideo
975
void THEORAPLAY_freeVideo(const THEORAPLAY_VideoFrame *_item)
976
{
977
THEORAPLAY_VideoFrame *item = (THEORAPLAY_VideoFrame *) _item;
978
979
980
if (item != NULL)
{
assert(item->next == NULL);
981
free(item->pixels);
982
983
free(item);
} // if
984
985
} // THEORAPLAY_freeVideo
986
987
988
989
990
991
992
993
994
995
996
997
unsigned int THEORAPLAY_seek(THEORAPLAY_Decoder *decoder, unsigned long mspos)
{
unsigned int retval;
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
Mutex_Lock(ctx->lock);
ctx->new_seek_position_ms = mspos;
retval = ++ctx->seek_generation;
Mutex_Unlock(ctx->lock);
return retval;
} // THEORAPLAY_seek
998
// end of theoraplay.c ...