1 /*
2 * framebuffer.c -- audio/video frame ringbuffers, reloaded.
3 * (C) 2005-2010 - Francesco Romani <fromani -at- gmail -dot- com>
4 * Based on code
5 * (C) 2001-2006 - Thomas Oestreich.
6 *
7 * This file is part of transcode, a video stream processing tool.
8 *
9 * transcode is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * transcode is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <pthread.h>
24
25 #include "transcode.h"
26 #include "tc_defaults.h"
27 #include "framebuffer.h"
28 #include "decoder.h"
29 #include "encoder-common.h"
30
31 #include "libtc/tcframes.h"
32 #include "libtc/ratiocodes.h"
33
34 /*
35 * Summary:
36 * This code acts as generic ringbuffer implementation, with
37 * specializations for main (audio and video) ringbufffers
38 * in order to cope legacy constraints from 1.0.x series.
39 * It replaces former src/{audio,video}_buffer.c in (hopefully!)
40 * a more generic, clean, maintanable, compact way.
41 *
42 * Please note that there is *still* some other ringbuffer
43 * scatthered through codebase (subtitle buffer,d emux buffers,
44 * possibly more). They will be merged lately or will be dropped
45 * or reworked.
46 *
47 * This code can, of course, be further improved (and GREATLY improved,
48 * especially for multithreading safeness), but doing so
49 * hasn't high priority on my TODO list, I've covered with this
50 * piece of code most urgent todos for 1.1.0. -- FR
51 */
52
53 static pthread_mutex_t aframe_list_lock = PTHREAD_MUTEX_INITIALIZER;
54 static aframe_list_t *aframe_list_head = NULL;
55 static aframe_list_t *aframe_list_tail = NULL;
56 static pthread_cond_t audio_import_cond = PTHREAD_COND_INITIALIZER;
57 static pthread_cond_t audio_filter_cond = PTHREAD_COND_INITIALIZER;
58 static pthread_cond_t audio_export_cond = PTHREAD_COND_INITIALIZER;
59
60 static pthread_mutex_t vframe_list_lock = PTHREAD_MUTEX_INITIALIZER;
61 static vframe_list_t *vframe_list_head = NULL;
62 static vframe_list_t *vframe_list_tail = NULL;
63 static pthread_cond_t video_import_cond = PTHREAD_COND_INITIALIZER;
64 static pthread_cond_t video_filter_cond = PTHREAD_COND_INITIALIZER;
65 static pthread_cond_t video_export_cond = PTHREAD_COND_INITIALIZER;
66
tc_framebuffer_interrupt_import(void)67 void tc_framebuffer_interrupt_import(void)
68 {
69 pthread_mutex_lock(&aframe_list_lock);
70 pthread_cond_signal(&audio_import_cond);
71 pthread_mutex_unlock(&aframe_list_lock);
72
73 pthread_mutex_lock(&vframe_list_lock);
74 pthread_cond_signal(&video_import_cond);
75 pthread_mutex_unlock(&vframe_list_lock);
76 }
77
tc_framebuffer_interrupt(void)78 void tc_framebuffer_interrupt(void)
79 {
80 pthread_mutex_lock(&aframe_list_lock);
81 pthread_cond_signal(&audio_import_cond);
82 pthread_cond_broadcast(&audio_filter_cond);
83 /* filter layer deserves special care */
84 pthread_cond_signal(&audio_export_cond);
85 pthread_mutex_unlock(&aframe_list_lock);
86
87 pthread_mutex_lock(&vframe_list_lock);
88 pthread_cond_signal(&video_import_cond);
89 pthread_cond_broadcast(&video_filter_cond);
90 /* filter layer deserves special care */
91 pthread_cond_signal(&video_export_cond);
92 pthread_mutex_unlock(&vframe_list_lock);
93 }
94
95 /* ------------------------------------------------------------------ */
96
97 /*
98 * Layered, custom allocator/disposer for ringbuffer structures.
99 * The idea is to simplify (from ringbuffer viewpoint!) frame
100 * allocation/disposal and to make it as much generic as is possible
101 * (avoif if()s and so on).
102 */
103
104 typedef TCFramePtr (*TCFrameAllocFn)(const TCFrameSpecs *);
105
106 typedef void (*TCFrameFreeFn)(TCFramePtr);
107
108 /* ------------------------------------------------------------------ */
109
110 typedef struct tcringframebuffer_ TCRingFrameBuffer;
111 struct tcringframebuffer_ {
112 /* real ringbuffer */
113 TCFramePtr *frames;
114
115 /* indexes of ringbuffer */
116 int next;
117 int last;
118
119 /* counters. How many frames in various TCFrameStatus-es? */
120 int null;
121 int empty;
122 int wait;
123 int locked;
124 int ready;
125
126 /* what we need here? */
127 const TCFrameSpecs *specs;
128 /* (de)allocation helpers */
129 TCFrameAllocFn alloc;
130 TCFrameFreeFn free;
131 };
132
133 static TCRingFrameBuffer tc_audio_ringbuffer;
134 static TCRingFrameBuffer tc_video_ringbuffer;
135
136 /*
137 * Specs used internally. I don't export this structure directly
138 * because I want to be free to change it if needed
139 */
140 static TCFrameSpecs tc_specs = {
141 /* Largest supported values, to ensure the buffer is always big enough
142 * (see FIXME in tc_framebuffer_set_specs()) */
143 .frc = 3, // PAL, why not
144 .width = TC_MAX_V_FRAME_WIDTH,
145 .height = TC_MAX_V_FRAME_HEIGHT,
146 .format = TC_CODEC_RGB,
147 .rate = RATE,
148 .channels = CHANNELS,
149 .bits = BITS,
150 .samples = 48000.0,
151 };
152
153 /*
154 * Frame allocation/disposal helpers, needed by code below
155 * thin wrappers around libtc facilities
156 * I don't care about layering and performance loss, *here*, because
157 * frame are supposed to be allocated/disposed ahead of time, and
158 * always outside inner (performance-sensitive) loops.
159 */
160
161 /* ------------------------------------------------------------------ */
162
163 #define TCFRAMEPTR_IS_NULL(tcf) (tcf.generic == NULL)
164
tc_video_alloc(const TCFrameSpecs * specs)165 static TCFramePtr tc_video_alloc(const TCFrameSpecs *specs)
166 {
167 TCFramePtr frame;
168 frame.video = tc_new_video_frame(specs->width, specs->height,
169 specs->format, TC_FALSE);
170 return frame;
171 }
172
tc_audio_alloc(const TCFrameSpecs * specs)173 static TCFramePtr tc_audio_alloc(const TCFrameSpecs *specs)
174 {
175 TCFramePtr frame;
176 frame.audio = tc_new_audio_frame(specs->samples, specs->channels,
177 specs->bits);
178 return frame;
179 }
180
181
tc_video_free(TCFramePtr frame)182 static void tc_video_free(TCFramePtr frame)
183 {
184 tc_del_video_frame(frame.video);
185 }
186
tc_audio_free(TCFramePtr frame)187 static void tc_audio_free(TCFramePtr frame)
188 {
189 tc_del_audio_frame(frame.audio);
190 }
191
192 /* ------------------------------------------------------------------ */
193
194 /* exported commodities :) */
195
vframe_alloc_single(void)196 vframe_list_t *vframe_alloc_single(void)
197 {
198 return tc_new_video_frame(tc_specs.width, tc_specs.height,
199 tc_specs.format, TC_TRUE);
200 }
201
aframe_alloc_single(void)202 aframe_list_t *aframe_alloc_single(void)
203 {
204 return tc_new_audio_frame(tc_specs.samples, tc_specs.channels,
205 tc_specs.bits);
206 }
207
208 /* ------------------------------------------------------------------ */
209
tc_ring_framebuffer_dump_status(const TCRingFrameBuffer * rfb,const char * id)210 static void tc_ring_framebuffer_dump_status(const TCRingFrameBuffer *rfb,
211 const char *id)
212 {
213 tc_log_msg(__FILE__, "%s: null=%i empty=%i wait=%i"
214 " locked=%i ready=%i",
215 id, rfb->null, rfb->empty, rfb->wait,
216 rfb->locked, rfb->ready);
217 }
218
219
tc_framebuffer_get_specs(void)220 const TCFrameSpecs *tc_framebuffer_get_specs(void)
221 {
222 return &tc_specs;
223 }
224
225 /*
226 * using an <OOP-ism>accessor</OOP-ism> is also justified here
227 * by the fact that we compute (ahead of time) samples value for
228 * later usage.
229 */
tc_framebuffer_set_specs(const TCFrameSpecs * specs)230 void tc_framebuffer_set_specs(const TCFrameSpecs *specs)
231 {
232 /* silently ignore NULL specs */
233 if (specs != NULL) {
234 double fps;
235
236 /* raw copy first */
237 ac_memcpy(&tc_specs, specs, sizeof(TCFrameSpecs));
238
239 /* restore width/height/bpp
240 * (FIXME: temp until we have a way to know the max size that will
241 * be used through the decode/process/encode chain; without
242 * this, -V yuv420p -y raw -F rgb (e.g.) crashes with a
243 * buffer overrun)
244 */
245 tc_specs.width = TC_MAX_V_FRAME_WIDTH;
246 tc_specs.height = TC_MAX_V_FRAME_HEIGHT;
247 tc_specs.format = TC_CODEC_RGB;
248
249 /* then deduct missing parameters */
250 if (tc_frc_code_to_value(tc_specs.frc, &fps) == TC_NULL_MATCH) {
251 fps = 1.0; /* sane, very worst case value */
252 }
253 /* tc_specs.samples = (double)tc_specs.rate/fps; */
254 tc_specs.samples = (double)tc_specs.rate;
255 /*
256 * FIXME
257 * ok, so we use a MUCH larger buffer (big enough to store 1 *second*
258 * of raw audio, not 1 *frame*) than needed for reasons similar as
259 * seen for above video.
260 * Most notably, this helps in keeping buffers large enough to be
261 * suitable for encoder flush (see encode_lame.c first).
262 */
263 }
264 }
265
266 /* ------------------------------------------------------------------ */
267 /* NEW API, yet private */
268 /* ------------------------------------------------------------------ */
269
270 /*
271 * Threading notice:
272 *
273 * Generic code doesn't use any locking at all (yet).
274 * That's was a design choice. For clarity, locking is
275 * provided by back-compatibility wrapper functions,
276 * or by any other higher-lever caller.
277 *
278 * Client code (= outside this code) MUST NEVER used not-thread
279 * safe code.
280 */
281
282
283 /*
284 * tc_init_ring_framebuffer: (NOT thread safe)
285 * initialize a framebuffer ringbuffer by allocating needed
286 * amount of frames using given parameters.
287 *
288 * Parameters:
289 * rfb: ring framebuffer structure to initialize.
290 * specs: frame specifications to use for allocation.
291 * alloc: frame allocation function to use.
292 * free: frame disposal function to use.
293 * size: size of ringbuffer (number of frame to allocate)
294 * Return Value:
295 * > 0: wrong (NULL) parameters
296 * 0: succesfull
297 * < 0: allocation failed for one or more framesbuffers/internal error
298 */
tc_init_ring_framebuffer(TCRingFrameBuffer * rfb,const TCFrameSpecs * specs,TCFrameAllocFn alloc,TCFrameFreeFn free,int size)299 static int tc_init_ring_framebuffer(TCRingFrameBuffer *rfb,
300 const TCFrameSpecs *specs,
301 TCFrameAllocFn alloc,
302 TCFrameFreeFn free,
303 int size)
304 {
305 if (rfb == NULL || specs == NULL || size < 0
306 || alloc == NULL || free == NULL) {
307 return 1;
308 }
309 size = (size > 0) ?size :1; /* allocate at least one frame */
310
311 rfb->frames = tc_malloc(size * sizeof(TCFramePtr));
312 if (rfb->frames == NULL) {
313 return -1;
314 }
315
316 rfb->specs = specs;
317 rfb->alloc = alloc;
318 rfb->free = free;
319
320 for (rfb->last = 0; rfb->last < size; rfb->last++) {
321 rfb->frames[rfb->last] = rfb->alloc(rfb->specs);
322 if (TCFRAMEPTR_IS_NULL(rfb->frames[rfb->last])) {
323 if (verbose >= TC_DEBUG) {
324 tc_log_error(__FILE__, "failed frame allocation");
325 }
326 return -1;
327 }
328
329 rfb->frames[rfb->last].generic->status = TC_FRAME_NULL;
330 rfb->frames[rfb->last].generic->bufid = rfb->last;
331 }
332
333 rfb->next = 0;
334
335 rfb->null = size;
336 rfb->empty = 0;
337 rfb->wait = 0;
338 rfb->locked = 0;
339 rfb->ready = 0;
340
341 if (verbose >= TC_STATS) {
342 tc_log_info(__FILE__, "allocated %i frames in ringbuffer", size);
343 }
344 return 0;
345 }
346
347 /*
348 * tc_fini_ring_framebuffer: (NOT thread safe)
349 * finalize a framebuffer ringbuffer by freeing all acquired
350 * resources (framebuffer memory).
351 *
352 * Parameters:
353 * rfb: ring framebuffer structure to finalize.
354 * Return Value:
355 * None.
356 */
tc_fini_ring_framebuffer(TCRingFrameBuffer * rfb)357 static void tc_fini_ring_framebuffer(TCRingFrameBuffer *rfb)
358 {
359 if (rfb != NULL && rfb->free != NULL) {
360 int i = 0, n = rfb->last;
361
362 for (i = 0; i < rfb->last; i++) {
363 rfb->free(rfb->frames[i]);
364 }
365 tc_free(rfb->frames);
366 rfb->last = 0;
367
368 if (verbose >= TC_STATS) {
369 tc_log_info(__FILE__, "freed %i frames in ringbuffer", n);
370 }
371 }
372 }
373
374 /*
375 * tc_ring_framebuffer_retrieve_frame: (NOT thread safe)
376 * retrieve next unclaimed (TC_FRAME_NULL) framebuffer from
377 * ringbuffer and return a pointer to it for later usage
378 * by client code.
379 *
380 * Parameters:
381 * rfb: ring framebuffer to use
382 * Return Value:
383 * Always a framebuffer generic pointer. That can be pointing to
384 * NULL if there aren't no more unclaimed (TC_FRAME_NULL) framebuffer
385 * avalaible; otherwise it contains
386 * a pointer to retrieved framebuffer.
387 * DO NOT *free() such pointer directly! use
388 * tc_ring_framebuffer_release_frame() instead!
389 */
tc_ring_framebuffer_retrieve_frame(TCRingFrameBuffer * rfb)390 static TCFramePtr tc_ring_framebuffer_retrieve_frame(TCRingFrameBuffer *rfb)
391 {
392 TCFramePtr ptr;
393 ptr.generic = NULL;
394
395 if (rfb != NULL) {
396 int i = 0;
397
398 ptr = rfb->frames[rfb->next];
399 for (i = 0; i < rfb->last; i++) {
400 if (ptr.generic->status == TC_FRAME_NULL) {
401 break;
402 }
403 rfb->next++;
404 rfb->next %= rfb->last;
405 ptr = rfb->frames[rfb->next];
406 }
407
408 if (ptr.generic->status != TC_FRAME_NULL) {
409 if (verbose >= TC_FLIST) {
410 tc_log_warn(__FILE__, "retrieved buffer=%i, but not empty",
411 ptr.generic->status);
412 }
413 ptr.generic = NULL; /* enforce NULL-ness */
414 } else {
415 if (verbose >= TC_FLIST) {
416 tc_log_msg(__FILE__, "retrieved buffer = %i [%i]",
417 rfb->next, ptr.generic->bufid);
418 }
419 /* adjust internal pointer */
420 rfb->null--;
421 rfb->next++;
422 rfb->next %= rfb->last;
423 }
424 }
425 return ptr;
426 }
427
428 /*
429 * tc_ring_framebuffer_release_frame: (NOT thread safe)
430 * release a previously retrieved frame back to ringbuffer,
431 * removing claim from it and making again avalaible (TC_FRAME_NULL).
432 *
433 * Parameters:
434 * rfb: ring framebuffer to use.
435 * frame: generic pointer to frame to release.
436 * Return Value:
437 * > 0: wrong (NULL) parameters.
438 * 0: succesfull
439 * < 0: internal error (frame to be released isn't empty).
440 */
tc_ring_framebuffer_release_frame(TCRingFrameBuffer * rfb,TCFramePtr frame)441 static int tc_ring_framebuffer_release_frame(TCRingFrameBuffer *rfb,
442 TCFramePtr frame)
443 {
444 if (rfb == NULL || TCFRAMEPTR_IS_NULL(frame)) {
445 return 1;
446 }
447 if (verbose >= TC_FLIST) {
448 tc_log_msg(__FILE__, "releasing frame #%i [%i]",
449 frame.generic->bufid, rfb->next);
450 }
451 frame.generic->status = TC_FRAME_NULL;
452 rfb->null++;
453 return 0;
454 }
455
456 /*
457 * tc_ring_framebuffer_register_frame: (NOT thread safe)
458 * retrieve and register a framebuffer from a ringbuffer by
459 * attaching an ID to it, setup properly status and updating
460 * internal ringbuffer counters.
461 *
462 * That's the function that client code is supposed to use
463 * (maybe wrapped by some thin macros to save status setting troubles).
464 * In general, dont' use retrieve_frame directly, use register_frame
465 * instead.
466 *
467 * Parameters:
468 * rfb: ring framebuffer to use
469 * id: id to attach to registered framebuffer
470 * status: status of framebuffer to register. This was needed to
471 * make registering process multi-purpose.
472 * Return Value:
473 * Always a generic framebuffer pointer. That can be pointing to NULL
474 * if there isn't no more framebuffer avalaible on given ringbuffer;
475 * otherwise, it will point to a valid framebuffer.
476 */
tc_ring_framebuffer_register_frame(TCRingFrameBuffer * rfb,int id,int status)477 static TCFramePtr tc_ring_framebuffer_register_frame(TCRingFrameBuffer *rfb,
478 int id, int status)
479 {
480 TCFramePtr ptr;
481
482 /* retrive a valid pointer from the pool */
483 if (verbose >= TC_FLIST) {
484 tc_log_msg(__FILE__, "register frame id = %i", id);
485 }
486 #ifdef STATBUFFER
487 ptr = tc_ring_framebuffer_retrieve_frame(rfb);
488 #else
489 ptr = rfb->alloc(rfb->specs);
490 #endif
491
492 if (!TCFRAMEPTR_IS_NULL(ptr)) {
493 if (status == TC_FRAME_EMPTY) {
494 rfb->empty++;
495 /* blank common attributes */
496 memset(ptr.generic, 0, sizeof(frame_list_t));
497 ptr.generic->id = id;
498 } else if (status == TC_FRAME_WAIT) {
499 rfb->wait++;
500 }
501 ptr.generic->status = status;
502
503 /* enforce */
504 ptr.generic->next = NULL;
505 ptr.generic->prev = NULL;
506
507 if (verbose >= TC_FLIST) {
508 tc_ring_framebuffer_dump_status(rfb, "register_frame");
509 }
510 }
511 return ptr;
512 }
513
514 /*
515 * tc_ring_framebuffer_remove_frame: (NOT thread safe)
516 * De-register and release a given framebuffer;
517 * also updates internal ringbuffer counters.
518 *
519 * That's the function that client code is supposed to use.
520 * In general, don't use release_frame directly, use remove_frame
521 * instead.
522 *
523 * Parameters:
524 * rfb: ring framebuffer to use.
525 * frame: generic pointer to frambuffer to remove.
526 * Return Value:
527 * None.
528 */
tc_ring_framebuffer_remove_frame(TCRingFrameBuffer * rfb,TCFramePtr frame)529 static void tc_ring_framebuffer_remove_frame(TCRingFrameBuffer *rfb,
530 TCFramePtr frame)
531 {
532 if (rfb != NULL || !TCFRAMEPTR_IS_NULL(frame)) {
533 if (frame.generic->status == TC_FRAME_READY) {
534 rfb->ready--;
535 }
536 if (frame.generic->status == TC_FRAME_LOCKED) {
537 rfb->locked--;
538 }
539 /* release valid pointer to pool */
540 #ifdef STATBUFFER
541 tc_ring_framebuffer_release_frame(rfb, frame);
542 #else
543 rfb->free(frame);
544 #endif
545
546 if (verbose >= TC_FLIST) {
547 tc_ring_framebuffer_dump_status(rfb, "remove_frame");
548 }
549 }
550 }
551
552 /* ------------------------------------------------------------------ */
553 /* Backwared-compatible API */
554 /* ------------------------------------------------------------------ */
555
aframe_alloc(int num)556 int aframe_alloc(int num)
557 {
558 return tc_init_ring_framebuffer(&tc_audio_ringbuffer, &tc_specs,
559 tc_audio_alloc, tc_audio_free, num);
560 }
561
vframe_alloc(int num)562 int vframe_alloc(int num)
563 {
564 return tc_init_ring_framebuffer(&tc_video_ringbuffer, &tc_specs,
565 tc_video_alloc, tc_video_free, num);
566 }
567
568
aframe_free(void)569 void aframe_free(void)
570 {
571 tc_fini_ring_framebuffer(&tc_audio_ringbuffer);
572 }
573
vframe_free(void)574 void vframe_free(void)
575 {
576 tc_fini_ring_framebuffer(&tc_video_ringbuffer);
577 }
578
579
580 /* ------------------------------------------------------------------ */
581
582 /*
583 * Macro VS generic functions like above:
584 *
585 * I've used generic code and TCFramePtr in every place I was
586 * capable to introduce them in a *clean* way without using any
587 * casting. Of course there is still a lot of room for improvements,
588 * but back compatibility is an issue too. I'd like to get rid
589 * of all those macro and swtich to pure generic code of course,
590 * so this will be improved in future revisions. In the
591 * meantime, patches and suggestions welcome ;) -- FR
592 */
593
594 #define LIST_FRAME_APPEND(ptr, tail) do { \
595 if ((tail) != NULL) { \
596 (tail)->next = (ptr); \
597 (ptr)->prev = (tail); \
598 } \
599 (tail) = (ptr); \
600 } while (0)
601
602 #define LIST_FRAME_INSERT(ptr, head) do { \
603 if ((head) == NULL) { \
604 (head) = ptr; \
605 } \
606 } while (0)
607
608
aframe_register(int id)609 aframe_list_t *aframe_register(int id)
610 {
611 int interrupted = TC_FALSE;
612 TCFramePtr frame;
613
614 pthread_mutex_lock(&aframe_list_lock);
615
616 if (verbose >= TC_FLIST)
617 tc_log_msg(__FILE__, "(A|register) requesting a new audio frame");
618
619 while ((!interrupted && tc_import_audio_running())
620 && tc_audio_ringbuffer.null == 0) {
621 if (verbose >= TC_FLIST)
622 tc_log_msg(__FILE__, "(A|register) audio frame not ready, waiting");
623 pthread_cond_wait(&audio_import_cond, &aframe_list_lock);
624 if (verbose >= TC_FLIST)
625 tc_log_msg(__FILE__, "(A|register) audio frame wait ended");
626 interrupted = !tc_running();
627 }
628
629 if (interrupted) {
630 frame.audio = NULL;
631 } else {
632 if (verbose >= TC_FLIST)
633 tc_log_msg(__FILE__, "new audio frame ready");
634
635 frame = tc_ring_framebuffer_register_frame(&tc_audio_ringbuffer,
636 id, TC_FRAME_EMPTY);
637 if (!TCFRAMEPTR_IS_NULL(frame)) {
638 /*
639 * complete initialization:
640 * backward-compatible stuff
641 */
642 LIST_FRAME_APPEND(frame.audio, aframe_list_tail);
643 /* first frame registered must set aframe_list_head */
644 LIST_FRAME_INSERT(frame.audio, aframe_list_head);
645 }
646 }
647 pthread_mutex_unlock(&aframe_list_lock);
648 return frame.audio;
649 }
650
vframe_register(int id)651 vframe_list_t *vframe_register(int id)
652 {
653 int interrupted = TC_FALSE;
654 TCFramePtr frame;
655
656 pthread_mutex_lock(&vframe_list_lock);
657
658 if (verbose >= TC_FLIST)
659 tc_log_msg(__FILE__, "(V|register) requesting a new video frame");
660
661 while ((!interrupted && tc_import_video_running())
662 && tc_video_ringbuffer.null == 0) {
663 if (verbose >= TC_FLIST)
664 tc_log_msg(__FILE__, "(V|register) video frame not ready, waiting");
665 pthread_cond_wait(&video_import_cond, &vframe_list_lock);
666 if (verbose >= TC_FLIST)
667 tc_log_msg(__FILE__, "(V|register) video frame wait ended");
668 interrupted = !tc_running();
669 }
670
671 if (interrupted) {
672 frame.video = NULL;
673 } else {
674 if (verbose >= TC_FLIST)
675 tc_log_msg(__FILE__, "new video frame ready");
676
677 frame = tc_ring_framebuffer_register_frame(&tc_video_ringbuffer,
678 id, TC_FRAME_EMPTY);
679 if (!TCFRAMEPTR_IS_NULL(frame)) {
680 /*
681 * complete initialization:
682 * backward-compatible stuff
683 */
684 LIST_FRAME_APPEND(frame.video, vframe_list_tail);
685 /* first frame registered must set vframe_list_head */
686 LIST_FRAME_INSERT(frame.video, vframe_list_head);
687 }
688 }
689 pthread_mutex_unlock(&vframe_list_lock);
690 return frame.video;
691 }
692
693
694 /* ------------------------------------------------------------------ */
695
696
697 #define LIST_FRAME_LINK(ptr, f, tail) do { \
698 /* insert after ptr */ \
699 (ptr)->next = (f)->next; \
700 (f)->next = (ptr); \
701 (ptr)->prev = (f); \
702 \
703 if ((ptr)->next == NULL) { \
704 /* must be last ptr in the list */ \
705 (tail) = (ptr); \
706 } \
707 } while (0)
708
709
aframe_dup(aframe_list_t * f)710 aframe_list_t *aframe_dup(aframe_list_t *f)
711 {
712 int interrupted = TC_FALSE;
713 TCFramePtr frame;
714
715 if (f == NULL) {
716 tc_log_warn(__FILE__, "aframe_dup: empty frame");
717 return NULL;
718 }
719
720 pthread_mutex_lock(&aframe_list_lock);
721
722 while (!interrupted && tc_audio_ringbuffer.null == 0) {
723 if (verbose >= TC_FLIST)
724 tc_log_msg(__FILE__, "(A|dup) audio frame not ready, waiting");
725 pthread_cond_wait(&audio_import_cond, &aframe_list_lock);
726 if (verbose >= TC_FLIST)
727 tc_log_msg(__FILE__, "(A|dup) audio frame wait ended");
728 interrupted = !tc_running();
729 }
730
731 frame = tc_ring_framebuffer_register_frame(&tc_audio_ringbuffer,
732 0, TC_FRAME_WAIT);
733 if (!TCFRAMEPTR_IS_NULL(frame)) {
734 aframe_copy(frame.audio, f, 1);
735
736 LIST_FRAME_LINK(frame.audio, f, aframe_list_tail);
737 }
738 pthread_mutex_unlock(&aframe_list_lock);
739 return frame.audio;
740 }
741
vframe_dup(vframe_list_t * f)742 vframe_list_t *vframe_dup(vframe_list_t *f)
743 {
744 int interrupted = TC_FALSE;
745 TCFramePtr frame;
746
747 if (f == NULL) {
748 tc_log_warn(__FILE__, "vframe_dup: empty frame");
749 return NULL;
750 }
751
752 pthread_mutex_lock(&vframe_list_lock);
753
754 while (!interrupted && tc_video_ringbuffer.null == 0) {
755 if (verbose >= TC_FLIST)
756 tc_log_msg(__FILE__, "(V|dup) video frame not ready, waiting");
757 pthread_cond_wait(&video_import_cond, &vframe_list_lock);
758 if (verbose >= TC_FLIST)
759 tc_log_msg(__FILE__, "(V|dup) video frame wait ended");
760 interrupted = !tc_running();
761 }
762
763 frame = tc_ring_framebuffer_register_frame(&tc_video_ringbuffer,
764 0, TC_FRAME_WAIT);
765 if (!TCFRAMEPTR_IS_NULL(frame)) {
766 vframe_copy(frame.video, f, 1);
767
768 LIST_FRAME_LINK(frame.video, f, vframe_list_tail);
769 }
770 pthread_mutex_unlock(&vframe_list_lock);
771 return frame.video;
772 }
773
774
775 /* ------------------------------------------------------------------ */
776
777 #define LIST_FRAME_REMOVE(ptr, head, tail) do { \
778 if ((ptr)->prev != NULL) { \
779 ((ptr)->prev)->next = (ptr)->next; \
780 } \
781 if ((ptr)->next != NULL) { \
782 ((ptr)->next)->prev = (ptr)->prev; \
783 } \
784 \
785 if ((ptr) == (tail)) { \
786 (tail) = (ptr)->prev; \
787 } \
788 if ((ptr) == (head)) { \
789 (head) = (ptr)->next; \
790 } \
791 } while (0)
792
aframe_remove(aframe_list_t * ptr)793 void aframe_remove(aframe_list_t *ptr)
794 {
795 if (ptr == NULL) {
796 tc_log_warn(__FILE__, "aframe_remove: given NULL frame pointer");
797 } else {
798 TCFramePtr frame;
799 frame.audio = ptr;
800
801 pthread_mutex_lock(&aframe_list_lock);
802
803 LIST_FRAME_REMOVE(ptr, aframe_list_head, aframe_list_tail);
804
805 tc_ring_framebuffer_remove_frame(&tc_audio_ringbuffer, frame);
806 pthread_cond_signal(&audio_import_cond);
807
808 pthread_mutex_unlock(&aframe_list_lock);
809 }
810 }
811
vframe_remove(vframe_list_t * ptr)812 void vframe_remove(vframe_list_t *ptr)
813 {
814 if (ptr == NULL) {
815 tc_log_warn(__FILE__, "vframe_remove: given NULL frame pointer");
816 } else {
817 TCFramePtr frame;
818 frame.video = ptr;
819
820 pthread_mutex_lock(&vframe_list_lock);
821
822 LIST_FRAME_REMOVE(ptr, vframe_list_head, vframe_list_tail);
823
824 tc_ring_framebuffer_remove_frame(&tc_video_ringbuffer, frame);
825 pthread_cond_signal(&video_import_cond);
826
827 pthread_mutex_unlock(&vframe_list_lock);
828 }
829 }
830
831 /* ------------------------------------------------------------------ */
832 /* ------------------------------------------------------------------ */
833
aframe_retrieve_nowait(void)834 static aframe_list_t *aframe_retrieve_nowait(void)
835 {
836 aframe_list_t *ptr = NULL;
837 pthread_mutex_lock(&aframe_list_lock);
838
839 if (verbose >= TC_CLEANUP) {
840 tc_log_msg(__FILE__, "(A|retrieve_nowait) requesting a new audio frame");
841 }
842 if (aframe_list_head != NULL && aframe_list_head->status == TC_FRAME_READY) {
843 ptr = aframe_list_head;
844 }
845 if (verbose >= TC_CLEANUP) {
846 tc_log_msg(__FILE__, "got a new audio frame reference: %p", ptr);
847 }
848 pthread_mutex_unlock(&aframe_list_lock);
849 return ptr;
850 }
851
852
vframe_retrieve_nowait(void)853 static vframe_list_t *vframe_retrieve_nowait(void)
854 {
855 vframe_list_t *ptr = NULL;
856 pthread_mutex_lock(&vframe_list_lock);
857
858 if (verbose >= TC_CLEANUP) {
859 tc_log_msg(__FILE__, "(V|retrieve_nowait) requesting a new video frame");
860 }
861 if (vframe_list_head != NULL && vframe_list_head->status == TC_FRAME_READY) {
862 ptr = vframe_list_head;
863 }
864 if (verbose >= TC_CLEANUP) {
865 tc_log_msg(__FILE__, "got a new video frame reference: %p", ptr);
866 }
867 pthread_mutex_unlock(&vframe_list_lock);
868 return ptr;
869 }
870
871
872 /* ------------------------------------------------------------------ */
873
aframe_retrieve(void)874 aframe_list_t *aframe_retrieve(void)
875 {
876 int interrupted = TC_FALSE;
877 aframe_list_t *ptr = NULL;
878 pthread_mutex_lock(&aframe_list_lock);
879
880 if (verbose >= TC_FLIST)
881 tc_log_msg(__FILE__, "(A|retrieve) requesting a new audio frame");
882 while (!interrupted
883 && (aframe_list_head == NULL
884 || aframe_list_head->status != TC_FRAME_READY)) {
885 if (verbose >= TC_FLIST) {
886 tc_log_msg(__FILE__, "(A|retrieve) audio frame not ready, waiting");
887 tc_ring_framebuffer_dump_status(&tc_audio_ringbuffer, "retrieve");
888 }
889 pthread_cond_wait(&audio_export_cond, &aframe_list_lock);
890 if (verbose >= TC_FLIST)
891 tc_log_msg(__FILE__, "(A|retrieve) audio wait just ended");
892 interrupted = !tc_running();
893 }
894
895 if (!interrupted) {
896 ptr = aframe_list_head;
897 if (verbose >= TC_FLIST)
898 tc_log_msg(__FILE__, "got a new audio frame reference: %p", ptr);
899 }
900 pthread_mutex_unlock(&aframe_list_lock);
901 return ptr;
902 }
903
904
vframe_retrieve(void)905 vframe_list_t *vframe_retrieve(void)
906 {
907 int interrupted = TC_FALSE;
908 vframe_list_t *ptr = NULL;
909 pthread_mutex_lock(&vframe_list_lock);
910
911 if (verbose >= TC_FLIST)
912 tc_log_msg(__FILE__, "(V|retrieve) requesting a new video frame");
913 while (!interrupted
914 && (vframe_list_head == NULL
915 || vframe_list_head->status != TC_FRAME_READY)) {
916 if (verbose >= TC_FLIST) {
917 tc_log_msg(__FILE__, "(V|retrieve) video frame not ready, waiting");
918 tc_ring_framebuffer_dump_status(&tc_video_ringbuffer, "retrieve");
919 }
920 pthread_cond_wait(&video_export_cond, &vframe_list_lock);
921 if (verbose >= TC_FLIST)
922 tc_log_msg(__FILE__, "(V|retrieve) video wait just ended");
923 interrupted = !tc_running();
924 }
925
926 if (!interrupted) {
927 ptr = vframe_list_head;
928 if (verbose >= TC_FLIST)
929 tc_log_msg(__FILE__, "got a new video frame reference: %p", ptr);
930 }
931 pthread_mutex_unlock(&vframe_list_lock);
932 return ptr;
933 }
934
935 #undef LIST_FRAME_RETRIEVE
936
937 /* ------------------------------------------------------------------ */
938
aframe_flush(void)939 void aframe_flush(void)
940 {
941 int i = 0, done = TC_FALSE;
942
943 do {
944 aframe_list_t *ptr = aframe_retrieve_nowait();
945 if (!ptr) {
946 done = TC_TRUE;
947 } else {
948 if (verbose >= TC_CLEANUP) {
949 tc_log_msg(__FILE__,
950 "flushing audio buffer id=[%i] bufid=[%i]",
951 ptr->id, ptr->bufid);
952 }
953 aframe_remove(ptr);
954 i++;
955 }
956 } while (!done);
957
958 if (verbose >= TC_CLEANUP) {
959 tc_log_msg(__FILE__, "flushed %i audio frames", i);
960 }
961 }
962
vframe_flush(void)963 void vframe_flush(void)
964 {
965 int i = 0, done = TC_FALSE;
966
967 do {
968 vframe_list_t *ptr = vframe_retrieve_nowait();
969 if (!ptr) {
970 done = TC_TRUE;
971 } else {
972 if (verbose >= TC_CLEANUP) {
973 tc_log_msg(__FILE__,
974 "flushing video buffer id=[%i] bufid=[%i]",
975 ptr->id, ptr->bufid);
976 }
977 vframe_remove(ptr);
978 i++;
979 }
980 } while (!done);
981
982 if (verbose >= TC_CLEANUP) {
983 tc_log_msg(__FILE__, "flushed %i video frames", i);
984 }
985 }
986
tc_framebuffer_flush(void)987 void tc_framebuffer_flush(void)
988 {
989 aframe_flush();
990 vframe_flush();
991 }
992
993
994 /* ------------------------------------------------------------------ */
995
996 #define DEC_COUNTERS(RFB, STATUS) do { \
997 if ((STATUS) == TC_FRAME_READY) { \
998 (RFB)->ready--; \
999 } \
1000 if ((STATUS) == TC_FRAME_LOCKED) { \
1001 (RFB)->locked--; \
1002 } \
1003 if ((STATUS) == TC_FRAME_WAIT) { \
1004 (RFB)->wait--; \
1005 } \
1006 } while(0)
1007
1008 #define INC_COUNTERS(RFB, STATUS) do { \
1009 if ((STATUS) == TC_FRAME_READY) { \
1010 (RFB)->ready++; \
1011 } \
1012 if ((STATUS) == TC_FRAME_LOCKED) { \
1013 (RFB)->locked++; \
1014 } \
1015 if ((STATUS) == TC_FRAME_WAIT) { \
1016 (RFB)->wait++; \
1017 } \
1018 } while(0)
1019
1020 #define FRAME_SET_STATUS(RFB, PTR, NEW_STATUS) do { \
1021 DEC_COUNTERS((RFB), (PTR)->status); \
1022 (PTR)->status = (NEW_STATUS); \
1023 INC_COUNTERS((RFB), (PTR)->status); \
1024 } while (0)
1025
1026 #define FRAME_LOOKUP(RFB, PTR, OLD_STATUS, NEW_STATUS) do { \
1027 /* move along the chain and check for status */ \
1028 for (; (PTR) != NULL; (PTR) = (PTR)->next) { \
1029 if ((PTR)->status == (OLD_STATUS)) { \
1030 /* found matching frame */ \
1031 FRAME_SET_STATUS(RFB, PTR, NEW_STATUS); \
1032 break; \
1033 } \
1034 } \
1035 } while (0)
1036
1037
aframe_reserve(void)1038 aframe_list_t *aframe_reserve(void)
1039 {
1040 int interrupted = TC_FALSE;
1041 aframe_list_t *ptr = NULL;
1042
1043 pthread_mutex_lock(&aframe_list_lock);
1044
1045 while (!interrupted && tc_audio_ringbuffer.wait == 0) {
1046 if (verbose >= TC_FLIST)
1047 tc_log_msg(__FILE__, "(A|reserve) audio frame not ready, waiting");
1048 pthread_cond_wait(&audio_filter_cond, &aframe_list_lock);
1049 if (verbose >= TC_FLIST)
1050 tc_log_msg(__FILE__, "(A|reserve) audio wait just ended");
1051 interrupted = !tc_running();
1052 }
1053
1054 if (!interrupted) {
1055 ptr = aframe_list_head;
1056 FRAME_LOOKUP(&tc_audio_ringbuffer, ptr, TC_FRAME_WAIT, TC_FRAME_LOCKED);
1057 }
1058
1059 pthread_mutex_unlock(&aframe_list_lock);
1060 return ptr;
1061 }
1062
vframe_reserve(void)1063 vframe_list_t *vframe_reserve(void)
1064 {
1065 int interrupted = TC_FALSE;
1066 vframe_list_t *ptr = NULL;
1067
1068 pthread_mutex_lock(&vframe_list_lock);
1069
1070 while (!interrupted && tc_video_ringbuffer.wait == 0) {
1071 if (verbose >= TC_FLIST)
1072 tc_log_msg(__FILE__, "(V|reserve) video frame not ready, waiting");
1073 pthread_cond_wait(&video_filter_cond, &vframe_list_lock);
1074 if (verbose >= TC_FLIST)
1075 tc_log_msg(__FILE__, "(V|reserve) video wait just ended");
1076 interrupted = !tc_running();
1077 }
1078
1079 if (!interrupted) {
1080 ptr = vframe_list_head;
1081 FRAME_LOOKUP(&tc_video_ringbuffer, ptr, TC_FRAME_WAIT, TC_FRAME_LOCKED);
1082 }
1083
1084 pthread_mutex_unlock(&vframe_list_lock);
1085 return ptr;
1086 }
1087
1088 #undef FRAME_LOOKUP
1089
1090 /* ------------------------------------------------------------------ */
1091
1092
1093 #define FRAME_SET_EXT_STATUS(RFB, PTR, NEW_STATUS) do { \
1094 if ((PTR)->status == TC_FRAME_EMPTY) { \
1095 (RFB)->empty--; \
1096 } \
1097 FRAME_SET_STATUS((RFB), (PTR), (NEW_STATUS)); \
1098 if ((PTR)->status == TC_FRAME_EMPTY) { \
1099 (RFB)->empty++; \
1100 } \
1101 } while (0)
1102
1103
aframe_push_next(aframe_list_t * ptr,int status)1104 void aframe_push_next(aframe_list_t *ptr, int status)
1105 {
1106 if (ptr == NULL) {
1107 /* a bit more of paranoia */
1108 tc_log_warn(__FILE__, "aframe_push_next: given NULL frame pointer");
1109 } else {
1110 pthread_mutex_lock(&aframe_list_lock);
1111 FRAME_SET_EXT_STATUS(&tc_audio_ringbuffer, ptr, status);
1112
1113 if (status == TC_FRAME_WAIT) {
1114 pthread_cond_signal(&audio_filter_cond);
1115 } else if (status == TC_FRAME_READY && ptr == aframe_list_head) { // XXX
1116 pthread_cond_signal(&audio_export_cond);
1117 }
1118 if (verbose >= TC_FLIST) {
1119 tc_ring_framebuffer_dump_status(&tc_audio_ringbuffer, "push_next");
1120 }
1121 pthread_mutex_unlock(&aframe_list_lock);
1122 }
1123 }
1124
1125
vframe_push_next(vframe_list_t * ptr,int status)1126 void vframe_push_next(vframe_list_t *ptr, int status)
1127 {
1128 if (ptr == NULL) {
1129 /* a bit more of paranoia */
1130 tc_log_warn(__FILE__, "vframe_push_next: given NULL frame pointer");
1131 } else {
1132 pthread_mutex_lock(&vframe_list_lock);
1133 FRAME_SET_EXT_STATUS(&tc_video_ringbuffer, ptr, status);
1134
1135 if (status == TC_FRAME_WAIT) {
1136 pthread_cond_signal(&video_filter_cond);
1137 } else if (status == TC_FRAME_READY && ptr == vframe_list_head) { // XXX
1138 pthread_cond_signal(&video_export_cond);
1139 }
1140 if (verbose >= TC_FLIST) {
1141 tc_ring_framebuffer_dump_status(&tc_video_ringbuffer, "push_next");
1142 }
1143 pthread_mutex_unlock(&vframe_list_lock);
1144 }
1145 }
1146
1147 #undef FRAME_SET_STATUS
1148 #undef FRAME_SET_EXT_STATUS
1149
1150 /* ------------------------------------------------------------------ */
1151
1152
aframe_dump_status(void)1153 void aframe_dump_status(void)
1154 {
1155 tc_ring_framebuffer_dump_status(&tc_audio_ringbuffer,
1156 "audio buffer status");
1157 }
1158
vframe_dump_status(void)1159 void vframe_dump_status(void)
1160 {
1161 tc_ring_framebuffer_dump_status(&tc_video_ringbuffer,
1162 "video buffer status");
1163 }
1164
vframe_have_more(void)1165 int vframe_have_more(void)
1166 {
1167 int ret;
1168 pthread_mutex_lock(&vframe_list_lock);
1169 ret = (vframe_list_tail == NULL) ?0 :1;
1170 pthread_mutex_unlock(&vframe_list_lock);
1171 return ret;
1172 }
1173
aframe_have_more(void)1174 int aframe_have_more(void)
1175 {
1176 int ret;
1177 pthread_mutex_lock(&aframe_list_lock);
1178 ret = (aframe_list_tail == NULL) ?0 :1;
1179 pthread_mutex_unlock(&aframe_list_lock);
1180 return ret;
1181 }
1182
1183 /* ------------------------------------------------------------------ */
1184 /* Frame copying routines */
1185 /* ------------------------------------------------------------------ */
1186
aframe_copy(aframe_list_t * dst,const aframe_list_t * src,int copy_data)1187 void aframe_copy(aframe_list_t *dst, const aframe_list_t *src,
1188 int copy_data)
1189 {
1190 if (!dst || !src) {
1191 tc_log_warn(__FILE__, "aframe_copy: given NULL frame pointer");
1192 return;
1193 }
1194
1195 /* copy all common fields with just one move */
1196 ac_memcpy(dst, src, sizeof(frame_list_t));
1197
1198 if (copy_data == 1) {
1199 /* really copy video data */
1200 ac_memcpy(dst->audio_buf, src->audio_buf, dst->audio_size);
1201 } else {
1202 /* soft copy, new frame points to old audio data */
1203 dst->audio_buf = src->audio_buf;
1204 }
1205 }
1206
vframe_copy(vframe_list_t * dst,const vframe_list_t * src,int copy_data)1207 void vframe_copy(vframe_list_t *dst, const vframe_list_t *src,
1208 int copy_data)
1209 {
1210 if (!dst || !src) {
1211 tc_log_warn(__FILE__, "vframe_copy: given NULL frame pointer");
1212 return;
1213 }
1214
1215 /* copy all common fields with just one move */
1216 ac_memcpy(dst, src, sizeof(frame_list_t));
1217
1218 dst->deinter_flag = src->deinter_flag;
1219 dst->free = src->free;
1220 /*
1221 * we assert that plane pointers *are already properly set*
1222 * we're focused on copy _content_ here.
1223 */
1224
1225 if (copy_data == 1) {
1226 /* really copy video data */
1227 ac_memcpy(dst->video_buf, src->video_buf, dst->video_size);
1228 } else {
1229 /* soft copy, new frame points to old video data */
1230 dst->video_buf = src->video_buf;
1231 }
1232 }
1233
1234 /*************************************************************************/
1235
vframe_get_counters(int * im,int * fl,int * ex)1236 void vframe_get_counters(int *im, int *fl, int *ex)
1237 {
1238 pthread_mutex_lock(&vframe_list_lock);
1239 *im = tc_video_ringbuffer.null + tc_video_ringbuffer.empty;
1240 *fl = tc_video_ringbuffer.wait + tc_video_ringbuffer.locked;
1241 *ex = tc_video_ringbuffer.ready;
1242 pthread_mutex_unlock(&vframe_list_lock);
1243 }
1244
aframe_get_counters(int * im,int * fl,int * ex)1245 void aframe_get_counters(int *im, int *fl, int *ex)
1246 {
1247 pthread_mutex_lock(&aframe_list_lock);
1248 *im = tc_audio_ringbuffer.null + tc_audio_ringbuffer.empty;
1249 *fl = tc_audio_ringbuffer.wait + tc_audio_ringbuffer.locked;
1250 *ex = tc_audio_ringbuffer.ready;
1251 pthread_mutex_unlock(&aframe_list_lock);
1252 }
1253
tc_framebuffer_get_counters(int * im,int * fl,int * ex)1254 void tc_framebuffer_get_counters(int *im, int *fl, int *ex)
1255 {
1256 int v_im, v_fl, v_ex, a_im, a_fl, a_ex;
1257
1258 vframe_get_counters(&v_im, &v_fl, &v_ex);
1259 aframe_get_counters(&a_im, &a_fl, &a_ex);
1260
1261 *im = v_im + a_im;
1262 *fl = v_fl + a_fl;
1263 *ex = v_ex + a_ex;
1264 }
1265
1266 /*************************************************************************/
1267 /*
1268 * Local variables:
1269 * c-file-style: "stroustrup"
1270 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
1271 * indent-tabs-mode: nil
1272 * End:
1273 *
1274 * vim: expandtab shiftwidth=4:
1275 */
1276