You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
534 lines
16 KiB
534 lines
16 KiB
/*
|
|
* tccat.c
|
|
*
|
|
* Copyright (C) Thomas Oestreich - June 2001
|
|
*
|
|
* This file is part of transcode, a video stream processing tool
|
|
*
|
|
* transcode is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* transcode is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include "transcode.h"
|
|
#include "tcinfo.h"
|
|
#include "libtc/libtc.h"
|
|
#include "libtc/iodir.h"
|
|
#include "libtc/xio.h"
|
|
#include "ioaux.h"
|
|
#include "tc.h"
|
|
#include "dvd_reader.h"
|
|
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_LIBDVDREAD
|
|
#ifdef HAVE_LIBDVDREAD_INC
|
|
#include <dvdread/dvd_reader.h>
|
|
#else
|
|
#include <dvd_reader.h>
|
|
#endif
|
|
#else
|
|
#include "dvd_reader.h"
|
|
#endif
|
|
|
|
#define EXE "tccat"
|
|
|
|
//static char buf[TC_BUF_MAX];
|
|
|
|
int verbose = TC_INFO;
|
|
|
|
void import_exit(int code)
|
|
{
|
|
if (verbose & TC_DEBUG) {
|
|
tc_log_msg(EXE, "(pid=%d) exit (code %d)", (int) getpid(), code);
|
|
}
|
|
exit(code);
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------
|
|
*
|
|
* source extract thread
|
|
*
|
|
* ------------------------------------------------------------*/
|
|
|
|
#define IO_BUF_SIZE 1024
|
|
#define DVD_VIDEO_LB_LEN 2048
|
|
|
|
static void tccat_thread(info_t *ipipe)
|
|
{
|
|
const char *name = NULL;
|
|
int found = 0, itype = TC_MAGIC_UNKNOWN, type = TC_MAGIC_UNKNOWN;
|
|
int verbose_flag = ipipe->verbose;
|
|
int vob_offset = ipipe->vob_offset;
|
|
info_t ipipe_avi;
|
|
TCDirList tcdir;
|
|
|
|
switch(ipipe->magic) {
|
|
case TC_MAGIC_DVD_PAL: /* fallthrough */
|
|
case TC_MAGIC_DVD_NTSC:
|
|
if (verbose_flag & TC_DEBUG) {
|
|
tc_log_msg(__FILE__, "%s", filetype(ipipe->magic));
|
|
}
|
|
dvd_read(ipipe->dvd_title, ipipe->dvd_chapter, ipipe->dvd_angle);
|
|
break;
|
|
|
|
case TC_MAGIC_TS:
|
|
ts_read(ipipe->fd_in, ipipe->fd_out, ipipe->ts_pid);
|
|
break;
|
|
|
|
case TC_MAGIC_RAW:
|
|
if (verbose_flag & TC_DEBUG) {
|
|
tc_log_msg(__FILE__, "%s", filetype(ipipe->magic));
|
|
}
|
|
if(vob_offset > 0) {
|
|
/* get filesize in units of packs (2kB) */
|
|
off_t off = lseek(ipipe->fd_in,
|
|
vob_offset * (off_t) DVD_VIDEO_LB_LEN,
|
|
SEEK_SET);
|
|
|
|
if (off != (vob_offset * (off_t)DVD_VIDEO_LB_LEN)) {
|
|
tc_log_warn(__FILE__, "unable to seek to block %d",
|
|
vob_offset); /* drop this chunk/file */
|
|
goto vob_skip2;
|
|
}
|
|
}
|
|
tc_preadwrite(ipipe->fd_in, ipipe->fd_out);
|
|
vob_skip2:
|
|
break;
|
|
|
|
case TC_MAGIC_DIR:
|
|
/* PASS 1: check file type - file order not important */
|
|
if (tc_dirlist_open(&tcdir, ipipe->name, 0) < 0) {
|
|
tc_log_error(__FILE__, "unable to open directory \"%s\"",
|
|
ipipe->name);
|
|
exit(1);
|
|
} else if (verbose_flag & TC_DEBUG) {
|
|
tc_log_msg(__FILE__, "scanning directory \"%s\"", ipipe->name);
|
|
}
|
|
|
|
while ((name = tc_dirlist_scan(&tcdir)) != NULL) {
|
|
if ((ipipe->fd_in = open(name, O_RDONLY)) < 0) {
|
|
tc_log_perror(__FILE__, "file open");
|
|
exit(1);
|
|
}
|
|
/*
|
|
* first valid magic must be the same for all
|
|
* files to follow
|
|
*/
|
|
itype = fileinfo(ipipe->fd_in, 0);
|
|
close(ipipe->fd_in);
|
|
|
|
if (itype == TC_MAGIC_UNKNOWN || itype == TC_MAGIC_PIPE
|
|
|| itype == TC_MAGIC_ERROR) {
|
|
tc_log_error(__FILE__, "this version of transcode"
|
|
" supports only");
|
|
tc_log_error(__FILE__, "directories containing files of"
|
|
" identical file type.");
|
|
tc_log_error(__FILE__, "Please clean up directory %s and"
|
|
" restart.", ipipe->name);
|
|
|
|
tc_log_error(__FILE__, "file %s with filetype %s is invalid"
|
|
" for directory mode.",
|
|
name, filetype(itype));
|
|
|
|
exit(1);
|
|
} /* bad magic */
|
|
|
|
switch(itype) { /* supported file types, global fallthrough */
|
|
case TC_MAGIC_VOB:
|
|
case TC_MAGIC_DV_PAL:
|
|
case TC_MAGIC_DV_NTSC:
|
|
case TC_MAGIC_AC3:
|
|
case TC_MAGIC_YUV4MPEG:
|
|
case TC_MAGIC_AVI:
|
|
case TC_MAGIC_MPEG:
|
|
if (!found) { /* very first time only */
|
|
type = itype;
|
|
}
|
|
if (itype!=type) {
|
|
tc_log_error(__FILE__,"multiple filetypes not valid for"
|
|
" directory mode.");
|
|
exit(1);
|
|
}
|
|
found = 1;
|
|
break;
|
|
default:
|
|
tc_log_error(__FILE__, "invalid filetype %s for directory"
|
|
" mode.", filetype(type));
|
|
exit(1);
|
|
} /* check itype */
|
|
} /* process files */
|
|
tc_dirlist_close(&tcdir);
|
|
|
|
if (!found) {
|
|
tc_log_error(__FILE__, "no valid files found in %s", name);
|
|
exit(1);
|
|
} else if (verbose_flag & TC_DEBUG) {
|
|
tc_log_msg(__FILE__, "%s", filetype(type));
|
|
}
|
|
|
|
/* PASS 2: dump files in correct order */
|
|
if (tc_dirlist_open(&tcdir, ipipe->name, 1) < 0) {
|
|
tc_log_error(__FILE__, "unable to sort directory entries\"%s\"",
|
|
name);
|
|
exit(1);
|
|
}
|
|
|
|
while ((name = tc_dirlist_scan(&tcdir)) != NULL) {
|
|
if ((ipipe->fd_in = open(name, O_RDONLY)) < 0) {
|
|
tc_log_perror(__FILE__, "file open");
|
|
exit(1);
|
|
} else if(verbose_flag & TC_STATS) {
|
|
tc_log_msg(__FILE__, "processing %s", name);
|
|
}
|
|
|
|
/* type determined in pass 1 */
|
|
switch (type) {
|
|
case TC_MAGIC_VOB:
|
|
if (vob_offset > 0) {
|
|
/* get filesize in units of packs (2kB) */
|
|
off_t off;
|
|
off_t size = lseek(ipipe->fd_in, 0, SEEK_END);
|
|
lseek(ipipe->fd_in, 0, SEEK_SET);
|
|
|
|
if (size > vob_offset * (off_t)DVD_VIDEO_LB_LEN) {
|
|
/* offset within current file */
|
|
off = lseek(ipipe->fd_in,
|
|
vob_offset * (off_t)DVD_VIDEO_LB_LEN,
|
|
SEEK_SET);
|
|
vob_offset = 0;
|
|
} else {
|
|
vob_offset -= size/DVD_VIDEO_LB_LEN;
|
|
goto vob_skip;
|
|
}
|
|
}
|
|
tc_preadwrite(ipipe->fd_in, ipipe->fd_out);
|
|
vob_skip:
|
|
break;
|
|
/*
|
|
* all following (fallthrough stream typeas are all handlead in
|
|
* the same way since they can be viewd as concatenation of
|
|
* frames (no header or other things
|
|
*/
|
|
case TC_MAGIC_DV_PAL: /* fallthrough */
|
|
case TC_MAGIC_DV_NTSC: /* fallthrough */
|
|
case TC_MAGIC_AC3: /* fallthrough */
|
|
case TC_MAGIC_YUV4MPEG: /* fallthrough */
|
|
case TC_MAGIC_MPEG: /* fallthrough */
|
|
tc_preadwrite(ipipe->fd_in, ipipe->fd_out);
|
|
break;
|
|
|
|
case TC_MAGIC_AVI:
|
|
/* extract and concatenate streams. avilib must do it. */
|
|
ac_memcpy(&ipipe_avi, ipipe, sizeof(info_t));
|
|
ipipe_avi.name = (char *)name; /* real AVI file name */
|
|
ipipe_avi.magic = TC_MAGIC_AVI;
|
|
extract_avi(&ipipe_avi);
|
|
break;
|
|
|
|
default:
|
|
tc_log_error(__FILE__, "invalid filetype %s for directory"
|
|
" mode.", filetype(type));
|
|
exit(1);
|
|
}
|
|
close(ipipe->fd_in);
|
|
} /* process files */
|
|
tc_dirlist_close(&tcdir);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void version(void)
|
|
{
|
|
/* XXX why not plain old printf? */
|
|
tc_log_msg(EXE, "(%s v%s) (C) 2001-2003 Thomas Oestreich,"
|
|
" 2003-2010 Transcode Team",
|
|
PACKAGE, VERSION);
|
|
}
|
|
|
|
|
|
static void usage(int status)
|
|
{
|
|
/* XXX why not plain old printf? */
|
|
version();
|
|
|
|
fprintf(stderr,"\nUsage: %s [options]\n", EXE);
|
|
fprintf(stderr," -i name input file/directory%s name\n",
|
|
#ifdef HAVE_LIBDVDREAD
|
|
"/device/mountpoint"
|
|
#else
|
|
""
|
|
#endif
|
|
);
|
|
fprintf(stderr," -t magic file type [autodetect]\n");
|
|
#ifdef HAVE_LIBDVDREAD
|
|
fprintf(stderr," -T t[,c[-d][,a]] DVD title[,chapter(s)[,angle]]"
|
|
" [1,1,1]\n");
|
|
fprintf(stderr," -L process all following chapters"
|
|
" [off]\n");
|
|
#endif
|
|
fprintf(stderr," -S n seek to VOB stream offset nx2kB"
|
|
" [0]\n");
|
|
fprintf(stderr," -P stream DVD ( needs -T )\n");
|
|
fprintf(stderr," -a dump AVI-file/socket audio"
|
|
" stream\n");
|
|
fprintf(stderr," -n id transport stream id [0x10]\n");
|
|
fprintf(stderr," -d mode verbosity mode\n");
|
|
fprintf(stderr," -v print version\n");
|
|
|
|
exit(status);
|
|
}
|
|
|
|
typedef enum {
|
|
TCCAT_SOURCE_STDIN = 1,
|
|
TCCAT_SOURCE_FILE,
|
|
TCCAT_SOURCE_DVD,
|
|
TCCAT_SOURCE_DIR,
|
|
TCCAT_SOURCE_TS
|
|
} TCCatSources;
|
|
|
|
/* ------------------------------------------------------------
|
|
* universal extract frontend
|
|
* ------------------------------------------------------------*/
|
|
|
|
#define VALIDATE_OPTION \
|
|
if (optarg[0] == '-') usage(EXIT_FAILURE)
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct stat fbuf;
|
|
info_t ipipe;
|
|
int end_chapter, start_chapter;
|
|
int title = 1, chapter1 = 1, chapter2 = -1, angle = 1;
|
|
int max_chapters, max_angles, max_titles;
|
|
int n = 0, j, stream = 0, audio = 0, user = 0, source = 0;
|
|
|
|
int vob_offset = 0;
|
|
int ch, ts_pid = 0x10;
|
|
char *magic="", *name=NULL;
|
|
|
|
/* proper initialization */
|
|
memset(&ipipe, 0, sizeof(info_t));
|
|
|
|
libtc_init(&argc, &argv);
|
|
|
|
while ((ch = getopt(argc, argv, "S:T:d:i:vt:LaP?hn:")) != -1) {
|
|
switch (ch) {
|
|
case 'i':
|
|
VALIDATE_OPTION;
|
|
name = optarg;
|
|
break;
|
|
|
|
case 'T':
|
|
VALIDATE_OPTION;
|
|
n = sscanf(optarg,"%d,%d-%d,%d", &title, &chapter1, &chapter2, &angle);
|
|
if (n != 4) {
|
|
n = sscanf(optarg,"%d,%d-%d", &title, &chapter1, &chapter2);
|
|
if (n != 3) {
|
|
n = sscanf(optarg,"%d,%d,%d", &title, &chapter1, &angle);
|
|
/* only do one chapter ! */
|
|
chapter2 = chapter1;
|
|
|
|
if (n < 0 || n > 3) {
|
|
tc_log_error(EXE, "invalid parameter for option -T");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
source = TCCAT_SOURCE_DVD;
|
|
|
|
if (chapter2 != -1) {
|
|
if (chapter2 < chapter1) {
|
|
tc_log_error(EXE, "invalid parameter for option -T");
|
|
exit(1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'P':
|
|
stream = 1;
|
|
break;
|
|
|
|
case 'a':
|
|
audio = 1;
|
|
break;
|
|
|
|
case 'd':
|
|
VALIDATE_OPTION;
|
|
verbose = atoi(optarg);
|
|
break;
|
|
|
|
case 'n':
|
|
VALIDATE_OPTION;
|
|
ts_pid = strtol(optarg, NULL, 16);
|
|
source = TCCAT_SOURCE_TS;
|
|
break;
|
|
|
|
case 'S':
|
|
VALIDATE_OPTION;
|
|
vob_offset = atoi(optarg);
|
|
break;
|
|
|
|
case 't':
|
|
VALIDATE_OPTION;
|
|
magic = optarg;
|
|
user = 1;
|
|
|
|
if (strcmp(magic, "dvd") == 0) {
|
|
source = TCCAT_SOURCE_DVD;
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
version();
|
|
exit(0);
|
|
break;
|
|
|
|
case 'h':
|
|
usage(EXIT_SUCCESS);
|
|
default:
|
|
usage(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* DVD debugging information */
|
|
if ((verbose & TC_DEBUG) && source == TCCAT_SOURCE_DVD) {
|
|
tc_log_msg(EXE, "T=%d %d %d %d %d", n, title, chapter1,
|
|
chapter2, angle);
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
* fill out defaults for info structure
|
|
* ------------------------------------------------------------*/
|
|
|
|
/* no autodetection yet */
|
|
if (argc == 1) {
|
|
usage(EXIT_FAILURE);
|
|
}
|
|
|
|
/* assume defaults */
|
|
if (name == NULL) {
|
|
source = TCCAT_SOURCE_STDIN;
|
|
ipipe.fd_in = STDIN_FILENO;
|
|
}
|
|
|
|
/* no stdin for DVD */
|
|
if (name == NULL && source == TCCAT_SOURCE_DVD) {
|
|
tc_log_error(EXE, "invalid directory/path_to_device");
|
|
usage(EXIT_FAILURE);
|
|
}
|
|
|
|
/* do not try to mess with the stdin stream */
|
|
if ((source != TCCAT_SOURCE_DVD) && (source != TCCAT_SOURCE_TS)
|
|
&& (source != TCCAT_SOURCE_STDIN)) {
|
|
/* file or directory? */
|
|
if (stat(name, &fbuf)) {
|
|
tc_log_error(EXE, "invalid file \"%s\"", name);
|
|
exit(1);
|
|
}
|
|
|
|
source = (S_ISDIR(fbuf.st_mode))
|
|
?TCCAT_SOURCE_DIR :TCCAT_SOURCE_FILE;
|
|
}
|
|
|
|
/* fill out defaults for info structure */
|
|
ipipe.fd_out = STDOUT_FILENO;
|
|
ipipe.verbose = verbose;
|
|
ipipe.dvd_title = title;
|
|
ipipe.dvd_chapter = chapter1;
|
|
ipipe.dvd_angle = angle;
|
|
ipipe.ts_pid = ts_pid;
|
|
ipipe.vob_offset = vob_offset;
|
|
|
|
if (name) {
|
|
ipipe.name = tc_strdup(name);
|
|
if (ipipe.name == NULL) {
|
|
tc_log_error(EXE, "could not allocate memory");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
ipipe.name = NULL;
|
|
}
|
|
|
|
ipipe.select = audio;
|
|
|
|
/* ------------------------------------------------------------
|
|
* source specific section
|
|
* ------------------------------------------------------------*/
|
|
|
|
switch(source) {
|
|
case TCCAT_SOURCE_TS:
|
|
ipipe.fd_in = xio_open(name, O_RDONLY);
|
|
if (ipipe.fd_in < 0) {
|
|
tc_log_perror(EXE, "file open");
|
|
exit(1);
|
|
}
|
|
ipipe.magic = TC_MAGIC_TS;
|
|
tccat_thread(&ipipe);
|
|
xio_close(ipipe.fd_in);
|
|
break;
|
|
|
|
case TCCAT_SOURCE_DVD:
|
|
if (dvd_init(name, &max_titles, verbose) < 0) {
|
|
tc_log_error(EXE, "(pid=%d) failed to open DVD %s", getpid(), name);
|
|
exit(1);
|
|
}
|
|
ipipe.magic = TC_MAGIC_DVD_PAL; /* FIXME */
|
|
dvd_query(title, &max_chapters, &max_angles);
|
|
/* set chapternumbers now we know how much there are */
|
|
start_chapter = (chapter1!=-1 && chapter1 <=max_chapters) ? chapter1:1;
|
|
end_chapter = (chapter2!=-1 && chapter2 <=max_chapters) ? chapter2:max_chapters;
|
|
|
|
for (j = start_chapter; j < end_chapter+1; j++) {
|
|
ipipe.dvd_chapter = j;
|
|
if (verbose & TC_DEBUG) {
|
|
tc_log_msg(EXE, "(pid=%d) processing chapter (%d/%d)",
|
|
getpid(), j, max_chapters);
|
|
}
|
|
if (stream) {
|
|
dvd_stream(title, j);
|
|
} else {
|
|
tccat_thread(&ipipe);
|
|
}
|
|
}
|
|
dvd_close();
|
|
break;
|
|
|
|
case TCCAT_SOURCE_FILE:
|
|
ipipe.fd_in = open(name, O_RDONLY);
|
|
if (ipipe.fd_in < 0) {
|
|
tc_log_perror(EXE, "file open");
|
|
exit(1);
|
|
}
|
|
|
|
case TCCAT_SOURCE_STDIN:
|
|
ipipe.magic = TC_MAGIC_RAW;
|
|
tccat_thread(&ipipe);
|
|
if (ipipe.fd_in != STDIN_FILENO) {
|
|
close(ipipe.fd_in);
|
|
}
|
|
break;
|
|
|
|
case TCCAT_SOURCE_DIR:
|
|
ipipe.magic = TC_MAGIC_DIR;
|
|
tccat_thread(&ipipe);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#include "libtc/static_xio.h"
|