diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/src/export_profile.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/src/export_profile.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/src/export_profile.c b/debian/transcode/transcode-1.1.7/src/export_profile.c new file mode 100644 index 00000000..08703fff --- /dev/null +++ b/debian/transcode/transcode-1.1.7/src/export_profile.c @@ -0,0 +1,511 @@ +/* + * export_profile.c -- transcode export profile support code - implementation + * (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 <unistd.h> + +#include "export_profile.h" +#include "libtc/libtc.h" +#include "libtc/cfgfile.h" +#include "libtc/tccodecs.h" + +/* OK, that's quite ugly but I found nothing better, yet.*/ +#ifdef TCEXPORT_PROFILE +/* same value for both macros */ +# define TC_EXPORT_PROFILE_OPT "-P" +#else +# define TC_EXPORT_PROFILE_OPT "--export_prof" +#endif + +#define USER_PROF_PATH ".transcode/profiles" + +/* all needed support variables/data packed in a nice structure */ +typedef struct tcexportprofile_ TCExportProfile; + +struct tcexportprofile_ { + size_t profile_count; + char **profiles; + + TCExportInfo info; + + /* auxiliary variables */ + const char *video_codec; + const char *audio_codec; + + const char *pre_clip_area; + const char *post_clip_area; +}; + +#define CLIP_AREA_INIT { 0, 0, 0, 0 } + +/* used in tc_log_*() calls */ +const char *package = __FILE__; + +static TCExportProfile prof_data = { + .profile_count = 0, + .profiles = NULL, + + .video_codec = NULL, + .audio_codec = NULL, + .pre_clip_area = NULL, + .post_clip_area = NULL, + + /* + * we need to take care of strings deallocating + * them between module_read_config() calls, to + * avoid memleaks, so we use NULL marking here. + */ + .info.video.string = NULL, + .info.video.module = NULL, + .info.video.module_opts = NULL, + .info.video.log_file = NULL, + + .info.audio.string = NULL, + .info.audio.module = NULL, + .info.audio.module_opts = NULL, + + .info.mplex.string = NULL, + .info.mplex.module = NULL, + .info.mplex.module_opts = NULL, + .info.mplex.out_file = NULL, + .info.mplex.out_file_aux = NULL, + + /* standard initialization */ + .info.video.width = PAL_W, + .info.video.height = PAL_H, + .info.video.keep_asr_flag = TC_FALSE, + .info.video.fast_resize_flag = TC_FALSE, + .info.video.zoom_interlaced_flag = TC_FALSE, + .info.video.pre_clip = CLIP_AREA_INIT, + .info.video.post_clip = CLIP_AREA_INIT, + .info.video.frc = 3, // XXX (magic number) + .info.video.asr = -1, // XXX + .info.video.par = 0, + .info.video.encode_fields = TC_ENCODE_FIELDS_PROGRESSIVE, + .info.video.gop_size = VKEYFRAMES, + .info.video.quantizer_min = VMINQUANTIZER, + .info.video.quantizer_max = VMAXQUANTIZER, + .info.video.format = CODEC_NULL, + .info.video.quality = -1, + .info.video.bitrate = VBITRATE, + .info.video.bitrate_max = VBITRATE, + .info.video.pass_number = VMULTIPASS, + + .info.audio.format = CODEC_NULL, + .info.audio.quality = -1, + .info.audio.bitrate = ABITRATE, + .info.audio.sample_rate = RATE, + .info.audio.sample_bits = BITS, + .info.audio.channels = CHANNELS, + .info.audio.mode = AMODE, + .info.audio.vbr_flag = TC_FALSE, + .info.audio.flush_flag = TC_FALSE, + .info.audio.bit_reservoir = TC_TRUE, +}; + +/* private helpers: declaration *******************************************/ + +/* + * tc_load_single_export_profile: + * tc_load_export_profile backend. + * Find and load, by looking into user profile path then into system + * profiles path, the i-th selected profile. + * If profile can't be loaded, it will just skipped. + * If verbose >= TC_DEBUG and the profile wasn't loaded, notify + * the user using tc_log*. + * If verbose >= TC_INFO and profile was loaded, notify the user + * using tc_log*. + * + * Parameters: + * i: load the i-th already parsed (see below) export profile. + * config: use this TCConfigEntry array, provided by frontend, to + * parse profile data file + * sys_path: system path to look for profile data + * user_path: user path to look for profile data + * Return value: + * 1: profile data succesfully loaded + * 0: profile data not loaded for some reasons (profile file + * not found or not readable). + * Side effects: + * Isn't a proper side effect, anyway it's worth to note that + * this function _will_ alter some data not explicitely provided, + * via config parameter. Note that this function WILL NOT alter + * config data, so caller providing config data will have full + * control of those unproper side effects by careful craft of + * TCConfigEntry data. + * Also note that this function mangle global private prof_data + * variable, most notably by invoking cleanup_strings on it + * to avoid memleaks in subsequent calls of module_read_config. + * Preconditions: + * this function should be always used _after_ a succesfull + * call to tc_setup_export_profiles, which parse the profiles + * selected by user. Otherwise it's still safe to call this function, + * but it always fail. + */ +static int tc_load_single_export_profile(int i, TCConfigEntry *config, + const char *sys_path, + const char *user_path); + +/* utilities used internally (yet) */ + +/* + * cleanup_strings: + * free()s and reset to NULL every not-NULL string in a given + * TCExportInfo structure + * + * Parameters: + * info: pointero to a TCExportInfo structure to cleanup. + * Return value: + * None. + */ +static void cleanup_strings(TCExportInfo *info); + +/* + * setup_clip_area: + * helper to parse a clipping area string into a TCArea structure. + * Automagically expand the clipping information using the same + * logic of transcode core (actually this code is a very + * little more than a rip-off form src/transcode.c). + * + * Parameters: + * str: clipping area string to parse. + * area: pointero to a TCArea where clipping parameters will be stored. + * Return value: + * 1: succesfull + * -1: error: malformed clipping string or bad parameters. + */ +static int setup_clip_area(const char *str, TCArea *area); + + +/*************************************************************************/ + +int tc_setup_export_profile(int *argc, char ***argv) +{ + const char *optval = NULL; + int ret; + + if (argc == NULL || argv == NULL) { + tc_log_warn(package, "tc_setup_export_profile: bad data reference"); + return -2; + } + + /* guess package name from command line */ + package = (*argv)[0]; + ret = tc_mangle_cmdline(argc, argv, + TC_EXPORT_PROFILE_OPT, &optval); + if (ret == 0) { /* success */ + prof_data.profiles = tc_strsplit(optval, ',', + &prof_data.profile_count); + ret = (int)prof_data.profile_count; + if (verbose >= TC_INFO) { + tc_log_info(package, "E: %-16s | %i", "profiles parsed", ret); + } + } + return ret; +} + +void tc_cleanup_export_profile(void) +{ + tc_strfreev(prof_data.profiles); + prof_data.profile_count = 0; + + cleanup_strings(&prof_data.info); +} + +const TCExportInfo *tc_load_export_profile(void) +{ + /* not all settings will be accessible from here */ + /* note static here */ + static TCConfigEntry profile_conf[] = { + /* video stuff */ + { "video_codec", &(prof_data.video_codec), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_module", &(prof_data.info.video.module), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_module_options", &(prof_data.info.video.module_opts), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_fourcc", &(prof_data.info.video.string), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_bitrate", &(prof_data.info.video.bitrate), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 12000000 }, + { "video_bitrate_max", &(prof_data.info.video.bitrate_max), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 12000000 }, + { "video_gop_size", &(prof_data.info.video.gop_size), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 2000 }, + { "video_encode_fields", &(prof_data.info.video.encode_fields), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 3 }, + // FIXME: switch to char/string? + { "video_frc", &(prof_data.info.video.frc), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 5 }, + { "video_asr", &(prof_data.info.video.asr), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 9 }, + { "video_par", &(prof_data.info.video.par), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 9 }, + // FIXME: expand achronym? + { "video_pre_clip", &(prof_data.pre_clip_area), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_post_clip", &(prof_data.post_clip_area), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "video_width", &(prof_data.info.video.width), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, + 1, TC_MAX_V_FRAME_WIDTH }, + { "video_height", &(prof_data.info.video.height), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, + 1, TC_MAX_V_FRAME_HEIGHT }, + { "video_keep_asr", &(prof_data.info.video.keep_asr_flag), + TCCONF_TYPE_FLAG, 0, 0, 1 }, + { "video_fast_resize", &(prof_data.info.video.fast_resize_flag), + TCCONF_TYPE_FLAG, 0, 0, 1 }, + { "video_zoom_interlaced", &(prof_data.info.video.zoom_interlaced_flag), + TCCONF_TYPE_FLAG, 0, 0, 1 }, + /* audio stuff */ + { "audio_codec", &(prof_data.audio_codec), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "audio_module", &(prof_data.info.audio.module), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "audio_module_options", &(prof_data.info.audio.module_opts), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "audio_bitrate", &(prof_data.info.audio.bitrate), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 1000000 }, + { "audio_frequency", &(prof_data.info.audio.sample_rate), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 48000 }, + // XXX: review min + { "audio_bits", &(prof_data.info.audio.sample_bits), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 8, 16 }, // XXX + { "audio_channels", &(prof_data.info.audio.channels), + TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 2 }, + /* multiplexing */ + { "mplex_module", &(prof_data.info.mplex.module), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { "mplex_module_options", &(prof_data.info.mplex.module_opts), + TCCONF_TYPE_STRING, 0, 0, 0 }, + { NULL, NULL, 0, 0, 0, 0 } + }; + char home_path[PATH_MAX + 1]; + const char *home = getenv("HOME"); + int i = 0; + + if (home != NULL) { + tc_snprintf(home_path, sizeof(home_path), "%s/%s", + home, USER_PROF_PATH); + } else { + tc_log_warn(package, "can't determinate home directory!"); + return NULL; + } + + for (i = 0; i < prof_data.profile_count; i++) { + tc_load_single_export_profile(i, profile_conf, + PROF_PATH, home_path); + } + return &prof_data.info; +} + +/* it's pretty naif yet. FR */ +void tc_export_profile_to_vob(const TCExportInfo *info, vob_t *vob) +{ + if (info == NULL || vob == NULL) { + return; + } + vob->ex_v_string = info->video.module_opts; + vob->ex_a_string = info->audio.module_opts; + vob->ex_m_string = info->mplex.module_opts; + vob->ex_v_codec = info->video.format; + vob->ex_a_codec = info->audio.format; + vob->ex_v_fcc = info->video.string; + vob->ex_frc = info->video.frc; + vob->ex_asr = info->video.asr; + vob->ex_par = info->video.par; + vob->encode_fields = info->video.encode_fields; + vob->divxbitrate = info->video.bitrate; + vob->mp3bitrate = info->audio.bitrate; + vob->video_max_bitrate = info->video.bitrate_max; + vob->divxkeyframes = info->video.gop_size; + vob->mp3frequency = info->audio.sample_rate; + vob->dm_bits = info->audio.sample_bits; + vob->dm_chan = info->audio.channels; + vob->mp3mode = info->audio.mode; + vob->bitreservoir = info->audio.bit_reservoir; + vob->zoom_interlaced = info->video.zoom_interlaced_flag; + if (info->video.fast_resize_flag) { + tc_compute_fast_resize_values(vob, TC_FALSE); + } else { + vob->zoom_width = info->video.width; + vob->zoom_height = info->video.height; + } +} + +/************************************************************************* + * private helpers: implementation + **************************************************************************/ + +#define SETUP_CODEC(TYPE) do { \ + int codec = 0; /* shortcut */\ + if (prof_data.TYPE ## _codec != NULL) { \ + codec = tc_codec_from_string(prof_data.TYPE ## _codec); \ + prof_data.info.TYPE.format = codec; \ + tc_free((char*)prof_data.TYPE ## _codec); /* avoid const warning */ \ + prof_data.TYPE ## _codec = NULL; \ + } \ +} while (0) + +#define SETUP_CLIPPING(TYPE) do { \ + if (prof_data.TYPE ## _clip_area != NULL) { \ + memset(&(prof_data.info.video.TYPE ## _clip), 0, sizeof(TCArea)); \ + setup_clip_area(prof_data.TYPE ## _clip_area, \ + &(prof_data.info.video.TYPE ## _clip)); \ + tc_free((char*)prof_data.TYPE ## _clip_area); /* avoid const warning */ \ + prof_data.TYPE ## _clip_area = NULL; \ + } \ +} while (0) + +static int tc_load_single_export_profile(int i, TCConfigEntry *config, + const char *sys_path, + const char *user_path) +{ + int found = 0, ret = 0; + char path_buf[PATH_MAX+1]; + const char *basedir = NULL; + + if (sys_path == NULL || user_path == NULL || config == NULL + || ((i < 0) || i >= prof_data.profile_count)) { + /* paranoia */ + tc_log_warn(package, "tc_load_single_export_profile:" + " bad data reference"); + return -1; + } + + tc_snprintf(path_buf, sizeof(path_buf), "%s/%s.cfg", + user_path, prof_data.profiles[i]); + ret = access(path_buf, R_OK); + if (ret == 0) { + found = 1; + basedir = user_path; + } else { + tc_snprintf(path_buf, sizeof(path_buf), "%s/%s.cfg", + sys_path, prof_data.profiles[i]); + ret = access(path_buf, R_OK); + if (ret == 0) { + found = 1; + basedir = PROF_PATH; + } + } + + if (found) { + char prof_name[TC_BUF_MIN]; + cleanup_strings(&prof_data.info); + tc_snprintf(prof_name, sizeof(prof_name), "%s.cfg", + prof_data.profiles[i]); + + tc_set_config_dir(basedir); + ret = module_read_config(prof_name, NULL, config, package); + if (ret == 0) { + found = 0; /* module_read_config() failed */ + } else { + if (verbose >= TC_INFO) { + tc_log_info(package, "E: %-16s | %s", "loaded profile", + path_buf); + } + SETUP_CODEC(video); + SETUP_CODEC(audio); + SETUP_CLIPPING(pre); + SETUP_CLIPPING(post); + } + } else { + if (verbose >= TC_DEBUG) { + tc_log_warn(package, "E: %-16s | %s (skipped)", "unable to load", + path_buf); + } + } + return found; +} + +#undef SETUP_CODEC +#undef SETUP_CLIPPING + +/* + * module_read_config (used internally, see later) + * allocates new strings for option values, so + * we need to take care of them using this couple + * of functions + */ + +#define CLEANUP_STRING(FIELD) do { \ + if (info->FIELD != NULL) {\ + tc_free(info->FIELD); \ + info->FIELD = NULL; \ + } \ +} while (0) + +static void cleanup_strings(TCExportInfo *info) +{ + if (info != NULL) { + /* paranoia */ + + CLEANUP_STRING(video.string); + CLEANUP_STRING(video.module); + CLEANUP_STRING(video.module_opts); + CLEANUP_STRING(video.log_file); + + CLEANUP_STRING(audio.string); + CLEANUP_STRING(audio.module); + CLEANUP_STRING(audio.module_opts); + + CLEANUP_STRING(mplex.string); + CLEANUP_STRING(mplex.module); + CLEANUP_STRING(mplex.module_opts); + CLEANUP_STRING(mplex.out_file); + CLEANUP_STRING(mplex.out_file_aux); + } +} + +#undef CLEANUP_STRING + +/*************************************************************************/ + +static int setup_clip_area(const char *str, TCArea *area) +{ + int n = sscanf(str, "%i,%i,%i,%i", + &area->top, &area->left, &area->bottom, &area->right); + if (n < 0) { + return -1; + } + + /* symmetrical clipping for only 1-3 arguments */ + if (n == 1 || n == 2) { + area->bottom = area->top; + } + if (n == 2 || n == 3) { + area->right = area->left; + } + + return 1; +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ |
