diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/libtc/framecode.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/libtc/framecode.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/libtc/framecode.c b/debian/transcode/transcode-1.1.7/libtc/framecode.c new file mode 100644 index 00000000..aba1f69a --- /dev/null +++ b/debian/transcode/transcode-1.1.7/libtc/framecode.c @@ -0,0 +1,509 @@ +/* + * framecode.c -- framecode list handling + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> /* for UINT_MAX and ULONG_MAX */ +#include <math.h> + +#include "libtc.h" +#include "framecode.h" + +/* Internal function prototypes: */ +static void normalize_fc_time(struct fc_time *range); +static struct fc_time *parse_one_range(const char *string, double fps, + const char **errmsg_ret, + int *errpos_ret); +static int parse_one_time(const char **strptr, unsigned int *hour_ret, + unsigned int *min_ret, unsigned int *sec_ret, + unsigned int *frame_ret, const char **errmsg_ret); +static int parse_one_value(const char **strptr, unsigned int *value_ret, + const char **errmsg_ret); + +/*************************************************************************/ +/************************** External interface ***************************/ +/*************************************************************************/ + +/** + * new_fc_time: Allocate a new, zeroed fc_time structure. + * + * Parameters: + * None. + * Return value: + * The allocated fc_time structure, or NULL on failure. + * Side effects: + * Prints an error message if allocation fails. + */ + +struct fc_time *new_fc_time(void) +{ + return tc_zalloc(sizeof(struct fc_time)); +} + +/*************************************************************************/ + +/** + * free_fc_time: Free a list of allocated fc_time structures. + * + * Parameters: + * list: The list of structures to free. + * Return value: + * None. + */ + +void free_fc_time(struct fc_time *list) +{ + while (list) { + struct fc_time *temp = list->next; + free(list); + list = temp; + } +} + +/*************************************************************************/ + +/** + * set_fc_time: Set fields of an fc_time structure from frame indices. + * + * Parameters: + * range: The fc_time structure to modify. + * start: The frame index for the start time, or -1 for no change. + * end: The frame index for the end time, or -1 for no change. + * Return value: + * None. + * Side effects: + * Prints an error message if the `range' parameter is invalid (either + * the parameter is NULL or it points to a range whose `fps' field is + * not a positive value). + */ + +void set_fc_time(struct fc_time *range, int start, int end) +{ + if (!range || range->fps <= 0) { + tc_log_error(__FILE__, "set_fc_time() with invalid range!"); + return; + } + if (start >= 0) { + range->sh = 0; + range->sm = 0; + range->ss = 0; + range->sf = start; + } + if (end >= 0) { + range->eh = 0; + range->em = 0; + range->es = 0; + range->ef = end; + } + normalize_fc_time(range); +} + +/*************************************************************************/ + +/** + * fc_time_contains: Return whether a list of fc_time structures contains + * a given frame index. + * + * Parameters: + * list: List of fc_time structures to check. + * frame: Frame index. + * Return value: + * Nonzero if one of the ranges contains the given frame index, else 0. + */ + +int fc_time_contains(const struct fc_time *list, unsigned int frame) +{ + while (list) { + if (frame >= list->stf && frame < list->etf) + return 1; + list = list->next; + } + return 0; +} + +/*************************************************************************/ + +/** + * new_fc_time_from_string: Parse a string into a list of fc_time + * structures. + * + * Parameters: + * string: The string to parse. + * separator: A string containing separators for distinct ranges + * within `string'. + * fps: The value to store in each range's `fps' field. + * verbose: If positive, each range will be printed as it is parsed. + * If negative, error messages will be suppressed. + * Return value: + * The list of fc_time structures on success, NULL on failure. + * Side effects: + * Prints an error message if parsing fails, unless verbose < 0. + */ + +struct fc_time *new_fc_time_from_string(const char *string, + const char *separator, + double fps, int verbose) +{ + struct fc_time *list, *tail; + char rangebuf[101]; /* Buffer to hold a single range for processing */ + const char *s; + + /* Sanity checks first */ + if (!string) { + if (verbose >= 0) { + tc_log_error(__FILE__, + "new_fc_time_from_string(): string is NULL!"); + } + return NULL; + } + if (!separator) { + if (verbose >= 0) { + tc_log_error(__FILE__, + "new_fc_time_from_string(): separator is NULL!"); + } + return NULL; + } + if (fps <= 0) { + if (verbose >= 0) { + tc_log_error(__FILE__, "new_fc_time_from_string(): fps <= 0!"); + } + return NULL; + } + + /* Loop through all ranges in the string */ + list = tail = NULL; + s = string + strspn(string,separator); + while (*s) { + struct fc_time *range; /* Newly-allocated fc_time structure */ + const char *errmsg; /* Error message from parse_one_range() */ + int errpos; /* Position of error within range string */ + const char *t; + + t = s + strcspn(s,separator); + if (t-s > sizeof(rangebuf)-1) { + if (verbose >= 0) { + tc_log_error(__FILE__, "new_fc_time_from_string():" + " range string too long! (%u/%u)", + (unsigned)(t-s), (unsigned)sizeof(rangebuf)-1); + /* Print out the string and the location of the error */ + tc_log_error(__FILE__, "%s", string); + tc_log_error(__FILE__, "%*s", (int)((s-string)+1), "^"); + } + /* Don't forget to free anything we already parsed */ + free_fc_time(list); + return NULL; + } + memcpy(rangebuf, s, t-s); + rangebuf[t-s] = 0; + errmsg = "unknown error"; + errpos = 0; + range = parse_one_range(rangebuf, fps, &errmsg, &errpos); + if (!range) { + if (verbose >= 0) { + tc_log_error(__FILE__, "Error parsing framecode range: %s", + errmsg); + tc_log_error(__FILE__, "%s", string); + tc_log_error(__FILE__, "%*s", (int)((s-string+errpos)+1), "^"); + } + free_fc_time(list); + return NULL; + } + if (verbose > 0) { + tc_log_info(__FILE__, "Range: %u:%02u:%02u.%u (%u)" + " - %u:%02u:%02u.%u (%u)", + range->sh, range->sm, range->ss, range->sf, + range->stf, + range->eh, range->em, range->es, range->ef, + range->etf); + } + if (!list) { + list = tail = range; + } else { + tail->next = range; + tail = range; + } + s = t + strspn(t,separator); + } + + /* Parsing completed successfully */ + return list; +} + +/*************************************************************************/ +/************************** Internal functions ***************************/ +/*************************************************************************/ + +/** + * normalize_fc_time: Convert the HH:MM:SS.FF times stored in the given + * fc_time structure to a normalized form, with MM < 60, SS < 60, and + * FF < range->fps; also, store the frame indices corresponding to the + * start and end times in range->stf and range->etf, respectively. + * Fractional frame numbers are rounded down to the next lowest integer. + * Used by set_fc_time() and parse_one_range(). + * + * Parameters: + * range: fc_time structure to normalize. + * Return value: + * None. + * Preconditions: + * range != NULL + * range->fps > 0 + */ + +static void normalize_fc_time(struct fc_time *range) +{ + /* Calculate frame index from time parameters (round down) */ + range->stf = floor(((range->sh * 60 + range->sm) * 60 + range->ss) + * range->fps) + + range->sf; + /* Calculate total number of seconds */ + range->ss = (unsigned int)floor(range->stf / range->fps); + /* Calculate frame remainder */ + range->sf = (unsigned int)floor(range->stf - (range->ss * range->fps)); + /* Calculate normalized hours, minutes, and seconds */ + range->sh = range->ss / 3600; + range->sm = (range->ss/60) % 60; + range->ss %= 60; + + /* Repeat for end time */ + range->etf = floor(((range->eh * 60 + range->em) * 60 + range->es) + * range->fps) + + range->ef; + range->es = (unsigned int)floor(range->etf / range->fps); + range->ef = (unsigned int)floor(range->etf - (range->es * range->fps)); + range->eh = range->es / 3600; + range->em = (range->es/60) % 60; + range->es %= 60; +} + +/*************************************************************************/ + +/** + * parse_one_range: Parse a string containing a single framecode range, + * and return a newly allocated fc_time structure containing the range. + * Used by new_fc_time_from_string(). + * + * Parameters: + * string: String to parse. + * fps: Frames-per-second value to use for the range. + * errmsg_ret: Pointer to location to store error message in. On + * failure, this is filled with a pointer to an error + * message; on success, the value is not modified. + * errpos_ret: Pointer to location to store error position in. On + * failure, this is filled with the offset in characters + * from the start of the string to the position at which + * the error occurred; on success, the value is not + * modified. + * Return value: + * The newly-allocated fc_time structure, or NULL on error. + * Preconditions: + * string != NULL + * fps > 0 + * errmsg_ret != NULL + * errpos_ret != NULL + */ + +static struct fc_time *parse_one_range(const char *string, double fps, + const char **errmsg_ret, + int *errpos_ret) +{ + struct fc_time *range; /* New fc_time structure */ + const char *s = string; /* Current parsing location */ + + /* Allocate new (cleared) fc_time and set FPS */ + range = new_fc_time(); + if (!range) { + *errmsg_ret = "out of memory"; + *errpos_ret = 0; + return NULL; + } + range->fps = fps; + range->stepf = 1; + + /* Parse start time */ + if (!parse_one_time(&s, &range->sh, &range->sm, &range->ss, &range->sf, + errmsg_ret)) + goto error; + + /* Check for and skip intervening hyphen */ + if (*s != '-') { + *errmsg_ret = "syntax error (expected '-')"; + goto error; + } + s++; + + /* Parse end time */ + if (!parse_one_time(&s, &range->eh, &range->em, &range->es, &range->ef, + errmsg_ret)) + goto error; + + /* Parse step value, if present */ + if (*s == '/') { + s++; + if (!parse_one_value(&s, &range->stepf, errmsg_ret)) + goto error; + } + + /* Make sure we're at the end of the string */ + if (*s) { + *errmsg_ret = "garbage at end of range"; + goto error; + } + + /* Successfully parsed: normalize values and return */ + normalize_fc_time(range); + return range; + + error: + *errpos_ret = s - string; + free_fc_time(range); + return NULL; +} + +/*************************************************************************/ + +/** + * parse_one_time: Parse an [[[HH:]MM:]SS.]FF time specification. + * + * Parameters: + * strptr: Pointer to the string to be parsed. + * hour_ret: Pointer to variable to receive the parsed hour value. + * The stored value is unchanged on error. + * min_ret: Pointer to variable to receive the parsed minute value. + * The stored value is unchanged on error. + * sec_ret: Pointer to variable to receive the parsed second value. + * The stored value is unchanged on error. + * frame_ret: Pointer to variable to receive the parsed frame value. + * The stored value is unchanged on error. + * errmsg_ret: As for parse_one_range(). + * Return value: + * Nonzero if parsing succeeded, zero on error. + * Preconditions: + * strptr != NULL + * *strptr != NULL + * hour_ret != NULL + * min_ret != NULL + * sec_ret != NULL + * frame_ret != NULL + * errmsg_ret != NULL + * Side effects: + * `*strptr' is advanced to the first character beyond the parsed + * string on success; on failure, the stored value points to the + * location of the error. + */ + +static int parse_one_time(const char **strptr, unsigned int *hour_ret, + unsigned int *min_ret, unsigned int *sec_ret, + unsigned int *frame_ret, const char **errmsg_ret) +{ + unsigned int hour = 0, min = 0, sec = 0, frame = 0; + int saw_colon = 0; /* for deciding whether it's a bare frame count */ + + if (!parse_one_value(strptr, &hour, errmsg_ret)) + return 0; + if (**strptr == ':') { + saw_colon = 1; + (*strptr)++; + if (!parse_one_value(strptr, &min, errmsg_ret)) + return 0; + if (**strptr == ':') { + (*strptr)++; + if (!parse_one_value(strptr, &sec, errmsg_ret)) + return 0; + } else { + sec = min; + min = hour; + hour = 0; + } + } else { + sec = hour; + hour = 0; + } + if (**strptr == '.') { + (*strptr)++; + if (!parse_one_value(strptr, &frame, errmsg_ret)) + return 0; + } else if (!saw_colon) { + /* No colon or dot--must be a bare frame count */ + frame = sec; + sec = 0; + } + + /* Success */ + *hour_ret = hour; + *min_ret = min; + *sec_ret = sec; + *frame_ret = frame; + return 1; +} + +/*************************************************************************/ + +/** + * parse_one_value: Parse a single base-10 nonnegative integer value from + * the given string. Used by parse_one_range(). + * + * Parameters: + * strptr: Pointer to the string to be parsed. + * value_ret: Pointer to variable to receive the parsed value. The + * stored value is unchanged on error. + * errmsg_ret: As for parse_one_range(). + * Return value: + * Nonzero if parsing succeeded, zero on error. + * Preconditions: + * strptr != NULL + * *strptr != NULL + * value_ret != NULL + * errmsg_ret != NULL + * Side effects: + * `*strptr' is advanced to the first character beyond the parsed + * string on success; on failure, the stored value is unchanged. + */ + +static int parse_one_value(const char **strptr, unsigned int *value_ret, + const char **errmsg_ret) +{ + const char *s; + unsigned long lvalue; + + errno = 0; + lvalue = (unsigned int)strtoul(*strptr, (char **)&s, 10); + if (s == *strptr) { + *errmsg_ret = "not a valid number"; + return 0; + } else if (errno == ERANGE +#if ULONG_MAX > UINT_MAX + || lvalue > UINT_MAX +#endif + ) { + *errmsg_ret = "value out of range"; + return 0; + } + *strptr = s; + *value_ret = (unsigned int)lvalue; + 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: + */ |
