1 /*
2  *  decoder.c -- transcode import layer module, implementation.
3  *
4  *  Copyright (C) Thomas Oestreich - June 2001
5  *  Updated and partially rewritten by
6  *  Francesco Romani - July 2007
7  *
8  *  This file is part of transcode, a video stream processing tool
9  *
10  *  transcode is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  transcode is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with GNU Make; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include "transcode.h"
27 #include "dl_loader.h"
28 #include "filter.h"
29 #include "framebuffer.h"
30 #include "video_trans.h"
31 #include "audio_trans.h"
32 #include "decoder.h"
33 #include "encoder.h"
34 #include "frame_threads.h"
35 #include "cmdline.h"
36 #include "probe.h"
37 
38 
39 /*************************************************************************/
40 
41 /* anonymous since used just internally */
42 enum {
43     TC_IM_THREAD_UNKNOWN = -1, /* halting cause not specified      */
44     TC_IM_THREAD_DONE = 0,     /* import ends as expected          */
45     TC_IM_THREAD_INTERRUPT,    /* external event interrupts import */
46     TC_IM_THREAD_EXT_ERROR,    /* external (I/O) error             */
47     TC_IM_THREAD_INT_ERROR,    /* internal (core) error            */
48     TC_IM_THREAD_PROBE_ERROR,  /* source is incompatible           */
49 };
50 
51 
52 typedef struct tcdecoderdata_ TCDecoderData;
53 struct tcdecoderdata_ {
54     const char *tag;            /* audio or video? used for logging */
55     FILE *fd;                   /* for stream import                */
56     void *im_handle;            /* import module handle             */
57     volatile int active_flag;   /* active or not?                   */
58     pthread_t thread_id;
59     pthread_mutex_t lock;
60 };
61 
62 
63 /*************************************************************************/
64 
65 static TCDecoderData video_decdata = {
66     .tag         = "video",
67     .fd          = NULL,
68     .im_handle   = NULL,
69     .active_flag = 0,
70     .thread_id   = (pthread_t)0,
71     .lock        = PTHREAD_MUTEX_INITIALIZER,
72 };
73 
74 static TCDecoderData audio_decdata = {
75     .tag         = "audio",
76     .fd          = NULL,
77     .im_handle   = NULL,
78     .active_flag = 0,
79     .thread_id   = (pthread_t)0,
80     .lock        = PTHREAD_MUTEX_INITIALIZER,
81 };
82 
83 static pthread_t tc_pthread_main = (pthread_t)0;
84 static long int vframecount = 0;
85 static long int aframecount = 0;
86 
87 
88 /*************************************************************************/
89 /*  Old-style compatibility support functions                            */
90 /*************************************************************************/
91 
92 struct modpair {
93     int codec; /* internal codec/colorspace/format */
94     int caps;  /* module capabilities              */
95 };
96 
97 static const struct modpair audpairs[] = {
98     { CODEC_PCM,     TC_CAP_PCM    },
99     { CODEC_AC3,     TC_CAP_AC3    },
100     { CODEC_RAW,     TC_CAP_AUD    },
101     { CODEC_NULL,    TC_CAP_NONE   } /* end marker, must be the last */
102 };
103 
104 static const struct modpair vidpairs[] = {
105     { CODEC_RGB,     TC_CAP_RGB    },
106     { CODEC_YUV,     TC_CAP_YUV    },
107     { CODEC_YUV422,  TC_CAP_YUV422 },
108     { CODEC_RAW_YUV, TC_CAP_VID    },
109     { CODEC_RAW,     TC_CAP_VID    },
110     { CODEC_NULL,    TC_CAP_NONE   } /* end marker, must be the last */
111 };
112 
113 
114 /*
115  * check_module_caps: verifies if a module is compatible with transcode
116  * core colorspace/format settings.
117  *
118  * Parameters:
119  *       param: data describing (old-style) module capabilities.
120  *       codec: codec/format/colorspace requested by core.
121  *      mpairs: table of formats/capabilities to be used for check.
122  * Return Value:
123  *       0: module INcompatible with core format request.
124  *      !0: module can accomplish to the core format request.
125  */
check_module_caps(const transfer_t * param,int codec,const struct modpair * mpairs)126 static int check_module_caps(const transfer_t *param, int codec,
127                              const struct modpair *mpairs)
128 {
129     int caps = 0;
130 
131     if (param->flag == verbose) {
132         caps = (codec == mpairs[0].codec);
133         /* legacy: grab the first and stay */
134     } else {
135         int i = 0;
136 
137         /* module returned capability flag */
138         if (verbose >= TC_DEBUG) {
139             tc_log_msg(__FILE__, "Capability flag 0x%x | 0x%x",
140                        param->flag, codec);
141         }
142 
143         for (i = 0; mpairs[i].codec != CODEC_NULL; i++) {
144             if (codec == mpairs[i].codec) {
145                 caps = (param->flag & mpairs[i].caps);
146                 break;
147             }
148         }
149     }
150     return caps;
151 }
152 
153 /*************************************************************************/
154 /*                  optimized block-wise fread                           */
155 /*************************************************************************/
156 
157 #ifdef PIPE_BUF
158 #define BLOCKSIZE PIPE_BUF /* 4096 on linux-x86 */
159 #else
160 #define BLOCKSIZE 4096
161 #endif
162 
mfread(uint8_t * buf,int size,int nelem,FILE * f)163 static int mfread(uint8_t *buf, int size, int nelem, FILE *f)
164 {
165     int fd = fileno(f);
166     int n = 0, r1 = 0, r2 = 0;
167     while (n < size*nelem-BLOCKSIZE) {
168         if ( !(r1 = read (fd, &buf[n], BLOCKSIZE))) return 0;
169         n += r1;
170     }
171     while (size*nelem-n) {
172         if ( !(r2 = read (fd, &buf[n], size*nelem-n)))return 0;
173         n += r2;
174     }
175     return nelem;
176 }
177 
178 /*************************************************************************/
179 /*               some macro goodies                                      */
180 /*************************************************************************/
181 
182 #define RETURN_IF_NULL(HANDLE, MEDIA) do { \
183     if ((HANDLE) == NULL) { \
184         tc_log_error(PACKAGE, "Loading %s import module failed", (MEDIA)); \
185         tc_log_error(PACKAGE, \
186                      "Did you enable this module when you ran configure?"); \
187         return TC_ERROR; \
188     } \
189 } while (0)
190 
191 #define RETURN_IF_NOT_SUPPORTED(CAPS, MEDIA) do { \
192     if (!(CAPS)) { \
193         tc_log_error(PACKAGE, "%s format not supported by import module", \
194                      (MEDIA)); \
195         return TC_ERROR; \
196     } \
197 } while (0)
198 
199 #define RETURN_IF_FUNCTION_FAILED(func, ...) do { \
200     int ret = func(__VA_ARGS__); \
201     if (ret != TC_OK) { \
202         return TC_ERROR; \
203     } \
204 } while (0)
205 
206 #define RETURN_IF_REGISTRATION_FAILED(PTR, MEDIA) do { \
207     /* ok, that's pure paranoia */ \
208     if ((PTR) == NULL) { \
209         tc_log_error(__FILE__, "frame registration failed (%s)", (MEDIA)); \
210         return TC_IM_THREAD_INT_ERROR; \
211     } \
212 } while (0)
213 
214 /*************************************************************************/
215 /*               stream-specific functions                               */
216 /*************************************************************************/
217 /*               status handling functions                               */
218 /*************************************************************************/
219 
220 
221 /*
222  * tc_import_thread_stop (Thread safe): mark the import status flag
223  * as `stopped'; the import thread will stop as soon as is possible.
224  *
225  * Parameters:
226  *      decdata: pointer to a TCDecoderData structure representing the
227  *               import thread to stop.
228  * Return Value:
229  *      None
230  */
tc_import_thread_stop(TCDecoderData * decdata)231 static void tc_import_thread_stop(TCDecoderData *decdata)
232 {
233     pthread_mutex_lock(&decdata->lock);
234     decdata->active_flag = TC_FALSE;
235     pthread_mutex_unlock(&decdata->lock);
236 }
237 
238 /*
239  * tc_import_thread_start (Thread safe): mark the import status flag
240  * as `started'; import thread become running and it starts producing data.
241  *
242  * Parameters:
243  *      decdata: pointer to a TCDecoderData structure representing the
244  *               import thread to start.
245  * Return Value:
246  *      None
247  */
tc_import_thread_start(TCDecoderData * decdata)248 static void tc_import_thread_start(TCDecoderData *decdata)
249 {
250     pthread_mutex_lock(&decdata->lock);
251     decdata->active_flag = TC_TRUE;
252     pthread_mutex_unlock(&decdata->lock);
253 }
254 
255 /*
256  * tc_import_thread_is_active (Thread safe): poll for the current
257  * status flag of an import thread.
258  *
259  * Parameters:
260  *      decdata: pointer to a TCDecoderData structure representing the
261  *               import thread to query.
262  * Return Value:
263  *      TC_FALSE: import thread is stopped or stopping.
264  *      TC_TRUE:  import thread is running.
265  */
266 
tc_import_thread_is_active(TCDecoderData * decdata)267 static int tc_import_thread_is_active(TCDecoderData *decdata)
268 {
269     int flag;
270     pthread_mutex_lock(&decdata->lock);
271     flag = decdata->active_flag;
272     pthread_mutex_unlock(&decdata->lock);
273     return flag;
274 }
275 
276 /*************************************************************************/
277 /*               stream open/close functions                             */
278 /*************************************************************************/
279 
280 /*
281  * tc_import_{video,audio}_open: open audio stream for importing.
282  *
283  * Parameters:
284  *      vob: vob structure
285  * Return Value:
286  *         TC_OK: succesfull.
287  *      TC_ERROR: failure; reason was tc_log*()ged out.
288  */
tc_import_video_open(vob_t * vob)289 static int tc_import_video_open(vob_t *vob)
290 {
291     int ret;
292     transfer_t import_para;
293 
294     memset(&import_para, 0, sizeof(transfer_t));
295 
296     import_para.flag = TC_VIDEO;
297 
298     ret = tcv_import(TC_IMPORT_OPEN, &import_para, vob);
299     if (ret < 0) {
300         tc_log_error(PACKAGE, "video import module error: OPEN failed");
301         return TC_ERROR;
302     }
303 
304     video_decdata.fd = import_para.fd;
305 
306     return TC_OK;
307 }
308 
309 
tc_import_audio_open(vob_t * vob)310 static int tc_import_audio_open(vob_t *vob)
311 {
312     int ret;
313     transfer_t import_para;
314 
315     memset(&import_para, 0, sizeof(transfer_t));
316 
317     import_para.flag = TC_AUDIO;
318 
319     ret = tca_import(TC_IMPORT_OPEN, &import_para, vob);
320     if (ret < 0) {
321         tc_log_error(PACKAGE, "audio import module error: OPEN failed");
322         return TC_ERROR;
323     }
324 
325     audio_decdata.fd = import_para.fd;
326 
327     return TC_OK;
328 }
329 
330 /*
331  * tc_import_{video,audio}_close: close audio stream used for importing.
332  *
333  * Parameters:
334  *      None.
335  * Return Value:
336  *         TC_OK: succesfull.
337  *      TC_ERROR: failure; reason was tc_log*()ged out.
338  */
339 
tc_import_audio_close(void)340 static int tc_import_audio_close(void)
341 {
342     int ret;
343     transfer_t import_para;
344 
345     memset(&import_para, 0, sizeof(transfer_t));
346 
347     import_para.flag = TC_AUDIO;
348     import_para.fd   = audio_decdata.fd;
349 
350     ret = tca_import(TC_IMPORT_CLOSE, &import_para, NULL);
351     if (ret == TC_IMPORT_ERROR) {
352         tc_log_warn(PACKAGE, "audio import module error: CLOSE failed");
353         return TC_ERROR;
354     }
355     audio_decdata.fd = NULL;
356 
357     return TC_OK;
358 }
359 
tc_import_video_close(void)360 static int tc_import_video_close(void)
361 {
362     int ret;
363     transfer_t import_para;
364 
365     memset(&import_para, 0, sizeof(transfer_t));
366 
367     import_para.flag = TC_VIDEO;
368     import_para.fd   = video_decdata.fd;
369 
370     ret = tcv_import(TC_IMPORT_CLOSE, &import_para, NULL);
371     if (ret == TC_IMPORT_ERROR) {
372         tc_log_warn(PACKAGE, "video import module error: CLOSE failed");
373         return TC_ERROR;
374     }
375     video_decdata.fd = NULL;
376 
377     return TC_OK;
378 }
379 
380 
381 /*************************************************************************/
382 /*                       the import loops                                */
383 /*************************************************************************/
384 
385 
386 #define MARK_TIME_RANGE(PTR, VOB) do { \
387     /* Set skip attribute based on -c */ \
388     if (fc_time_contains((VOB)->ttime, (PTR)->id)) \
389         (PTR)->attributes &= ~TC_FRAME_IS_OUT_OF_RANGE; \
390     else \
391         (PTR)->attributes |= TC_FRAME_IS_OUT_OF_RANGE; \
392 } while (0)
393 
394 
395 /*
396  * stop_cause: specify the cause of an import loop termination.
397  *
398  * Parameters:
399  *      ret: termination cause identifier to be specified
400  * Return Value:
401  *      the most specific recognizable termination cause.
402  */
stop_cause(int ret)403 static int stop_cause(int ret)
404 {
405     if (ret == TC_IM_THREAD_UNKNOWN) {
406         if (tc_interrupted()) {
407             ret = TC_IM_THREAD_INTERRUPT;
408         } else if (tc_stopped()) {
409             ret = TC_IM_THREAD_DONE;
410         }
411     }
412     return ret;
413 }
414 
415 /*
416  * {video,audio}_import_loop: data import loops. Feed frame FIFOs with
417  * new data forever until are interrupted or stopped.
418  *
419  * Parameters:
420  *      vob: vob structure
421  * Return Value:
422  *      TC_IM_THREAD_* value reporting operation status.
423  */
video_import_loop(vob_t * vob)424 static int video_import_loop(vob_t *vob)
425 {
426     int ret = 0, vbytes = 0;
427     vframe_list_t *ptr = NULL;
428     transfer_t import_para;
429     TCFrameStatus next = (tc_frame_threads_have_video_workers())
430                             ?TC_FRAME_WAIT :TC_FRAME_READY;
431     int im_ret = TC_IM_THREAD_UNKNOWN;
432 
433     if (verbose >= TC_DEBUG)
434         tc_log_msg(__FILE__, "video thread id=%ld", (unsigned long)pthread_self());
435 
436     vbytes = vob->im_v_size;
437 
438     while (tc_running() && tc_import_thread_is_active(&video_decdata)) {
439 
440         if (verbose >= TC_THREADS)
441             tc_log_msg(__FILE__, "(V) %10s [%ld] %i bytes", "requesting",
442                        vframecount, vbytes);
443 
444         /* stage 1: register new blank frame */
445         ptr = vframe_register(vframecount);
446         if (ptr == NULL) {
447             if (verbose >= TC_THREADS)
448                 tc_log_msg(__FILE__, "(V) frame registration interrupted!");
449             break;
450         }
451 
452         /* stage 2: fill the frame with data */
453         ptr->attributes = 0;
454         MARK_TIME_RANGE(ptr, vob);
455 
456         if (verbose >= TC_THREADS)
457             tc_log_msg(__FILE__, "(V) new frame registered and marked, now filling...");
458 
459         if (video_decdata.fd != NULL) {
460             if (vbytes && (ret = mfread(ptr->video_buf, vbytes, 1, video_decdata.fd)) != 1)
461                 ret = -1;
462             ptr->video_len  = vbytes;
463             ptr->video_size = vbytes;
464         } else {
465             import_para.fd         = NULL;
466             import_para.buffer     = ptr->video_buf;
467             import_para.buffer2    = ptr->video_buf2;
468             import_para.size       = vbytes;
469             import_para.flag       = TC_VIDEO;
470             import_para.attributes = ptr->attributes;
471 
472             ret = tcv_import(TC_IMPORT_DECODE, &import_para, vob);
473 
474             ptr->video_len   = import_para.size;
475             ptr->video_size  = import_para.size;
476             ptr->attributes |= import_para.attributes;
477         }
478 
479         if (verbose >= TC_THREADS)
480             tc_log_msg(__FILE__, "(V) new frame filled (%s)", (ret == -1) ?"FAILED" :"OK");
481 
482         if (ret < 0) {
483             if (verbose >= TC_DEBUG)
484                 tc_log_msg(__FILE__, "(V) data read failed - end of stream");
485 
486             ptr->video_len  = 0;
487             ptr->video_size = 0;
488             if (!tc_has_more_video_in_file(vob)) {
489                 ptr->attributes = TC_FRAME_IS_END_OF_STREAM;
490             } else {
491                 ptr->attributes = TC_FRAME_IS_SKIPPED;
492             }
493         }
494 
495         ptr->v_height   = vob->im_v_height;
496         ptr->v_width    = vob->im_v_width;
497         ptr->v_bpp      = BPP;
498 
499         if (verbose >= TC_THREADS)
500             tc_log_msg(__FILE__, "(V) new frame is being processed");
501 
502         /* stage 3: account filled frame and process it if needed */
503         if (TC_FRAME_NEED_PROCESSING(ptr)) {
504             //first stage pre-processing - (synchronous)
505             preprocess_vid_frame(vob, ptr);
506 
507             //filter pre-processing - (synchronous)
508             ptr->tag = TC_VIDEO|TC_PRE_S_PROCESS;
509             tc_filter_process((frame_list_t *)ptr);
510         }
511 
512         if (verbose >= TC_THREADS)
513             tc_log_msg(__FILE__, "(V) new frame ready to be pushed");
514 
515         /* stage 4: push frame to next transcoding layer */
516         vframe_push_next(ptr, next);
517 
518         if (verbose >= TC_THREADS)
519             tc_log_msg(__FILE__, "(V) %10s [%ld] %i bytes", "received",
520                        vframecount, ptr->video_size);
521 
522         if (verbose >= TC_THREADS)
523             tc_log_msg(__FILE__, "(V) new frame pushed");
524 
525         if (ret < 0) {
526             /*
527              * we must delay this stuff in order to properly END_OF_STREAM
528              * frames _and_ to push them to subsequent stages
529              */
530             tc_import_thread_stop(&audio_decdata);
531             im_ret = TC_IM_THREAD_DONE;
532             break;
533         }
534         vframecount++;
535     }
536     return stop_cause(im_ret);
537 }
538 
539 
540 #define GET_AUDIO_FRAME do { \
541     if (audio_decdata.fd != NULL) { \
542         if (abytes && (ret = mfread(ptr->audio_buf, abytes, 1, audio_decdata.fd)) != 1) { \
543             ret = -1; \
544         } \
545         ptr->audio_len  = abytes; \
546         ptr->audio_size = abytes; \
547     } else { \
548         import_para.fd         = NULL; \
549         import_para.buffer     = ptr->audio_buf; \
550         import_para.size       = abytes; \
551         import_para.flag       = TC_AUDIO; \
552         import_para.attributes = ptr->attributes; \
553         \
554         ret = tca_import(TC_IMPORT_DECODE, &import_para, vob); \
555         \
556         ptr->audio_len  = import_para.size; \
557         ptr->audio_size = import_para.size; \
558     } \
559 } while (0)
560 
audio_import_loop(vob_t * vob)561 static int audio_import_loop(vob_t *vob)
562 {
563     int ret = 0, abytes;
564     aframe_list_t *ptr = NULL;
565     transfer_t import_para;
566     TCFrameStatus next = (tc_frame_threads_have_audio_workers())
567                             ?TC_FRAME_WAIT :TC_FRAME_READY;
568     int im_ret = TC_IM_THREAD_UNKNOWN;
569 
570     if (verbose >= TC_DEBUG)
571         tc_log_msg(__FILE__, "audio thread id=%ld",
572                    (unsigned long)pthread_self());
573 
574     abytes = vob->im_a_size;
575 
576     while (tc_running() && tc_import_thread_is_active(&audio_decdata)) {
577         /* stage 1: audio adjustment for non-PAL frame rates */
578         if (aframecount != 0 && aframecount % TC_LEAP_FRAME == 0) {
579             abytes = vob->im_a_size + vob->a_leap_bytes;
580         } else {
581             abytes = vob->im_a_size;
582         }
583 
584         if (verbose >= TC_THREADS)
585             tc_log_msg(__FILE__, "(A) %10s [%ld] %i bytes",
586                        "requesting", aframecount, abytes);
587 
588         /* stage 2: register new blank frame */
589         ptr = aframe_register(aframecount);
590         if (ptr == NULL) {
591             if (verbose >= TC_THREADS)
592                 tc_log_msg(__FILE__, "(A) frame registration interrupted!");
593             break;
594         }
595 
596         ptr->attributes = 0;
597         MARK_TIME_RANGE(ptr, vob);
598 
599         if (verbose >= TC_THREADS)
600             tc_log_msg(__FILE__, "(A) new frame registered and marked, now syncing...");
601 
602         /* stage 3: fill the frame with data */
603         /* stage 3.1: resync audio by discarding frames, if needed */
604         if (vob->sync > 0) {
605             // discard vob->sync frames
606             while (vob->sync--) {
607                 GET_AUDIO_FRAME;
608 
609                 if (ret == -1)
610                     break;
611             }
612             vob->sync++;
613         }
614 
615         /* stage 3.2: grab effective audio data */
616         if (vob->sync == 0) {
617             GET_AUDIO_FRAME;
618         }
619 
620         /* stage 3.3: silence at last */
621         if (vob->sync < 0) {
622             if (verbose >= TC_DEBUG)
623                 tc_log_msg(__FILE__, " zero padding %d", vob->sync);
624             memset(ptr->audio_buf, 0, abytes);
625             ptr->audio_len  = abytes;
626             ptr->audio_size = abytes;
627             vob->sync++;
628         }
629         /* stage 3.x final note: all this stuff can be done in a cleaner way... */
630 
631         if (verbose >= TC_THREADS)
632             tc_log_msg(__FILE__, "(A) syncing done, new frame ready to be filled...");
633 
634         if (ret < 0) {
635             if (verbose >= TC_DEBUG)
636                 tc_log_msg(__FILE__, "(A) data read failed - end of stream");
637 
638             ptr->audio_len  = 0;
639             ptr->audio_size = 0;
640             if (!tc_has_more_audio_in_file(vob)) {
641                 ptr->attributes = TC_FRAME_IS_END_OF_STREAM;
642             } else {
643                 ptr->attributes = TC_FRAME_IS_SKIPPED;
644             }
645         }
646 
647         // init frame buffer structure with import frame data
648         ptr->a_rate = vob->a_rate;
649         ptr->a_bits = vob->a_bits;
650         ptr->a_chan = vob->a_chan;
651 
652         /* stage 4: account filled frame and process it if needed */
653         if (TC_FRAME_NEED_PROCESSING(ptr)) {
654             ptr->tag = TC_AUDIO|TC_PRE_S_PROCESS;
655             tc_filter_process((frame_list_t *)ptr);
656         }
657 
658         /* stage 5: push frame to next transcoding layer */
659         aframe_push_next(ptr, next);
660 
661         if (verbose >= TC_THREADS)
662             tc_log_msg(__FILE__, "(A) %10s [%ld] %i bytes", "received",
663                                  aframecount, ptr->audio_size);
664 
665         if (ret < 0) {
666             tc_import_thread_stop(&audio_decdata);
667             im_ret = TC_IM_THREAD_DONE;
668             break;
669         }
670         aframecount++;
671     }
672     return stop_cause(im_ret);
673 }
674 
675 #undef GET_AUDIO_FRAME
676 #undef MARK_TIME_RANGE
677 
678 /*************************************************************************/
679 /*        ladies and gentlemens, the thread routines                     */
680 /*************************************************************************/
681 
682 /* audio decode thread wrapper */
audio_import_thread(void * _vob)683 static void *audio_import_thread(void *_vob)
684 {
685     static int ret = 0;
686     ret = audio_import_loop(_vob);
687     if (verbose >= TC_CLEANUP)
688         tc_log_msg(__FILE__, "audio decode loop ends with code 0x%i", ret);
689     pthread_exit(&ret);
690 }
691 
692 /* video decode thread wrapper */
video_import_thread(void * _vob)693 static void *video_import_thread(void *_vob)
694 {
695     static int ret = 0;
696     ret = video_import_loop(_vob);
697     if (verbose >= TC_CLEANUP)
698         tc_log_msg(__FILE__, "video decode loop ends with code 0x%i", ret);
699     pthread_exit(&ret);
700 }
701 
702 
703 /*************************************************************************/
704 /*               main API functions                                      */
705 /*************************************************************************/
706 
tc_import_status()707 int tc_import_status()
708 {
709     return tc_import_video_status() && tc_import_audio_status();
710 }
711 
tc_import_video_running(void)712 int tc_import_video_running(void)
713 {
714     return tc_import_thread_is_active(&video_decdata);
715 }
716 
tc_import_audio_running(void)717 int tc_import_audio_running(void)
718 {
719     return tc_import_thread_is_active(&audio_decdata);
720 }
721 
tc_import_video_status(void)722 int tc_import_video_status(void)
723 {
724     return vframe_have_more() || tc_import_thread_is_active(&video_decdata);
725 }
726 
tc_import_audio_status(void)727 int tc_import_audio_status(void)
728 {
729     return aframe_have_more() || tc_import_thread_is_active(&audio_decdata);
730 }
731 
732 
tc_import_threads_cancel(void)733 void tc_import_threads_cancel(void)
734 {
735     void *status = NULL;
736     int vret, aret;
737 
738     if (tc_decoder_delay)
739         tc_log_info(__FILE__, "sleeping for %i seconds to cool down", tc_decoder_delay);
740     sleep(tc_decoder_delay);
741     tc_log_info(__FILE__, "cancelling the import threads");
742 
743     tc_import_thread_stop(&video_decdata);
744     tc_import_thread_stop(&audio_decdata);
745 
746     tc_framebuffer_interrupt_import();
747 
748     vret = pthread_join(video_decdata.thread_id, &status);
749     if (verbose >= TC_DEBUG) {
750         int *pst = status; /* avoid explicit cast in log below */
751         tc_log_msg(__FILE__, "video thread exit (ret_code=%i)"
752                              " (status_code=%i)", vret, *pst);
753     }
754 
755     aret = pthread_join(audio_decdata.thread_id, &status);
756     if (verbose >= TC_DEBUG) {
757         int *pst = status; /* avoid explicit cast in log below */
758         tc_log_msg(__FILE__, "audio thread exit (ret_code=%i)"
759                              " (status_code=%i)", aret, *pst);
760     }
761     return;
762 }
763 
764 
tc_import_threads_create(vob_t * vob)765 void tc_import_threads_create(vob_t *vob)
766 {
767     int ret;
768 
769     tc_import_thread_start(&audio_decdata);
770     ret = pthread_create(&audio_decdata.thread_id, NULL,
771                          audio_import_thread, vob);
772     if (ret != 0)
773         tc_error("failed to start audio stream import thread");
774 
775     tc_import_thread_start(&video_decdata);
776     ret = pthread_create(&video_decdata.thread_id, NULL,
777                          video_import_thread, vob);
778     if (ret != 0)
779         tc_error("failed to start video stream import thread");
780 }
781 
782 
tc_import_init(vob_t * vob,const char * a_mod,const char * v_mod)783 int tc_import_init(vob_t *vob, const char *a_mod, const char *v_mod)
784 {
785     transfer_t import_para;
786     int caps;
787 
788     a_mod = (a_mod == NULL) ?TC_DEFAULT_IMPORT_AUDIO :a_mod;
789     audio_decdata.im_handle = load_module(a_mod, TC_IMPORT+TC_AUDIO);
790     RETURN_IF_NULL(audio_decdata.im_handle, "audio");
791 
792     v_mod = (v_mod == NULL) ?TC_DEFAULT_IMPORT_VIDEO :v_mod;
793     video_decdata.im_handle = load_module(v_mod, TC_IMPORT+TC_VIDEO);
794     RETURN_IF_NULL(video_decdata.im_handle, "video");
795 
796     memset(&import_para, 0, sizeof(transfer_t));
797     import_para.flag = verbose;
798     tca_import(TC_IMPORT_NAME, &import_para, NULL);
799 
800     caps = check_module_caps(&import_para, vob->im_a_codec, audpairs);
801     RETURN_IF_NOT_SUPPORTED(caps, "audio");
802 
803     memset(&import_para, 0, sizeof(transfer_t));
804     import_para.flag = verbose;
805     tcv_import(TC_IMPORT_NAME, &import_para, NULL);
806 
807     caps = check_module_caps(&import_para, vob->im_v_codec, vidpairs);
808     RETURN_IF_NOT_SUPPORTED(caps, "video");
809 
810     tc_pthread_main = pthread_self();
811 
812     return TC_OK;
813 }
814 
815 
tc_import_open(vob_t * vob)816 int tc_import_open(vob_t *vob)
817 {
818     RETURN_IF_FUNCTION_FAILED(tc_import_audio_open, vob);
819     RETURN_IF_FUNCTION_FAILED(tc_import_video_open, vob);
820 
821     return TC_OK;
822 }
823 
tc_import_close(void)824 int tc_import_close(void)
825 {
826     RETURN_IF_FUNCTION_FAILED(tc_import_audio_close);
827     RETURN_IF_FUNCTION_FAILED(tc_import_video_close);
828 
829     return TC_OK;
830 }
831 
tc_import_shutdown(void)832 void tc_import_shutdown(void)
833 {
834     if (verbose >= TC_DEBUG) {
835         tc_log_msg(__FILE__, "unloading audio import module");
836     }
837 
838     unload_module(audio_decdata.im_handle);
839     audio_decdata.im_handle = NULL;
840 
841     if (verbose >= TC_DEBUG) {
842         tc_log_msg(__FILE__, "unloading video import module");
843     }
844 
845     unload_module(video_decdata.im_handle);
846     video_decdata.im_handle = NULL;
847 }
848 
849 
850 /*************************************************************************/
851 /*             the new multi-input sequential API                        */
852 /*************************************************************************/
853 
dump_probeinfo(const ProbeInfo * pi,int i,const char * tag)854 static void dump_probeinfo(const ProbeInfo *pi, int i, const char *tag)
855 {
856 #ifdef DEBUG
857     tc_log_warn(__FILE__, "(%s): %ix%i asr=%i frc=%i codec=0x%lX",
858                 tag, pi->width, pi->height, pi->asr, pi->frc, pi->codec);
859 
860     if (i >= 0) {
861         tc_log_warn(__FILE__, "(%s): #%i %iHz %ich %ibits format=0x%X",
862                     tag, i,
863                     pi->track[i].samplerate, pi->track[i].chan,
864                     pi->track[i].bits, pi->track[i].format);
865     }
866 #endif
867 }
868 
probe_im_stream(const char * src,ProbeInfo * info)869 static int probe_im_stream(const char *src, ProbeInfo *info)
870 {
871     static pthread_mutex_t probe_mutex = PTHREAD_MUTEX_INITIALIZER; /* XXX */
872     /* UGLY! */
873     int ret = 1; /* be optimistic! */
874 
875     pthread_mutex_lock(&probe_mutex);
876     ret = probe_stream_data(src, seek_range, info);
877     pthread_mutex_unlock(&probe_mutex);
878 
879     dump_probeinfo(info, 0, "probed");
880 
881     return ret;
882 }
883 
probe_matches(const ProbeInfo * ref,const ProbeInfo * cand,int i)884 static int probe_matches(const ProbeInfo *ref, const ProbeInfo *cand, int i)
885 {
886     if (ref->width  != cand->width || ref->height != cand->height
887      || ref->frc    != cand->frc   || ref->asr    != cand->asr
888      || ref->codec  != cand->codec) {
889         tc_log_error(__FILE__, "video parameters mismatch");
890         dump_probeinfo(ref,  -1, "old");
891         dump_probeinfo(cand, -1, "new");
892         return 0;
893     }
894 
895     if (i > ref->num_tracks || i > cand->num_tracks) {
896         tc_log_error(__FILE__, "track parameters mismatch (i=%i|ref=%i|cand=%i)",
897                      i, ref->num_tracks, cand->num_tracks);
898         return 0;
899     }
900     if (ref->track[i].samplerate != cand->track[i].samplerate
901      || ref->track[i].chan       != cand->track[i].chan      ) {
902 //     || ref->track[i].bits       != cand->track[i].bits      ) {
903 //     || ref->track[i].format     != cand->track[i].format    ) { XXX XXX XXX
904         tc_log_error(__FILE__, "audio parameters mismatch");
905         dump_probeinfo(ref,  i, "old");
906         dump_probeinfo(cand, i, "new");
907         return 0;
908     }
909 
910     return 1;
911 }
912 
probe_from_vob(ProbeInfo * info,const vob_t * vob)913 static void probe_from_vob(ProbeInfo *info, const vob_t *vob)
914 {
915     /* copy only interesting fields */
916     if (info != NULL && vob != NULL) {
917         int i = 0;
918 
919         info->width    = vob->im_v_width;
920         info->height   = vob->im_v_height;
921         info->codec    = vob->v_codec_flag;
922         info->asr      = vob->im_asr;
923         info->frc      = vob->im_frc;
924 
925         for (i = 0; i < TC_MAX_AUD_TRACKS; i++) {
926             memset(&(info->track[i]), 0, sizeof(ProbeTrackInfo));
927         }
928         i = vob->a_track;
929 
930         info->track[i].samplerate = vob->a_rate;
931         info->track[i].chan       = vob->a_chan;
932         info->track[i].bits       = vob->a_bits;
933         info->track[i].format     = vob->a_codec_flag;
934     }
935 }
936 
937 /* ok, that sucks. I know. I can't do any better now. */
current_in_file(vob_t * vob,int kind)938 static const char *current_in_file(vob_t *vob, int kind)
939 {
940     if (kind == TC_VIDEO)
941     	return vob->video_in_file;
942     if (kind == TC_AUDIO)
943     	return vob->audio_in_file;
944     return NULL; /* cannot happen */
945 }
946 
947 #define RETURN_IF_PROBE_FAILED(ret, src) do {                        \
948     if (ret == 0) {                                                  \
949         tc_log_error(PACKAGE, "probing of source '%s' failed", src); \
950         status = TC_IM_THREAD_PROBE_ERROR;                           \
951     }                                                                \
952 } while (0)
953 
954 /* black magic in here? Am I looking for troubles? */
955 #define SWAP(type, a, b) do { \
956     type tmp = a;             \
957     a = b;                    \
958     b = tmp;                  \
959 } while (0)
960 
961 
962 /*************************************************************************/
963 
964 typedef struct tcmultiimportdata_ TCMultiImportData;
965 struct tcmultiimportdata_ {
966     int             kind;
967 
968     TCDecoderData   *decdata;
969     vob_t           *vob;
970 
971     ProbeInfo       infos;
972 
973     int             (*open)(vob_t *vob);
974     int             (*import_loop)(vob_t *vob);
975     int             (*close)(void);
976 
977     int             (*next)(vob_t *vob);
978 };
979 
980 
981 #define MULTIDATA_INIT(MEDIA, VOB, KIND) do {                           \
982     MEDIA ## _multidata.kind         = KIND;                          \
983                                                                       \
984     MEDIA ## _multidata.vob          = VOB;                           \
985     MEDIA ## _multidata.decdata      = &(MEDIA ## _decdata);          \
986                                                                       \
987     MEDIA ## _multidata.open         = tc_import_ ## MEDIA ## _open;  \
988     MEDIA ## _multidata.import_loop  = MEDIA ## _import_loop;         \
989     MEDIA ## _multidata.close        = tc_import_ ## MEDIA ## _close; \
990     MEDIA ## _multidata.next         = tc_next_ ## MEDIA ## _in_file; \
991 } while (0)
992 
993 #define MULTIDATA_FINI(MEDIA) do { \
994     ; /* nothing */ \
995 } while (0)
996 
997 
998 
999 static TCMultiImportData audio_multidata;
1000 static TCMultiImportData video_multidata;
1001 
1002 /*************************************************************************/
1003 
multi_import_thread(void * _sid)1004 static void *multi_import_thread(void *_sid)
1005 {
1006     static int status = TC_IM_THREAD_UNKNOWN; /* can't we be more specific? */
1007 
1008     TCMultiImportData *sid = _sid;
1009     int ret = TC_OK, track_id = sid->vob->a_track;
1010     ProbeInfo infos;
1011     ProbeInfo *old = &(sid->infos), *new = &infos;
1012     const char *fname = NULL;
1013     long int i = 1;
1014 
1015     while (tc_running() && tc_import_thread_is_active(sid->decdata)) {
1016         ret = sid->open(sid->vob);
1017         if (ret == TC_ERROR) {
1018             status = TC_IM_THREAD_EXT_ERROR;
1019             break;
1020         }
1021 
1022         status = sid->import_loop(sid->vob);
1023         /* source should always be closed */
1024 
1025         ret = sid->close();
1026         if (ret == TC_ERROR) {
1027             status = TC_IM_THREAD_EXT_ERROR;
1028             break;
1029         }
1030 
1031         ret = sid->next(sid->vob);
1032         if (ret == TC_ERROR) {
1033             status = TC_IM_THREAD_DONE;
1034             break;
1035         }
1036 
1037         fname = current_in_file(sid->vob, sid->kind);
1038         /* probing coherency check */
1039         ret = probe_im_stream(fname, new);
1040         RETURN_IF_PROBE_FAILED(ret, fname);
1041 
1042         if (probe_matches(old, new, track_id)) {
1043             if (verbose) {
1044                 tc_log_info(__FILE__, "switching to %s source #%li: %s",
1045                             sid->decdata->tag, i, fname);
1046             }
1047         } else {
1048             tc_log_error(PACKAGE, "source '%s' in directory"
1049                                   " not compatible with former", fname);
1050             status = TC_IM_THREAD_PROBE_ERROR;
1051             break;
1052         }
1053         /* now prepare for next probing round by swapping pointers */
1054         SWAP(ProbeInfo*, old, new);
1055 
1056         i++;
1057     }
1058     status = stop_cause(status);
1059 
1060     tc_framebuffer_interrupt();
1061 
1062     pthread_exit(&status);
1063 }
1064 
1065 /*************************************************************************/
1066 
tc_multi_import_threads_create(vob_t * vob)1067 void tc_multi_import_threads_create(vob_t *vob)
1068 {
1069     int ret;
1070 
1071     probe_from_vob(&(audio_multidata.infos), vob);
1072     MULTIDATA_INIT(audio, vob, TC_AUDIO);
1073     tc_import_thread_start(&audio_decdata);
1074     ret = pthread_create(&audio_decdata.thread_id, NULL,
1075                          multi_import_thread, &audio_multidata);
1076     if (ret != 0) {
1077         tc_error("failed to start sequential audio stream import thread");
1078     }
1079 
1080     probe_from_vob(&(video_multidata.infos), vob);
1081     MULTIDATA_INIT(video, vob, TC_VIDEO);
1082     tc_import_thread_start(&video_decdata);
1083     ret = pthread_create(&video_decdata.thread_id, NULL,
1084                          multi_import_thread, &video_multidata);
1085     if (ret != 0) {
1086         tc_error("failed to start sequential video stream import thread");
1087     }
1088 
1089     tc_info("sequential streams import threads started");
1090 }
1091 
1092 
tc_multi_import_threads_cancel(void)1093 void tc_multi_import_threads_cancel(void)
1094 {
1095     MULTIDATA_FINI(audio);
1096     MULTIDATA_FINI(video);
1097 
1098     tc_import_threads_cancel();
1099 }
1100 
1101 
1102 /*************************************************************************/
1103 
1104 /*
1105  * Local variables:
1106  *   c-file-style: "stroustrup"
1107  *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
1108  *   indent-tabs-mode: nil
1109  * End:
1110  *
1111  * vim: expandtab shiftwidth=4:
1112  */
1113 
1114