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.

537 lines
15 KiB

/*
* tcmodchain.c -- simple module system explorer frontend
* (C) 2006-2010 - Francesco Romani <fromani at gmail dot com>
*
* 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 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <glob.h>
#include "tcstub.h"
#define EXE "tcmodchain"
/*************************************************************************/
void version(void)
{
printf("%s v%s (C) 2006-2010 Transcode Team\n", EXE, VERSION);
}
enum {
STATUS_DONE = -1, /* used internally */
STATUS_OK = 0,
STATUS_BAD_PARAM,
STATUS_MODULE_ERROR,
STATUS_MODULE_MISMATCH,
STATUS_GLOB_FAILED,
};
#define MAX_MODS (16)
typedef struct modrequest_ ModRequest;
struct modrequest_ {
char **rawdata; /* main reference */
const char *type; /* commodity */
const char *name; /* commodity */
TCModule module;
};
static void modrequest_init(ModRequest *modr);
static int modrequest_load(TCFactory factory,
ModRequest *modr, const char *str);
static int modrequest_scan(const char *modpath, const char *modstr,
glob_t *globbuf);
static int modrequest_fill(TCFactory factory, ModRequest *mods,
size_t maxmods, size_t *modnum, glob_t *globbuf);
#define modrequest_free(GLOBBUF) globfree((GLOBBUF))
static int modrequest_unload(TCFactory factory, ModRequest *modr);
/*************************************************************************/
enum {
TC_MODULE_DEMUXER = 1, /* future */
TC_MODULE_DECODER = 2, /* future */
TC_MODULE_FILTER = 4, /* future */
TC_MODULE_ENCODER = 8,
TC_MODULE_MUXER = 16,
TC_MODULE_FIXED = 1024,
TC_MODULE_TEMPLATE = 2048,
};
static uint32_t parse_modstr(const char *str)
{
uint32_t ret = 0;
if (str != NULL && strlen(str) > 0) {
struct {
const char *name;
int kind;
size_t off;
} tags[] = {
{ "encode:", TC_MODULE_ENCODER, strlen("encode:") },
{ "multiplex:", TC_MODULE_MUXER, strlen("multiplex:") },
{ NULL, 0, 0 },
};
int i = 0;
for (i = 0; tags[i].name != NULL; i++) {
if (strncmp(str, tags[i].name, tags[i].off) == 0) {
ret |= tags[i].kind;
str += tags[i].off;
break;
}
}
/* found something supported/making sense */
if (ret != 0 && (str != NULL && strlen(str) > 0)) {
if (!strcmp(str, "*")) {
ret |= TC_MODULE_TEMPLATE;
} else {
ret |= TC_MODULE_FIXED;
}
}
}
return ret;
}
static int parse_path(const char *fullpath, char *modstr, size_t buflen)
{
const char *pc = NULL;
char *pr = NULL;
if (!fullpath || !modstr || (buflen < TC_BUF_MIN)) {
return TC_ERROR;
}
pc = strrchr(fullpath, '/');
if (pc == NULL || strlen(pc + 1) > buflen) {
return TC_ERROR;
}
strlcpy(modstr, pc + 1, buflen);
pr = strrchr(modstr, '.');
if (pr == NULL) {
return TC_ERROR;
}
*pr = '\0';
pr = strchr(modstr, '_');
if (pr == NULL) {
return TC_ERROR;
}
*pr = ':';
return TC_OK;
}
/*************************************************************************/
static void modrequest_init(ModRequest *modr)
{
if (modr != NULL) {
modr->rawdata = NULL;
modr->type = NULL;
modr->name = NULL;
modr->module = NULL;
}
}
static int modrequest_load(TCFactory factory,
ModRequest *modr, const char *str)
{
size_t pieces = 0;
if (factory == NULL || modr == NULL || str == NULL) {
tc_log_warn(EXE, "wrong parameters for modrequest_load");
return TC_ERROR;
}
modr->rawdata = tc_strsplit(str, ':', &pieces);
if (modr->rawdata == NULL || pieces != 2) {
tc_log_warn(EXE, "malformed module string: %s", str);
return TC_ERROR;
}
modr->type = modr->rawdata[0];
modr->name = modr->rawdata[1];
modr->module = tc_new_module(factory, modr->type, modr->name, TC_NONE);
if (modr->module == NULL) {
tc_log_warn(EXE, "failed creation of module: %s", str);
return TC_ERROR;
}
return TC_OK;
}
static int modrequest_unload(TCFactory factory, ModRequest *modr)
{
if (factory == NULL || modr == NULL) {
tc_log_warn(EXE, "wrong parameters for modrequest_load");
return TC_ERROR;
}
tc_del_module(factory, modr->module);
tc_strfreev(modr->rawdata);
/* re-blank fields */
modrequest_init(modr);
return TC_OK;
}
static int modrequest_scan(const char *modpath, const char *modstr,
glob_t *globbuf)
{
char path_model[PATH_MAX];
char buf[TC_BUF_MIN];
const char *pc = NULL;
int err = 0;
pc = strchr(modstr, ':');
if (pc == NULL) {
return 1;
}
if ((pc - modstr + 1) > sizeof(buf)) {
return 2; /* XXX watch out here */
}
strlcpy(buf, modstr, pc - modstr + 1);
tc_snprintf(path_model, sizeof(path_model), "%s/%s_*.so", modpath, buf);
err = glob(path_model, GLOB_ERR, NULL, globbuf);
if (err) {
tc_log_error(EXE, "error while scanning for modules: %s",
(err == GLOB_NOSPACE) ?"can't get enough memory" :
(err == GLOB_ABORTED) ?"read error" :
/* GLOB_NOMATCH */ "no modules found");
return -1;
}
return 0;
}
static int modrequest_fill(TCFactory factory, ModRequest *mods,
size_t maxmods, size_t *modnum, glob_t *globbuf)
{
char modstr[TC_BUF_MIN];
int i = 0, count = 0, lim = 0;
if (!factory || !mods || !globbuf) {
return TC_ERROR;
}
if (maxmods < globbuf->gl_pathc) {
tc_log_warn(EXE, "found %u candidate modules, but "
"only %u allowed (dropping remaining)",
(unsigned)globbuf->gl_pathc, (unsigned)maxmods);
}
lim = TC_MIN(maxmods, globbuf->gl_pathc);
for (i = 0; i < lim; i++) {
int ret;
ret = parse_path(globbuf->gl_pathv[i], modstr, TC_BUF_MIN);
if (ret != 0) {
tc_log_warn(EXE, "error while parsing '%s', skipping",
globbuf->gl_pathv[i]);
continue;
}
ret = modrequest_load(factory, &mods[i], modstr);
if (ret != 0) {
tc_log_warn(EXE, "error while loading '%s', skipping",
modstr);
continue;
}
count++;
}
if (modnum != NULL) {
*modnum += count;
}
return TC_OK;
}
/*************************************************************************/
typedef struct cmdletdata_ CmdLetData;
struct cmdletdata_ {
ModRequest mods[MAX_MODS];
size_t modsnum;
const char *modpath;
TCFactory factory;
};
typedef int (*CmdLet)(CmdLetData *cdata, int argc, char **argv);
#define CLEANUP(CDATA) do { \
int i = 0; \
for (i = 0; i < (CDATA)->modsnum; i++) { \
modrequest_unload((CDATA)->factory, &((CDATA)->mods[i])); \
} \
(CDATA)->modsnum = 0; \
} while (0)
/* XXX */
static int check_module_pair(const ModRequest *head,
const ModRequest *tail,
const ModRequest *ref,
int verbose)
{
int ret = 0;
if (head->module == NULL || tail->module == NULL) {
tc_log_error(EXE, "check_module_pair: missing module handle");
return -1;
}
ret = tc_module_info_match(TC_CODEC_ANY,
tc_module_get_info(head->module),
tc_module_get_info(tail->module));
if (verbose >= TC_DEBUG) {
tc_log_info(EXE, "%s:%s | %s:%s [%s]",
head->type, head->name, tail->type, tail->name,
(ret == 1) ?"OK" :"MISMATCH");
} else if (verbose >= TC_INFO) {
if (ret == 1) {
printf("%s\n", ref->name);
}
}
return ret;
}
static int cmdlet_usage(CmdLetData *unused, int ac, char **av)
{
version();
printf("Usage: %s [options] module [module... [module...]]\n",
EXE);
printf(" -L list mode (see manpage for details)\n");
printf(" -C check mode (see manpage for details)\n");
printf(" -d verbosity verbosity mode [1 == TC_INFO]\n");
printf(" -m PATH use PATH as module path\n");
printf(" -v show program version and exit\n");
printf(" -h show this help message\n");
return STATUS_OK;
}
static int cmdlet_check(CmdLetData *cdata, int ac, char **av)
{
int i = 0, matches = 0;
int status = STATUS_OK; /* let's be optimisc, once in lifetime */
if (ac < 2) {
tc_log_error(EXE, "not enough arguments for `check' mode");
return STATUS_BAD_PARAM;
}
for (i = 0; i < ac; i++) {
modrequest_load(cdata->factory, &cdata->mods[cdata->modsnum], av[i]);
cdata->modsnum++;
}
status = STATUS_OK;
if (cdata->modsnum >= 2) {
/* N modules, so N - 1 interfaces */
for (i = 0; i < cdata->modsnum - 1; i++) {
int ret = check_module_pair(&cdata->mods[i], &cdata->mods[i + 1],
&cdata->mods[i],
(verbose >= TC_INFO) ?TC_DEBUG :TC_QUIET);
if (ret != -1) { /* no error */
matches += ret;
}
}
if (matches < cdata->modsnum - 1) {
status = STATUS_MODULE_MISMATCH;
}
}
if (verbose) {
if (status == STATUS_OK) {
tc_log_info(EXE, "module chain OK");
} else {
tc_log_info(EXE, "module chain ILLEGAL");
}
}
CLEANUP(cdata);
return status;
}
static int cmdlet_list(CmdLetData *cdata, int ac, char **av)
{
glob_t globbuf;
int ret, i = 0, fid = 0 /* fixed id */, tid = 0; /* template id */
uint32_t modkind[2] = { 0, 0 };
ModRequest fixed;
if (ac != 2) {
tc_log_error(EXE, "wrong number of arguments for `list' mode");
return STATUS_BAD_PARAM;
}
/* we support only encoder|multiplexor, yet */
modkind[0] = parse_modstr(av[0]);
if (!(modkind[0] & TC_MODULE_ENCODER)) {
tc_log_error(EXE, "unknown/unsupported module '%s'", av[0]);
return STATUS_BAD_PARAM;
}
modkind[1] = parse_modstr(av[1]);
if (!(modkind[1] & TC_MODULE_MUXER)) {
tc_log_error(EXE, "unknown/unsupported module '%s'", av[1]);
return STATUS_BAD_PARAM;
}
if ((modkind[0] & TC_MODULE_FIXED)
&& (modkind[1] & TC_MODULE_TEMPLATE)) {
fid = 0;
tid = 1;
} else if ((modkind[0] & TC_MODULE_TEMPLATE)
&& (modkind[1] & TC_MODULE_FIXED)) {
fid = 1;
tid = 0;
} else {
tc_log_error(EXE, "incorrect arguments,"
" maybe you want to use `check' mode?");
return STATUS_BAD_PARAM;
}
modrequest_init(&fixed);
ret = modrequest_load(cdata->factory, &fixed, av[fid]);
if (ret != TC_OK) {
return STATUS_MODULE_ERROR;
}
ret = modrequest_scan(cdata->modpath, av[tid], &globbuf);
if (ret != 0) {
return STATUS_GLOB_FAILED;
}
ret = modrequest_fill(cdata->factory, cdata->mods, MAX_MODS,
&cdata->modsnum, &globbuf);
if (ret != TC_OK) {
return STATUS_MODULE_ERROR;
}
for (i = 0; i < cdata->modsnum; i++) {
const ModRequest *H = (tid == 0) ?(&cdata->mods[i]) :(&fixed);
const ModRequest *T = (tid == 1) ?(&cdata->mods[i]) :(&fixed);
check_module_pair(H, T, &(cdata->mods[i]),
(verbose == 0) ?TC_INFO :verbose);
}
CLEANUP(cdata);
modrequest_free(&globbuf);
ret = modrequest_unload(cdata->factory, &fixed);
if (ret != TC_OK) {
return STATUS_MODULE_ERROR;
}
return STATUS_OK;
}
/*************************************************************************/
int main(int argc, char *argv[])
{
/* needed by filter modules */
TCVHandle tcv_handle = tcv_init();
CmdLet cmdlet = cmdlet_usage;
int ch, ret, status, i = 0;
CmdLetData cdata = {
.modpath = MOD_PATH,
.factory = NULL,
.modsnum = 0,
};
ac_init(AC_ALL);
tc_set_config_dir(NULL);
libtc_init(&argc, &argv);
filter[0].id = 0; /* to make gcc happy */
for (i = 0; i < MAX_MODS; i++) {
modrequest_init(&cdata.mods[i]);
}
while (1) {
ch = getopt(argc, argv, "LCd:?vhm:");
if (ch == -1) {
break;
}
switch (ch) {
case 'L':
cmdlet = cmdlet_list;
break;
case 'C':
cmdlet = cmdlet_check;
break;
case 'd':
if (optarg[0] == '-') {
cmdlet_usage(&cdata, argc, argv);
return STATUS_BAD_PARAM;
}
verbose = atoi(optarg);
break;
case 'm':
cdata.modpath = optarg;
break;
case 'v':
version();
return STATUS_OK;
case '?': /* fallthrough */
case 'h': /* fallthrough */
default:
cmdlet_usage(&cdata, argc, argv);
return STATUS_OK;
}
}
/* XXX: watch out here */
argc -= optind;
argv += optind;
/*
* we can't distinguish from OMS and NMS modules at glance, so try
* first using new module system
*/
cdata.factory = tc_new_module_factory(cdata.modpath, verbose);
status = cmdlet(&cdata, argc, argv);
ret = tc_del_module_factory(cdata.factory); /* XXX: unchecked */
tcv_free(tcv_handle);
return status;
}
/*************************************************************************/
/*
* Local variables:
* c-file-style: "stroustrup"
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/