/
theoraplay.c
819 lines (698 loc) · 24 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
13
14
15
16
17
// 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.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
18
19
20
21
#ifdef _WIN32
#include <windows.h>
#define THEORAPLAY_THREAD_T HANDLE
#define THEORAPLAY_MUTEX_T HANDLE
22
#define sleepms(x) Sleep(x)
23
24
#else
#include <pthread.h>
25
26
#include <unistd.h>
#define sleepms(x) usleep((x) * 1000)
27
28
29
30
#define THEORAPLAY_THREAD_T pthread_t
#define THEORAPLAY_MUTEX_T pthread_mutex_t
#endif
31
#include "theoraplay.h"
32
33
34
#include "theora/theoradec.h"
#include "vorbis/codec.h"
35
36
#define THEORAPLAY_INTERNAL 1
37
38
typedef THEORAPLAY_VideoFrame VideoFrame;
typedef THEORAPLAY_AudioPacket AudioPacket;
39
40
41
// !!! FIXME: these all count on the pixel format being TH_PF_420 for now.
42
43
44
typedef unsigned char *(*ConvertVideoFrameFn)(const th_info *tinfo,
const th_ycbcr_buffer ycbcr);
45
46
47
static unsigned char *ConvertVideoFrame420ToYUVPlanar(
const th_info *tinfo, const th_ycbcr_buffer ycbcr,
const int p0, const int p1, const int p2)
48
49
50
51
52
53
54
55
56
57
58
{
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);
if (yuv)
{
unsigned char *dst = yuv;
for (i = 0; i < h; i++, dst += w)
59
memcpy(dst, ycbcr[p0].data + yoff + ycbcr[p0].stride * i, w);
60
for (i = 0; i < (h / 2); i++, dst += w/2)
61
memcpy(dst, ycbcr[p1].data + uvoff + ycbcr[p1].stride * i, w / 2);
62
for (i = 0; i < (h / 2); i++, dst += w/2)
63
memcpy(dst, ycbcr[p2].data + uvoff + ycbcr[p2].stride * i, w / 2);
64
65
66
} // if
return yuv;
67
68
69
70
71
72
73
} // ConvertVideoFrame420ToYUVPlanar
static unsigned char *ConvertVideoFrame420ToYV12(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
return ConvertVideoFrame420ToYUVPlanar(tinfo, ycbcr, 0, 2, 1);
74
} // ConvertVideoFrame420ToYV12
75
76
77
78
79
80
81
82
83
static unsigned char *ConvertVideoFrame420ToIYUV(const th_info *tinfo,
const th_ycbcr_buffer ycbcr)
{
return ConvertVideoFrame420ToYUVPlanar(tinfo, ycbcr, 0, 1, 2);
} // ConvertVideoFrame420ToIYUV
84
85
86
87
88
89
// 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
90
91
92
93
94
95
96
// 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
97
98
99
100
101
typedef struct TheoraDecoder
{
// Thread wrangling...
102
int thread_created;
103
THEORAPLAY_MUTEX_T lock;
104
volatile int halt;
105
int thread_done;
106
THEORAPLAY_THREAD_T worker;
107
108
// API state...
109
THEORAPLAY_Io *io;
110
unsigned int maxframes; // Max video frames to buffer.
111
112
113
114
115
116
117
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;
118
119
THEORAPLAY_VideoFormat vidfmt;
ConvertVideoFrameFn vidcvt;
120
121
122
VideoFrame *videolist;
VideoFrame *videolisttail;
123
124
125
AudioPacket *audiolist;
AudioPacket *audiolisttail;
126
127
128
} TheoraDecoder;
129
130
131
132
133
134
135
136
137
138
139
140
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
#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
192
static int FeedMoreOggData(THEORAPLAY_Io *io, ogg_sync_state *sync)
193
{
194
long buflen = 4096;
195
char *buffer = ogg_sync_buffer(sync, buflen);
196
if (buffer == NULL)
197
return -1;
198
199
buflen = io->read(io, buffer, buflen);
200
201
202
if (buflen <= 0)
return 0;
203
return (ogg_sync_wrote(sync, buflen) == 0) ? 1 : -1;
204
205
} // FeedMoreOggData
206
207
208
// This massive function is where all the effort happens.
static void WorkerThread(TheoraDecoder *ctx)
209
210
211
{
// 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.
212
213
214
215
#define queue_ogg_page(ctx) do { \
if (tpackets) ogg_stream_pagein(&tstream, &page); \
if (vpackets) ogg_stream_pagein(&vstream, &page); \
} while (0)
216
217
218
219
unsigned long audioframes = 0;
unsigned long videoframes = 0;
double fps = 0.0;
220
221
int was_error = 1; // resets to 0 at the end.
int eos = 0; // end of stream flag.
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Too much Ogg/Vorbis/Theora state...
ogg_packet packet;
ogg_sync_state sync;
ogg_page page;
int vpackets = 0;
vorbis_info vinfo;
vorbis_comment vcomment;
ogg_stream_state vstream;
int vdsp_init = 0;
vorbis_dsp_state vdsp;
int tpackets = 0;
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;
ogg_sync_init(&sync);
vorbis_info_init(&vinfo);
vorbis_comment_init(&vcomment);
th_comment_init(&tcomment);
th_info_init(&tinfo);
247
248
249
250
int bos = 1;
while (!ctx->halt && bos)
{
251
if (FeedMoreOggData(ctx->io, &sync) <= 0)
252
goto cleanup;
253
254
// parse out the initial header.
255
while ( (!ctx->halt) && (ogg_sync_pageout(&sync, &page) > 0) )
256
257
258
{
ogg_stream_state test;
259
if (!ogg_page_bos(&page)) // not a header.
260
{
261
queue_ogg_page(ctx);
262
263
264
265
bos = 0;
break;
} // if
266
267
ogg_stream_init(&test, ogg_page_serialno(&page));
ogg_stream_pagein(&test, &page);
268
269
ogg_stream_packetout(&test, &packet);
270
if (!tpackets && (th_decode_headerin(&tinfo, &tcomment, &tsetup, &packet) >= 0))
271
{
272
273
memcpy(&tstream, &test, sizeof (test));
tpackets = 1;
274
} // if
275
else if (!vpackets && (vorbis_synthesis_headerin(&vinfo, &vcomment, &packet) >= 0))
276
{
277
278
memcpy(&vstream, &test, sizeof (test));
vpackets = 1;
279
280
281
282
283
284
285
286
287
288
} // else if
else
{
// whatever it is, we don't care about it
ogg_stream_clear(&test);
} // else
} // while
} // while
// no audio OR video?
289
290
if (ctx->halt || (!vpackets && !tpackets))
goto cleanup;
291
292
// apparently there are two more theora and two more vorbis headers next.
293
while ((!ctx->halt) && ((tpackets && (tpackets < 3)) || (vpackets && (vpackets < 3))))
294
{
295
while (!ctx->halt && tpackets && (tpackets < 3))
296
{
297
if (ogg_stream_packetout(&tstream, &packet) != 1)
298
break; // get more data?
299
300
301
if (!th_decode_headerin(&tinfo, &tcomment, &tsetup, &packet))
goto cleanup;
tpackets++;
302
303
} // while
304
while (!ctx->halt && vpackets && (vpackets < 3))
305
{
306
if (ogg_stream_packetout(&vstream, &packet) != 1)
307
break; // get more data?
308
309
310
if (vorbis_synthesis_headerin(&vinfo, &vcomment, &packet))
goto cleanup;
vpackets++;
311
312
313
} // while
// get another page, try again?
314
315
if (ogg_sync_pageout(&sync, &page) > 0)
queue_ogg_page(ctx);
316
else if (FeedMoreOggData(ctx->io, &sync) <= 0)
317
goto cleanup;
318
319
320
} // while
// okay, now we have our streams, ready to set up decoding.
321
if (!ctx->halt && tpackets)
322
323
{
// th_decode_alloc() docs say to check for insanely large frames yourself.
324
325
if ((tinfo.frame_width > 99999) || (tinfo.frame_height > 99999))
goto cleanup;
326
327
328
// We treat "unspecified" as NTSC. *shrug*
if ( (tinfo.colorspace != TH_CS_UNSPECIFIED) &&
329
330
(tinfo.colorspace != TH_CS_ITU_REC_470M) &&
(tinfo.colorspace != TH_CS_ITU_REC_470BG) )
331
332
333
334
335
{
assert(0 && "Unsupported colorspace."); // !!! FIXME
goto cleanup;
} // if
336
if (tinfo.pixel_fmt != TH_PF_420) { assert(0); goto cleanup; } // !!! FIXME
337
338
339
if (tinfo.fps_denominator != 0)
fps = ((double) tinfo.fps_numerator) / ((double) tinfo.fps_denominator);
340
341
342
tdec = th_decode_alloc(&tinfo, tsetup);
if (!tdec) goto cleanup;
343
344
345
346
// 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;
347
348
// !!! FIXME: maybe an API to set this?
//th_decode_ctl(tdec, TH_DECCTL_GET_PPLEVEL_MAX, &pp_level_max, sizeof(pp_level_max));
349
th_decode_ctl(tdec, TH_DECCTL_SET_PPLEVEL, &pp_level_max, sizeof(pp_level_max));
350
351
352
} // if
// Done with this now.
353
if (tsetup != NULL)
354
{
355
356
th_setup_free(tsetup);
tsetup = NULL;
357
358
} // if
359
if (!ctx->halt && vpackets)
360
{
361
362
363
364
365
366
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;
367
368
369
370
371
} // if
// Now we can start the actual decoding!
// Note that audio and video don't _HAVE_ to start simultaneously.
372
Mutex_Lock(ctx->lock);
373
374
375
ctx->prepped = 1;
ctx->hasvideo = (tpackets != 0);
ctx->hasaudio = (vpackets != 0);
376
Mutex_Unlock(ctx->lock);
377
378
while (!ctx->halt && !eos)
379
380
381
382
383
384
{
int need_pages = 0; // need more Ogg pages?
int saw_video_frame = 0;
// 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.
385
while (!ctx->halt && vpackets)
386
387
{
float **pcm = NULL;
388
const int frames = vorbis_synthesis_pcmout(&vdsp, &pcm);
389
390
if (frames > 0)
{
391
const int channels = vinfo.channels;
392
393
int chanidx, frameidx;
float *samples;
394
AudioPacket *item = (AudioPacket *) malloc(sizeof (AudioPacket));
395
396
if (item == NULL) goto cleanup;
item->playms = (unsigned long) ((((double) audioframes) / ((double) vinfo.rate)) * 1000.0);
397
item->channels = channels;
398
item->freq = vinfo.rate;
399
400
401
402
403
404
405
item->frames = frames;
item->samples = (float *) malloc(sizeof (float) * frames * channels);
item->next = NULL;
if (item->samples == NULL)
{
free(item);
406
goto cleanup;
407
408
409
410
} // if
// I bet this beats the crap out of the CPU cache...
samples = item->samples;
411
for (frameidx = 0; frameidx < frames; frameidx++)
412
413
414
415
416
{
for (chanidx = 0; chanidx < channels; chanidx++)
*(samples++) = pcm[chanidx][frameidx];
} // for
417
vorbis_synthesis_read(&vdsp, frames); // we ate everything.
418
419
audioframes += frames;
420
//printf("Decoded %d frames of audio.\n", (int) frames);
421
Mutex_Lock(ctx->lock);
422
ctx->audioms += item->playms;
423
424
425
426
427
428
429
430
431
432
433
if (ctx->audiolisttail)
{
assert(ctx->audiolist);
ctx->audiolisttail->next = item;
} // if
else
{
assert(!ctx->audiolist);
ctx->audiolist = item;
} // else
ctx->audiolisttail = item;
434
Mutex_Unlock(ctx->lock);
435
436
437
438
439
} // if
else // no audio available left in current packet?
{
// try to feed another packet to the Vorbis stream...
440
if (ogg_stream_packetout(&vstream, &packet) <= 0)
441
442
443
{
if (!tpackets)
need_pages = 1; // no video, get more pages now.
444
break; // we'll get more pages when the video catches up.
445
} // if
446
447
else
{
448
449
if (vorbis_synthesis(&vblock, &packet) == 0)
vorbis_synthesis_blockin(&vdsp, &vblock);
450
451
452
453
} // else
} // else
} // while
454
if (!ctx->halt && tpackets)
455
456
457
{
// Theora, according to example_player.c, is
// "one [packet] in, one [frame] out."
458
if (ogg_stream_packetout(&tstream, &packet) <= 0)
459
460
461
462
need_pages = 1;
else
{
ogg_int64_t granulepos = 0;
463
const int rc = th_decode_packetin(tdec, &packet, &granulepos);
464
465
466
467
468
if (rc == TH_DUPFRAME)
videoframes++; // nothing else to do.
else if (rc == 0) // new frame!
{
th_ycbcr_buffer ycbcr;
469
if (th_decode_ycbcr_out(tdec, ycbcr) == 0)
470
{
471
VideoFrame *item = (VideoFrame *) malloc(sizeof (VideoFrame));
472
if (item == NULL) goto cleanup;
473
item->playms = (fps == 0) ? 0 : (unsigned int) ((((double) videoframes) / fps) * 1000.0);
474
item->fps = fps;
475
476
477
478
item->width = tinfo.pic_width;
item->height = tinfo.pic_height;
item->format = ctx->vidfmt;
item->pixels = ctx->vidcvt(&tinfo, ycbcr);
479
480
item->next = NULL;
481
if (item->pixels == NULL)
482
483
{
free(item);
484
goto cleanup;
485
486
} // if
487
//printf("Decoded another video frame.\n");
488
Mutex_Lock(ctx->lock);
489
490
491
492
493
494
495
496
497
498
499
500
if (ctx->videolisttail)
{
assert(ctx->videolist);
ctx->videolisttail->next = item;
} // if
else
{
assert(!ctx->videolist);
ctx->videolist = item;
} // else
ctx->videolisttail = item;
ctx->videocount++;
501
Mutex_Unlock(ctx->lock);
502
503
504
505
506
507
508
509
510
511
saw_video_frame = 1;
} // if
videoframes++;
} // if
} // else
} // if
if (!ctx->halt && need_pages)
{
512
const int rc = FeedMoreOggData(ctx->io, &sync);
513
514
515
516
517
518
519
520
521
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
522
523
524
525
526
527
} // if
// Sleep the process until we have space for more frames.
if (saw_video_frame)
{
int go_on = !ctx->halt;
528
//printf("Sleeping.\n");
529
530
531
while (go_on)
{
// !!! FIXME: This is stupid. I should use a semaphore for this.
532
Mutex_Lock(ctx->lock);
533
go_on = !ctx->halt && (ctx->videocount >= ctx->maxframes);
534
Mutex_Unlock(ctx->lock);
535
if (go_on)
536
sleepms(10);
537
} // while
538
//printf("Awake!\n");
539
540
} // if
} // while
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
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);
557
ctx->io->close(ctx->io);
558
ctx->thread_done = 1;
559
560
561
562
563
564
565
} // WorkerThread
static void *WorkerThreadEntry(void *_this)
{
TheoraDecoder *ctx = (TheoraDecoder *) _this;
WorkerThread(ctx);
566
//printf("Worker thread is done.\n");
567
568
569
return NULL;
} // WorkerThreadEntry
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
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
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;
io->close = IoFopenClose;
io->userdata = f;
return THEORAPLAY_startDecode(io, maxframes, vidfmt);
} // THEORAPLAY_startDecodeFile
THEORAPLAY_Decoder *THEORAPLAY_startDecode(THEORAPLAY_Io *io,
612
613
const unsigned int maxframes,
THEORAPLAY_VideoFormat vidfmt)
614
{
615
616
617
618
619
TheoraDecoder *ctx = NULL;
ConvertVideoFrameFn vidcvt = NULL;
switch (vidfmt)
{
620
621
// !!! FIXME: current expects TH_PF_420.
#define VIDCVT(t) case THEORAPLAY_VIDFMT_##t: vidcvt = ConvertVideoFrame420To##t; break;
622
VIDCVT(YV12)
623
VIDCVT(IYUV)
624
625
626
VIDCVT(RGB)
VIDCVT(RGBA)
#undef VIDCVT
627
default: goto startdecode_failed; // invalid/unsupported format.
628
629
} // switch
630
ctx = (TheoraDecoder *) malloc(sizeof (TheoraDecoder));
631
if (ctx == NULL)
632
goto startdecode_failed;
633
634
635
memset(ctx, '\0', sizeof (TheoraDecoder));
ctx->maxframes = maxframes;
636
637
ctx->vidfmt = vidfmt;
ctx->vidcvt = vidcvt;
638
ctx->io = io;
639
640
if (Mutex_Create(ctx) == 0)
641
{
642
ctx->thread_created = (Thread_Create(ctx, WorkerThreadEntry) == 0);
643
644
if (ctx->thread_created)
return (THEORAPLAY_Decoder *) ctx;
645
646
} // if
647
Mutex_Destroy(ctx->lock);
648
649
650
startdecode_failed:
io->close(io);
651
652
653
654
655
656
657
658
free(ctx);
return NULL;
} // THEORAPLAY_startDecode
void THEORAPLAY_stopDecode(THEORAPLAY_Decoder *decoder)
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
659
660
if (!ctx)
return;
661
662
663
664
if (ctx->thread_created)
{
ctx->halt = 1;
665
666
Thread_Join(ctx->worker);
Mutex_Destroy(ctx->lock);
667
668
} // if
669
VideoFrame *videolist = ctx->videolist;
670
671
while (videolist)
{
672
VideoFrame *next = videolist->next;
673
free(videolist->pixels);
674
675
676
677
free(videolist);
videolist = next;
} // while
678
AudioPacket *audiolist = ctx->audiolist;
679
680
while (audiolist)
{
681
AudioPacket *next = audiolist->next;
682
683
684
685
686
687
688
689
690
691
692
free(audiolist->samples);
free(audiolist);
audiolist = next;
} // while
free(ctx);
} // THEORAPLAY_stopDecode
int THEORAPLAY_isDecoding(THEORAPLAY_Decoder *decoder)
{
693
694
695
696
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
int retval = 0;
if (ctx)
{
697
Mutex_Lock(ctx->lock);
698
699
retval = ( ctx && (ctx->audiolist || ctx->videolist ||
(ctx->thread_created && !ctx->thread_done)) );
700
Mutex_Unlock(ctx->lock);
701
702
} // if
return retval;
703
704
705
} // THEORAPLAY_isDecoding
706
707
708
709
#define GET_SYNCED_VALUE(typ, defval, decoder, member) \
TheoraDecoder *ctx = (TheoraDecoder *) decoder; \
typ retval = defval; \
if (ctx) { \
710
Mutex_Lock(ctx->lock); \
711
retval = ctx->member; \
712
Mutex_Unlock(ctx->lock); \
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
} \
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);
} // THEORAPLAY_hasAudioStream
unsigned int THEORAPLAY_availableAudio(THEORAPLAY_Decoder *decoder)
{
GET_SYNCED_VALUE(unsigned int, 0, decoder, audioms);
} // THEORAPLAY_hasAudioStream
746
747
int THEORAPLAY_decodingError(THEORAPLAY_Decoder *decoder)
{
748
GET_SYNCED_VALUE(int, 0, decoder, decode_error);
749
750
751
} // THEORAPLAY_decodingError
752
const THEORAPLAY_AudioPacket *THEORAPLAY_getAudio(THEORAPLAY_Decoder *decoder)
753
754
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
755
AudioPacket *retval;
756
757
Mutex_Lock(ctx->lock);
758
759
760
retval = ctx->audiolist;
if (retval)
{
761
ctx->audioms -= retval->playms;
762
763
764
765
766
ctx->audiolist = retval->next;
retval->next = NULL;
if (ctx->audiolist == NULL)
ctx->audiolisttail = NULL;
} // if
767
Mutex_Unlock(ctx->lock);
768
769
770
771
772
return retval;
} // THEORAPLAY_getAudio
773
void THEORAPLAY_freeAudio(const THEORAPLAY_AudioPacket *_item)
774
{
775
THEORAPLAY_AudioPacket *item = (THEORAPLAY_AudioPacket *) _item;
776
777
778
779
780
781
if (item != NULL)
{
assert(item->next == NULL);
free(item->samples);
free(item);
} // if
782
783
784
} // THEORAPLAY_freeAudio
785
const THEORAPLAY_VideoFrame *THEORAPLAY_getVideo(THEORAPLAY_Decoder *decoder)
786
787
{
TheoraDecoder *ctx = (TheoraDecoder *) decoder;
788
VideoFrame *retval;
789
790
Mutex_Lock(ctx->lock);
791
792
793
794
795
796
797
798
799
800
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
801
Mutex_Unlock(ctx->lock);
802
803
804
805
806
return retval;
} // THEORAPLAY_getVideo
807
void THEORAPLAY_freeVideo(const THEORAPLAY_VideoFrame *_item)
808
{
809
THEORAPLAY_VideoFrame *item = (THEORAPLAY_VideoFrame *) _item;
810
811
812
if (item != NULL)
{
assert(item->next == NULL);
813
free(item->pixels);
814
815
free(item);
} // if
816
817
} // THEORAPLAY_freeVideo
818
// end of theoraplay.cpp ...