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