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.

608 lines
19 KiB

/*
* pvm_parser.c
*
* Copyright (C) Malanchini Marzio - August 2003
* Updates and port to new libtc configuration file parser:
* (C) 2007 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, 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.
*
*/
/*
* OK, I must say that I'm not very proud of my work done here.
* I think I'll must improve it soon.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "libtc/libtc.h"
#include "libtc/tclist.h"
#include "libtc/cfgfile.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pvm_parser.h>
#include <pvm_version.h>
/*************************************************************************/
/* helpers */
#define LOCALHOST "."
/* XXX ?! sic -- Fromani */
static char *pvm_hostname(char *candidate)
{
if (candidate) {
tc_strstrip(candidate);
if (strlen(candidate) > 0) { // XXX enlarge check value
return candidate;
}
}
return LOCALHOST;
}
/* commodity */
typedef char PVMString[TC_BUF_MIN];
typedef struct pvmnodedata_ PVMNodeData;
struct pvmnodedata_ {
PVMString hostname;
int enabled; /* flag */
int maxprocs;
};
/*************************************************************************/
/* main data structure */
static pvm_config_env s_pvm_conf;
/*************************************************************************/
/* forward declarations of dispatchers */
static pvm_config_filelist *dispatch_list(TCList *src, int type,
char *codec, char *destination,
pvm_config_filelist **ret);
static int dispatch_node(int id, PVMNodeData *data,
pvm_config_env *env);
static int dispatch_null(int id, pvm_config_env *env);
static int dispatch_merger(int id, pvm_config_env *env);
static int dispatch_modules(int id, pvm_config_env *env);
static int dispatch_syslist(int id, pvm_config_env *env);
/*************************************************************************/
/*
* since configuration uses a lot of strings, we need a lot of temporary
* buffers to store them
*/
/* Nodes */
static int nodes_num = 1; /* this is pretty ugly, isn't it? */
static PVMNodeData nodes_data[PVM_MAX_NODES];
/* SystemMerger */
static PVMString systemmerger_hostname;
static PVMString systemmerger_mplexparams;
/* AudioMerger */
static PVMString audiomerger_hostname;
/* VideoMerger */
static PVMString videomerger_hostname;
/* ExportAudioModule */
static PVMString exportaudiomod_codec;
static PVMString exportaudiomod_params[PVM_MAX_CODEC_PARAMS];
/* ExportVideoModule */
static PVMString exportvideomod_codec;
static PVMString exportvideomod_params[PVM_MAX_CODEC_PARAMS];
/* SystemList */
static PVMString systemlist_codec;
static PVMString systemlist_destination;
static PVMString systemlist_mplexparams;
/* AddAudio */
static PVMString addaudio_codec;
static PVMString addaudio_destination;
/* AddVideo */
static PVMString addvideo_codec;
static PVMString addvideo_destination;
/*************************************************************************/
#define NODEINIT(ID) \
{ { "Hostname", (nodes_data[ID].hostname), TCCONF_TYPE_STRING, 0, 0, 0 }, \
{ "NumProcMax", &(nodes_data[ID].maxprocs), TCCONF_TYPE_INT, \
0, 1, PVM_MAX_NODE_PROCS }, \
{ "Enabled", &(nodes_data[ID].enabled), TCCONF_TYPE_FLAG, 0, 0, 1 }, \
{ NULL, NULL, 0, 0, 0, 0, } }
static TCConfigEntry node_conf[PVM_MAX_NODES][4] = {
NODEINIT(0),
NODEINIT(1),
NODEINIT(2),
NODEINIT(3),
NODEINIT(4),
NODEINIT(5),
NODEINIT(6),
NODEINIT(7),
};
#undef NODEINIT
static TCConfigEntry pvmhostcaps_conf[] = {
{ "NumProcMaxForHost", &(s_pvm_conf.s_nproc),
TCCONF_TYPE_INT, 0, 1, PVM_NUM_NODE_PROCS },
{ "MaxProcForCluster", &(s_pvm_conf.s_max_proc),
TCCONF_TYPE_INT, 0, 1, PVM_MAX_CLUSTER_PROCS },
{ "NumElabFrameForTask", &(s_pvm_conf.s_num_frame_task),
TCCONF_TYPE_INT, 0, 1, PVM_NUM_TASK_FRAMES },
{ "InternalMultipass", &(s_pvm_conf.s_internal_multipass),
TCCONF_TYPE_FLAG, 0, 0, 1 },
{ "Nodes", &nodes_num, TCCONF_TYPE_INT, 0, 1, PVM_MAX_NODES },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry videomerger_conf[] = {
{ "Hostname", videomerger_hostname, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "BuildOnlyBatchMergeList",
&(s_pvm_conf.s_video_merger.s_build_only_list),
TCCONF_TYPE_FLAG, 0, 0, 1 },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry audiomerger_conf[] = {
{ "Hostname", audiomerger_hostname, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "BuildOnlyBatchMergeList",
&(s_pvm_conf.s_audio_merger.s_build_only_list),
TCCONF_TYPE_FLAG, 0, 0, 1 },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry systemmerger_conf[] = {
{ "Hostname", systemmerger_hostname, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "BuildOnlyBatchMergeList",
&(s_pvm_conf.s_system_merger.s_build_only_list),
TCCONF_TYPE_FLAG, 0, 0, 1 },
{ "MultiplexParams", systemmerger_mplexparams,
TCCONF_TYPE_STRING, 0, 0, },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry exportaudiomod_conf[] = {
{ "Codec", exportaudiomod_codec, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Param1", exportaudiomod_params[0], TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Param2", exportaudiomod_params[1], TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Param3", exportaudiomod_params[2], TCCONF_TYPE_STRING, 0, 0, 0 },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry exportvideomod_conf[] = {
{ "Codec", exportvideomod_codec, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Param1", exportvideomod_params[0], TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Param2", exportvideomod_params[1], TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Param3", exportvideomod_params[2], TCCONF_TYPE_STRING, 0, 0, 0 },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry systemlist_conf[] = {
{ "Destination", systemlist_destination, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Codec", systemlist_codec, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "BuildOnlyBatchMergeList",
&(s_pvm_conf.s_build_intermed_file),
TCCONF_TYPE_FLAG, 0, 0, 1 },
{ "MultiplexParams", systemlist_mplexparams,
TCCONF_TYPE_STRING, 0, 0, },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry addaudio_conf[] = {
{ "Destination", addaudio_destination, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Codec", addaudio_codec, TCCONF_TYPE_STRING, 0, 0, 0 },
{ NULL, NULL, 0, 0, 0, 0, },
};
static TCConfigEntry addvideo_conf[] = {
{ "Destination", addvideo_destination, TCCONF_TYPE_STRING, 0, 0, 0 },
{ "Codec", addvideo_codec, TCCONF_TYPE_STRING, 0, 0, 0 },
{ NULL, NULL, 0, 0, 0, 0, },
};
/*************************************************************************/
typedef struct pvm_conf_item_ PVMConfItem;
struct pvm_conf_item_ {
const char *name;
TCConfigEntry *conf;
int (*dispatch)(int id, pvm_config_env *env);
int serverside; /* flag */
int parsed;
};
/* BIGFAT WARNING: please take a great care to keep in sync */
enum config_idx {
CONF_PVM_HOST_CAPS_IDX = 0,
CONF_AUDIO_MERGER_IDX,
CONF_VIDEO_MERGER_IDX,
CONF_SYSTEM_MERGER_IDX,
CONF_EXPORT_AUDIO_MOD_IDX,
CONF_EXPORT_VIDEO_MOD_IDX,
CONF_SYSTEM_LIST_IDX,
CONF_ADD_AUDIO_IDX,
CONF_ADD_VIDEO_IDX,
};
static PVMConfItem pvm_config[] = {
{ "PvmHostCapability", pvmhostcaps_conf, dispatch_null, 0, 0, },
{ "AudioMerger", audiomerger_conf, dispatch_merger, 0, 0, },
{ "VideoMerger", videomerger_conf, dispatch_merger, 0, 0, },
{ "SystemMerger", systemmerger_conf, dispatch_merger, 0, 0, },
{ "ExportAudioModule", exportaudiomod_conf, dispatch_modules, 0, 0, },
{ "ExportVideoModule", exportvideomod_conf, dispatch_modules, 0, 0, },
{ "SystemList", systemlist_conf, dispatch_syslist, 1, 0, },
{ "AddAudio", addaudio_conf, dispatch_null, 1, 0, },
{ "AddVideo", addvideo_conf, dispatch_null, 1, 0, },
{ NULL, NULL, NULL, 0, 0, },
};
typedef struct pvm_list_item_ PVMListItem;
struct pvm_list_item_ {
const char *name;
pvm_config_filelist **list;
int type;
int parsed;
/* dispatcher can be generic, so no need for specific one */
};
static PVMListItem pvm_filelist[] = {
{ "AddAudioList", &s_pvm_conf.p_add_list, TC_AUDIO, 0, },
{ "AddVideoList", &s_pvm_conf.p_add_list, TC_VIDEO, 0, },
{ "LogAudioList", &s_pvm_conf.p_add_loglist, TC_AUDIO, 0, },
{ "LogVideoList", &s_pvm_conf.p_add_loglist, TC_VIDEO, 0, },
{ "RemoveAudioList", &s_pvm_conf.p_rem_list, TC_AUDIO, 0, },
{ "RemoveVideoList", &s_pvm_conf.p_rem_list, TC_VIDEO, 0, },
{ NULL, NULL, 0, 0, },
};
/*************************************************************************/
/* dispatcher functions */
/*
* yes, that's ugly (and I don't really wnat expend too much on this).
* Improvements are warmly welcome, but a rewrite from scratch is
* probably the way to go.
*/
struct dispatch_data {
pvm_config_filelist *head;
pvm_config_filelist *tail;
char *codec;
char *destination;
int type;
};
static int dispatch_list_item(TCListItem *item, void *userdata)
{
struct dispatch_data *DD = userdata;
int ret = 0;
pvm_config_filelist *cur = tc_zalloc(sizeof(pvm_config_filelist)); // XXX
if (!cur) {
ret = 1;
} else {
cur->s_type = DD->type;
cur->p_codec = DD->codec; // softref
cur->p_destination = DD->destination; // softref
cur->p_filename = item->data; // hardref
if (!DD->head) {
DD->head = cur;
DD->tail = cur;
} else {
DD->tail->p_next = cur;
cur = DD->tail->p_next;
}
}
return ret;
}
static pvm_config_filelist *dispatch_list(TCList *src, int type,
char *codec, char *destination,
pvm_config_filelist **ret)
{
struct dispatch_data DD = {
.head = NULL,
.tail = NULL,
.type = type,
.codec = codec,
.destination = destination,
};
tc_list_foreach(src, dispatch_list_item, &DD);
if (ret) {
*ret = DD.tail;
}
return DD.head;
}
static int dispatch_node(int id, PVMNodeData *data,
pvm_config_env *env)
{
/*
* this insert nodes in reverse orders, so node defined last
* in configuration file is first on list, but nobody really
* cares about that.
*/
if (env && data && data->enabled) {
pvm_config_hosts *host = tc_zalloc(sizeof(pvm_config_hosts));
if (host) {
/* fill */
host->p_hostname = data->hostname;
host->s_nproc = data->maxprocs;
/* link */
host->p_next = env->p_pvm_hosts;
env->p_pvm_hosts = host;
return 1;
}
}
return 0;
}
static int dispatch_merger(int id, pvm_config_env *env)
{
switch (id) {
case CONF_AUDIO_MERGER_IDX:
env->s_audio_merger.p_hostname = pvm_hostname(audiomerger_hostname);
return 1;
case CONF_VIDEO_MERGER_IDX:
env->s_video_merger.p_hostname = pvm_hostname(videomerger_hostname);
return 1;
case CONF_SYSTEM_MERGER_IDX:
env->s_system_merger.p_hostname = pvm_hostname(systemmerger_hostname);
tc_strstrip(systemmerger_mplexparams);
env->p_multiplex_cmd = systemmerger_mplexparams;
return 1;
default: /* cannot happen */
return 0;
}
return 0; /* paranoia */
}
static int dispatch_null(int id, pvm_config_env *env)
{
return (env != NULL) ?1 :0;
}
static int dispatch_modules(int id, pvm_config_env *env)
{
pvm_config_codec *cfg = NULL;
char *codec = NULL;
PVMString *params = NULL;
switch (id) {
case CONF_EXPORT_AUDIO_MOD_IDX:
cfg = &(env->s_audio_codec);
codec = exportaudiomod_codec;
params = exportaudiomod_params;
break;
case CONF_EXPORT_VIDEO_MOD_IDX:
cfg = &(env->s_video_codec);
codec = exportvideomod_codec;
params = exportvideomod_params;
break;
default: /* cannot happen */
return 0;
}
tc_strstrip(codec);
tc_strstrip(params[0]);
tc_strstrip(params[1]);
tc_strstrip(params[2]);
cfg->p_codec = codec;
cfg->p_par1 = params[0];
cfg->p_par2 = params[1];
cfg->p_par3 = params[2];
return 1;
}
static int dispatch_syslist(int id, pvm_config_env *env)
{
if (env != NULL) {
tc_strstrip(systemlist_codec);
tc_strstrip(systemlist_destination);
tc_strstrip(systemlist_mplexparams);
env->s_sys_list.p_codec = systemlist_codec;
env->s_sys_list.p_destination = systemlist_destination;
env->p_multiplex_cmd = systemlist_mplexparams;
return 1;
}
return 0;
}
/*************************************************************************/
static int parse_nodes(char *p_hostfile, int nodes)
{
int i = 0, ret = 0, parsed = 0;
char buf[TC_BUF_LINE];
if (nodes > PVM_MAX_NODES) {
tc_log_warn(__FILE__, "excessive nodes requested, autolimit to %i",
PVM_MAX_NODES);
nodes = PVM_MAX_NODES;
}
for (i = 0; i < nodes; i++) {
tc_snprintf(buf, sizeof(buf), "Node%i", i+1);
ret = module_read_config(p_hostfile, buf, node_conf[i], __FILE__);
if (ret) {
int done = dispatch_node(i, &nodes_data[i], &s_pvm_conf);
if (done) {
parsed++;
}
}
}
return parsed;
}
static void parse_config(char *p_hostfile, int full)
{
int i = 0;
for (i = 0; pvm_config[i].name != NULL; i++) {
int ret = 0;
if (!full && pvm_config[i].serverside)
continue;
ret = module_read_config(p_hostfile, pvm_config[i].name,
pvm_config[i].conf, __FILE__);
if (ret) {
int done = pvm_config[i].dispatch(i, &s_pvm_conf);
pvm_config[i].parsed = done;
}
}
}
static void parse_filelist(char *p_hostfile)
{
int i = 0;
for (i = 0; pvm_filelist[i].name != NULL; i++) {
TCList *list = module_read_config_list(p_hostfile,
pvm_filelist[i].name,
__FILE__);
if (list) {
int type = pvm_filelist[i].type;
char *codec = (type == TC_VIDEO)
?addvideo_codec :addaudio_codec;
char *dest = (type == TC_VIDEO)
?addvideo_destination :addaudio_destination;
pvm_config_filelist *tail = NULL;
pvm_config_filelist *head = dispatch_list(list, type,
codec, dest, &tail);
if (head) { /* then tail is valid too */
pvm_filelist[i].parsed = 1;
if (*(pvm_filelist[i].list) != NULL) {
tail->p_next = *(pvm_filelist[i].list);
}
*(pvm_filelist[i].list) = head;
}
/* always */
module_free_config_list(list, (head != NULL) ?1 :0);
}
}
}
#define WAS_PARSED(IDX) pvm_config[(IDX)].parsed
static pvm_config_env *validate(int nodes, int verbose)
{
const char *errmsg = "???";
if (nodes < 0) {
errmsg = "Need one PVM node configured";
goto failed;
}
if (((!s_pvm_conf.s_audio_codec.p_codec) && WAS_PARSED(CONF_EXPORT_AUDIO_MOD_IDX))
|| ((!s_pvm_conf.s_video_codec.p_codec) && WAS_PARSED(CONF_EXPORT_VIDEO_MOD_IDX))) {
errmsg = "Need at least Codec parameter in the"
" [ExportVideoModule] or [ExportAudioModule] section";
goto failed;
}
if ((s_pvm_conf.s_system_merger.p_hostname != NULL)
&& (s_pvm_conf.p_multiplex_cmd == NULL)) {
errmsg = "MultiplexParams parameter required in the"
" [SystemMerger] section";
goto failed;
} else if (s_pvm_conf.s_system_merger.p_hostname != NULL) {
s_pvm_conf.s_video_merger.s_build_only_list = 1;
s_pvm_conf.s_audio_merger.s_build_only_list = 1;
}
if ((s_pvm_conf.p_add_list != NULL)
&& (s_pvm_conf.p_add_list->p_codec == NULL)
&& (WAS_PARSED(CONF_ADD_AUDIO_IDX) || WAS_PARSED(CONF_ADD_VIDEO_IDX))) {
errmsg = "Need at least Codec parameter in the [AddList] section";
goto failed;
}
/* done */
return &s_pvm_conf;
failed:
if (verbose) {
tc_log_error(__FILE__, "%s", errmsg);
}
pvm_parser_close();
return NULL;
}
#undef WAS_PARSED
pvm_config_env *pvm_parser_open(char *p_hostfile, int verbose, int full)
{
int i = 0;
/* setup defaults */
s_pvm_conf.p_pvm_hosts = NULL;
/* XXX: add more defaults? */
/* get user data */
parse_config(p_hostfile, full);
/* get node data */
i = parse_nodes(p_hostfile, nodes_num);
/* get lists */
if (full) {
parse_filelist(p_hostfile);
}
/* then validate it */
return validate(i, verbose);
}
void pvm_parser_close(void)
{
pvm_config_hosts *p_pvm_conf_host = NULL, *p_tmp = NULL;
pvm_config_filelist *p_pvm_conf_fileadd = NULL;
pvm_config_filelist *p_pvm_conf_filerem = NULL;
for (p_pvm_conf_host = s_pvm_conf.p_pvm_hosts; p_pvm_conf_host != NULL; ) {
p_tmp = p_pvm_conf_host->p_next;
free(p_pvm_conf_host);
p_pvm_conf_host = p_tmp;
}
for (p_pvm_conf_fileadd = s_pvm_conf.p_add_list; p_pvm_conf_fileadd != NULL; ) {
p_pvm_conf_filerem = p_pvm_conf_fileadd->p_next;
free(p_pvm_conf_fileadd);
p_pvm_conf_fileadd = p_pvm_conf_filerem;
}
for (p_pvm_conf_fileadd = s_pvm_conf.p_rem_list; p_pvm_conf_fileadd != NULL; ) {
p_pvm_conf_filerem = p_pvm_conf_fileadd->p_next;
free(p_pvm_conf_fileadd);
p_pvm_conf_fileadd = p_pvm_conf_filerem;
}
memset(&s_pvm_conf, 0, sizeof(s_pvm_conf));
}
/*************************************************************************/
/*
* Local variables:
* c-file-style: "stroustrup"
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/