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