627067118c
This is useful, since the normal timegm function isn't a standard function (requiring _BSD_SOURCE or _SVID_SOURCE on glibc to be visible, and not available on e.g. windows). The widely available function mktime uses the local time zone, which requires ugly workarounds to handle UTC time. Signed-off-by: Martin Storsjö <martin@martin.st>
748 lines
23 KiB
C
748 lines
23 KiB
C
/*
|
|
* This file is part of Libav.
|
|
*
|
|
* Libav is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* Libav 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with Libav; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* misc parsing utilities
|
|
*/
|
|
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
#include "avstring.h"
|
|
#include "avutil.h"
|
|
#include "eval.h"
|
|
#include "log.h"
|
|
#include "random_seed.h"
|
|
#include "parseutils.h"
|
|
|
|
typedef struct {
|
|
const char *abbr;
|
|
int width, height;
|
|
} VideoSizeAbbr;
|
|
|
|
typedef struct {
|
|
const char *abbr;
|
|
AVRational rate;
|
|
} VideoRateAbbr;
|
|
|
|
static const VideoSizeAbbr video_size_abbrs[] = {
|
|
{ "ntsc", 720, 480 },
|
|
{ "pal", 720, 576 },
|
|
{ "qntsc", 352, 240 }, /* VCD compliant NTSC */
|
|
{ "qpal", 352, 288 }, /* VCD compliant PAL */
|
|
{ "sntsc", 640, 480 }, /* square pixel NTSC */
|
|
{ "spal", 768, 576 }, /* square pixel PAL */
|
|
{ "film", 352, 240 },
|
|
{ "ntsc-film", 352, 240 },
|
|
{ "sqcif", 128, 96 },
|
|
{ "qcif", 176, 144 },
|
|
{ "cif", 352, 288 },
|
|
{ "4cif", 704, 576 },
|
|
{ "16cif", 1408,1152 },
|
|
{ "qqvga", 160, 120 },
|
|
{ "qvga", 320, 240 },
|
|
{ "vga", 640, 480 },
|
|
{ "svga", 800, 600 },
|
|
{ "xga", 1024, 768 },
|
|
{ "uxga", 1600,1200 },
|
|
{ "qxga", 2048,1536 },
|
|
{ "sxga", 1280,1024 },
|
|
{ "qsxga", 2560,2048 },
|
|
{ "hsxga", 5120,4096 },
|
|
{ "wvga", 852, 480 },
|
|
{ "wxga", 1366, 768 },
|
|
{ "wsxga", 1600,1024 },
|
|
{ "wuxga", 1920,1200 },
|
|
{ "woxga", 2560,1600 },
|
|
{ "wqsxga", 3200,2048 },
|
|
{ "wquxga", 3840,2400 },
|
|
{ "whsxga", 6400,4096 },
|
|
{ "whuxga", 7680,4800 },
|
|
{ "cga", 320, 200 },
|
|
{ "ega", 640, 350 },
|
|
{ "hd480", 852, 480 },
|
|
{ "hd720", 1280, 720 },
|
|
{ "hd1080", 1920,1080 },
|
|
};
|
|
|
|
static const VideoRateAbbr video_rate_abbrs[]= {
|
|
{ "ntsc", { 30000, 1001 } },
|
|
{ "pal", { 25, 1 } },
|
|
{ "qntsc", { 30000, 1001 } }, /* VCD compliant NTSC */
|
|
{ "qpal", { 25, 1 } }, /* VCD compliant PAL */
|
|
{ "sntsc", { 30000, 1001 } }, /* square pixel NTSC */
|
|
{ "spal", { 25, 1 } }, /* square pixel PAL */
|
|
{ "film", { 24, 1 } },
|
|
{ "ntsc-film", { 24000, 1001 } },
|
|
};
|
|
|
|
int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
|
|
{
|
|
int i;
|
|
int n = FF_ARRAY_ELEMS(video_size_abbrs);
|
|
char *p;
|
|
int width = 0, height = 0;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (!strcmp(video_size_abbrs[i].abbr, str)) {
|
|
width = video_size_abbrs[i].width;
|
|
height = video_size_abbrs[i].height;
|
|
break;
|
|
}
|
|
}
|
|
if (i == n) {
|
|
p = str;
|
|
width = strtol(p, &p, 10);
|
|
if (*p)
|
|
p++;
|
|
height = strtol(p, &p, 10);
|
|
}
|
|
if (width <= 0 || height <= 0)
|
|
return AVERROR(EINVAL);
|
|
*width_ptr = width;
|
|
*height_ptr = height;
|
|
return 0;
|
|
}
|
|
|
|
int av_parse_video_rate(AVRational *rate, const char *arg)
|
|
{
|
|
int i, ret;
|
|
int n = FF_ARRAY_ELEMS(video_rate_abbrs);
|
|
double res;
|
|
|
|
/* First, we check our abbreviation table */
|
|
for (i = 0; i < n; ++i)
|
|
if (!strcmp(video_rate_abbrs[i].abbr, arg)) {
|
|
*rate = video_rate_abbrs[i].rate;
|
|
return 0;
|
|
}
|
|
|
|
/* Then, we try to parse it as fraction */
|
|
if ((ret = av_expr_parse_and_eval(&res, arg, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, 0, NULL)) < 0)
|
|
return ret;
|
|
*rate = av_d2q(res, 1001000);
|
|
if (rate->num <= 0 || rate->den <= 0)
|
|
return AVERROR(EINVAL);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *name; ///< a string representing the name of the color
|
|
uint8_t rgb_color[3]; ///< RGB values for the color
|
|
} ColorEntry;
|
|
|
|
static ColorEntry color_table[] = {
|
|
{ "AliceBlue", { 0xF0, 0xF8, 0xFF } },
|
|
{ "AntiqueWhite", { 0xFA, 0xEB, 0xD7 } },
|
|
{ "Aqua", { 0x00, 0xFF, 0xFF } },
|
|
{ "Aquamarine", { 0x7F, 0xFF, 0xD4 } },
|
|
{ "Azure", { 0xF0, 0xFF, 0xFF } },
|
|
{ "Beige", { 0xF5, 0xF5, 0xDC } },
|
|
{ "Bisque", { 0xFF, 0xE4, 0xC4 } },
|
|
{ "Black", { 0x00, 0x00, 0x00 } },
|
|
{ "BlanchedAlmond", { 0xFF, 0xEB, 0xCD } },
|
|
{ "Blue", { 0x00, 0x00, 0xFF } },
|
|
{ "BlueViolet", { 0x8A, 0x2B, 0xE2 } },
|
|
{ "Brown", { 0xA5, 0x2A, 0x2A } },
|
|
{ "BurlyWood", { 0xDE, 0xB8, 0x87 } },
|
|
{ "CadetBlue", { 0x5F, 0x9E, 0xA0 } },
|
|
{ "Chartreuse", { 0x7F, 0xFF, 0x00 } },
|
|
{ "Chocolate", { 0xD2, 0x69, 0x1E } },
|
|
{ "Coral", { 0xFF, 0x7F, 0x50 } },
|
|
{ "CornflowerBlue", { 0x64, 0x95, 0xED } },
|
|
{ "Cornsilk", { 0xFF, 0xF8, 0xDC } },
|
|
{ "Crimson", { 0xDC, 0x14, 0x3C } },
|
|
{ "Cyan", { 0x00, 0xFF, 0xFF } },
|
|
{ "DarkBlue", { 0x00, 0x00, 0x8B } },
|
|
{ "DarkCyan", { 0x00, 0x8B, 0x8B } },
|
|
{ "DarkGoldenRod", { 0xB8, 0x86, 0x0B } },
|
|
{ "DarkGray", { 0xA9, 0xA9, 0xA9 } },
|
|
{ "DarkGreen", { 0x00, 0x64, 0x00 } },
|
|
{ "DarkKhaki", { 0xBD, 0xB7, 0x6B } },
|
|
{ "DarkMagenta", { 0x8B, 0x00, 0x8B } },
|
|
{ "DarkOliveGreen", { 0x55, 0x6B, 0x2F } },
|
|
{ "Darkorange", { 0xFF, 0x8C, 0x00 } },
|
|
{ "DarkOrchid", { 0x99, 0x32, 0xCC } },
|
|
{ "DarkRed", { 0x8B, 0x00, 0x00 } },
|
|
{ "DarkSalmon", { 0xE9, 0x96, 0x7A } },
|
|
{ "DarkSeaGreen", { 0x8F, 0xBC, 0x8F } },
|
|
{ "DarkSlateBlue", { 0x48, 0x3D, 0x8B } },
|
|
{ "DarkSlateGray", { 0x2F, 0x4F, 0x4F } },
|
|
{ "DarkTurquoise", { 0x00, 0xCE, 0xD1 } },
|
|
{ "DarkViolet", { 0x94, 0x00, 0xD3 } },
|
|
{ "DeepPink", { 0xFF, 0x14, 0x93 } },
|
|
{ "DeepSkyBlue", { 0x00, 0xBF, 0xFF } },
|
|
{ "DimGray", { 0x69, 0x69, 0x69 } },
|
|
{ "DodgerBlue", { 0x1E, 0x90, 0xFF } },
|
|
{ "FireBrick", { 0xB2, 0x22, 0x22 } },
|
|
{ "FloralWhite", { 0xFF, 0xFA, 0xF0 } },
|
|
{ "ForestGreen", { 0x22, 0x8B, 0x22 } },
|
|
{ "Fuchsia", { 0xFF, 0x00, 0xFF } },
|
|
{ "Gainsboro", { 0xDC, 0xDC, 0xDC } },
|
|
{ "GhostWhite", { 0xF8, 0xF8, 0xFF } },
|
|
{ "Gold", { 0xFF, 0xD7, 0x00 } },
|
|
{ "GoldenRod", { 0xDA, 0xA5, 0x20 } },
|
|
{ "Gray", { 0x80, 0x80, 0x80 } },
|
|
{ "Green", { 0x00, 0x80, 0x00 } },
|
|
{ "GreenYellow", { 0xAD, 0xFF, 0x2F } },
|
|
{ "HoneyDew", { 0xF0, 0xFF, 0xF0 } },
|
|
{ "HotPink", { 0xFF, 0x69, 0xB4 } },
|
|
{ "IndianRed", { 0xCD, 0x5C, 0x5C } },
|
|
{ "Indigo", { 0x4B, 0x00, 0x82 } },
|
|
{ "Ivory", { 0xFF, 0xFF, 0xF0 } },
|
|
{ "Khaki", { 0xF0, 0xE6, 0x8C } },
|
|
{ "Lavender", { 0xE6, 0xE6, 0xFA } },
|
|
{ "LavenderBlush", { 0xFF, 0xF0, 0xF5 } },
|
|
{ "LawnGreen", { 0x7C, 0xFC, 0x00 } },
|
|
{ "LemonChiffon", { 0xFF, 0xFA, 0xCD } },
|
|
{ "LightBlue", { 0xAD, 0xD8, 0xE6 } },
|
|
{ "LightCoral", { 0xF0, 0x80, 0x80 } },
|
|
{ "LightCyan", { 0xE0, 0xFF, 0xFF } },
|
|
{ "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
|
|
{ "LightGrey", { 0xD3, 0xD3, 0xD3 } },
|
|
{ "LightGreen", { 0x90, 0xEE, 0x90 } },
|
|
{ "LightPink", { 0xFF, 0xB6, 0xC1 } },
|
|
{ "LightSalmon", { 0xFF, 0xA0, 0x7A } },
|
|
{ "LightSeaGreen", { 0x20, 0xB2, 0xAA } },
|
|
{ "LightSkyBlue", { 0x87, 0xCE, 0xFA } },
|
|
{ "LightSlateGray", { 0x77, 0x88, 0x99 } },
|
|
{ "LightSteelBlue", { 0xB0, 0xC4, 0xDE } },
|
|
{ "LightYellow", { 0xFF, 0xFF, 0xE0 } },
|
|
{ "Lime", { 0x00, 0xFF, 0x00 } },
|
|
{ "LimeGreen", { 0x32, 0xCD, 0x32 } },
|
|
{ "Linen", { 0xFA, 0xF0, 0xE6 } },
|
|
{ "Magenta", { 0xFF, 0x00, 0xFF } },
|
|
{ "Maroon", { 0x80, 0x00, 0x00 } },
|
|
{ "MediumAquaMarine", { 0x66, 0xCD, 0xAA } },
|
|
{ "MediumBlue", { 0x00, 0x00, 0xCD } },
|
|
{ "MediumOrchid", { 0xBA, 0x55, 0xD3 } },
|
|
{ "MediumPurple", { 0x93, 0x70, 0xD8 } },
|
|
{ "MediumSeaGreen", { 0x3C, 0xB3, 0x71 } },
|
|
{ "MediumSlateBlue", { 0x7B, 0x68, 0xEE } },
|
|
{ "MediumSpringGreen", { 0x00, 0xFA, 0x9A } },
|
|
{ "MediumTurquoise", { 0x48, 0xD1, 0xCC } },
|
|
{ "MediumVioletRed", { 0xC7, 0x15, 0x85 } },
|
|
{ "MidnightBlue", { 0x19, 0x19, 0x70 } },
|
|
{ "MintCream", { 0xF5, 0xFF, 0xFA } },
|
|
{ "MistyRose", { 0xFF, 0xE4, 0xE1 } },
|
|
{ "Moccasin", { 0xFF, 0xE4, 0xB5 } },
|
|
{ "NavajoWhite", { 0xFF, 0xDE, 0xAD } },
|
|
{ "Navy", { 0x00, 0x00, 0x80 } },
|
|
{ "OldLace", { 0xFD, 0xF5, 0xE6 } },
|
|
{ "Olive", { 0x80, 0x80, 0x00 } },
|
|
{ "OliveDrab", { 0x6B, 0x8E, 0x23 } },
|
|
{ "Orange", { 0xFF, 0xA5, 0x00 } },
|
|
{ "OrangeRed", { 0xFF, 0x45, 0x00 } },
|
|
{ "Orchid", { 0xDA, 0x70, 0xD6 } },
|
|
{ "PaleGoldenRod", { 0xEE, 0xE8, 0xAA } },
|
|
{ "PaleGreen", { 0x98, 0xFB, 0x98 } },
|
|
{ "PaleTurquoise", { 0xAF, 0xEE, 0xEE } },
|
|
{ "PaleVioletRed", { 0xD8, 0x70, 0x93 } },
|
|
{ "PapayaWhip", { 0xFF, 0xEF, 0xD5 } },
|
|
{ "PeachPuff", { 0xFF, 0xDA, 0xB9 } },
|
|
{ "Peru", { 0xCD, 0x85, 0x3F } },
|
|
{ "Pink", { 0xFF, 0xC0, 0xCB } },
|
|
{ "Plum", { 0xDD, 0xA0, 0xDD } },
|
|
{ "PowderBlue", { 0xB0, 0xE0, 0xE6 } },
|
|
{ "Purple", { 0x80, 0x00, 0x80 } },
|
|
{ "Red", { 0xFF, 0x00, 0x00 } },
|
|
{ "RosyBrown", { 0xBC, 0x8F, 0x8F } },
|
|
{ "RoyalBlue", { 0x41, 0x69, 0xE1 } },
|
|
{ "SaddleBrown", { 0x8B, 0x45, 0x13 } },
|
|
{ "Salmon", { 0xFA, 0x80, 0x72 } },
|
|
{ "SandyBrown", { 0xF4, 0xA4, 0x60 } },
|
|
{ "SeaGreen", { 0x2E, 0x8B, 0x57 } },
|
|
{ "SeaShell", { 0xFF, 0xF5, 0xEE } },
|
|
{ "Sienna", { 0xA0, 0x52, 0x2D } },
|
|
{ "Silver", { 0xC0, 0xC0, 0xC0 } },
|
|
{ "SkyBlue", { 0x87, 0xCE, 0xEB } },
|
|
{ "SlateBlue", { 0x6A, 0x5A, 0xCD } },
|
|
{ "SlateGray", { 0x70, 0x80, 0x90 } },
|
|
{ "Snow", { 0xFF, 0xFA, 0xFA } },
|
|
{ "SpringGreen", { 0x00, 0xFF, 0x7F } },
|
|
{ "SteelBlue", { 0x46, 0x82, 0xB4 } },
|
|
{ "Tan", { 0xD2, 0xB4, 0x8C } },
|
|
{ "Teal", { 0x00, 0x80, 0x80 } },
|
|
{ "Thistle", { 0xD8, 0xBF, 0xD8 } },
|
|
{ "Tomato", { 0xFF, 0x63, 0x47 } },
|
|
{ "Turquoise", { 0x40, 0xE0, 0xD0 } },
|
|
{ "Violet", { 0xEE, 0x82, 0xEE } },
|
|
{ "Wheat", { 0xF5, 0xDE, 0xB3 } },
|
|
{ "White", { 0xFF, 0xFF, 0xFF } },
|
|
{ "WhiteSmoke", { 0xF5, 0xF5, 0xF5 } },
|
|
{ "Yellow", { 0xFF, 0xFF, 0x00 } },
|
|
{ "YellowGreen", { 0x9A, 0xCD, 0x32 } },
|
|
};
|
|
|
|
static int color_table_compare(const void *lhs, const void *rhs)
|
|
{
|
|
return av_strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
|
|
}
|
|
|
|
#define ALPHA_SEP '@'
|
|
|
|
int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen,
|
|
void *log_ctx)
|
|
{
|
|
char *tail, color_string2[128];
|
|
const ColorEntry *entry;
|
|
int len, hex_offset = 0;
|
|
|
|
if (color_string[0] == '#') {
|
|
hex_offset = 1;
|
|
} else if (!strncmp(color_string, "0x", 2))
|
|
hex_offset = 2;
|
|
|
|
if (slen < 0)
|
|
slen = strlen(color_string);
|
|
av_strlcpy(color_string2, color_string + hex_offset,
|
|
FFMIN(slen-hex_offset+1, sizeof(color_string2)));
|
|
if ((tail = strchr(color_string2, ALPHA_SEP)))
|
|
*tail++ = 0;
|
|
len = strlen(color_string2);
|
|
rgba_color[3] = 255;
|
|
|
|
if (!av_strcasecmp(color_string2, "random") || !av_strcasecmp(color_string2, "bikeshed")) {
|
|
int rgba = av_get_random_seed();
|
|
rgba_color[0] = rgba >> 24;
|
|
rgba_color[1] = rgba >> 16;
|
|
rgba_color[2] = rgba >> 8;
|
|
rgba_color[3] = rgba;
|
|
} else if (hex_offset ||
|
|
strspn(color_string2, "0123456789ABCDEFabcdef") == len) {
|
|
char *tail;
|
|
unsigned int rgba = strtoul(color_string2, &tail, 16);
|
|
|
|
if (*tail || (len != 6 && len != 8)) {
|
|
av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
if (len == 8) {
|
|
rgba_color[3] = rgba;
|
|
rgba >>= 8;
|
|
}
|
|
rgba_color[0] = rgba >> 16;
|
|
rgba_color[1] = rgba >> 8;
|
|
rgba_color[2] = rgba;
|
|
} else {
|
|
entry = bsearch(color_string2,
|
|
color_table,
|
|
FF_ARRAY_ELEMS(color_table),
|
|
sizeof(ColorEntry),
|
|
color_table_compare);
|
|
if (!entry) {
|
|
av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
memcpy(rgba_color, entry->rgb_color, 3);
|
|
}
|
|
|
|
if (tail) {
|
|
unsigned long int alpha;
|
|
const char *alpha_string = tail;
|
|
if (!strncmp(alpha_string, "0x", 2)) {
|
|
alpha = strtoul(alpha_string, &tail, 16);
|
|
} else {
|
|
alpha = 255 * strtod(alpha_string, &tail);
|
|
}
|
|
|
|
if (tail == alpha_string || *tail || alpha > 255) {
|
|
av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n",
|
|
alpha_string, color_string);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
rgba_color[3] = alpha;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* get a positive number between n_min and n_max, for a maximum length
|
|
of len_max. Return -1 if error. */
|
|
static int date_get_num(const char **pp,
|
|
int n_min, int n_max, int len_max)
|
|
{
|
|
int i, val, c;
|
|
const char *p;
|
|
|
|
p = *pp;
|
|
val = 0;
|
|
for(i = 0; i < len_max; i++) {
|
|
c = *p;
|
|
if (!isdigit(c))
|
|
break;
|
|
val = (val * 10) + c - '0';
|
|
p++;
|
|
}
|
|
/* no number read ? */
|
|
if (p == *pp)
|
|
return -1;
|
|
if (val < n_min || val > n_max)
|
|
return -1;
|
|
*pp = p;
|
|
return val;
|
|
}
|
|
|
|
static const char *small_strptime(const char *p, const char *fmt, struct tm *dt)
|
|
{
|
|
int c, val;
|
|
|
|
for(;;) {
|
|
c = *fmt++;
|
|
if (c == '\0') {
|
|
return p;
|
|
} else if (c == '%') {
|
|
c = *fmt++;
|
|
switch(c) {
|
|
case 'H':
|
|
val = date_get_num(&p, 0, 23, 2);
|
|
if (val == -1)
|
|
return NULL;
|
|
dt->tm_hour = val;
|
|
break;
|
|
case 'M':
|
|
val = date_get_num(&p, 0, 59, 2);
|
|
if (val == -1)
|
|
return NULL;
|
|
dt->tm_min = val;
|
|
break;
|
|
case 'S':
|
|
val = date_get_num(&p, 0, 59, 2);
|
|
if (val == -1)
|
|
return NULL;
|
|
dt->tm_sec = val;
|
|
break;
|
|
case 'Y':
|
|
val = date_get_num(&p, 0, 9999, 4);
|
|
if (val == -1)
|
|
return NULL;
|
|
dt->tm_year = val - 1900;
|
|
break;
|
|
case 'm':
|
|
val = date_get_num(&p, 1, 12, 2);
|
|
if (val == -1)
|
|
return NULL;
|
|
dt->tm_mon = val - 1;
|
|
break;
|
|
case 'd':
|
|
val = date_get_num(&p, 1, 31, 2);
|
|
if (val == -1)
|
|
return NULL;
|
|
dt->tm_mday = val;
|
|
break;
|
|
case '%':
|
|
goto match;
|
|
default:
|
|
return NULL;
|
|
}
|
|
} else {
|
|
match:
|
|
if (c != *p)
|
|
return NULL;
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
time_t av_timegm(struct tm *tm)
|
|
{
|
|
time_t t;
|
|
|
|
int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
|
|
|
|
if (m < 3) {
|
|
m += 12;
|
|
y--;
|
|
}
|
|
|
|
t = 86400 *
|
|
(d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
|
|
|
|
t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
|
|
|
|
return t;
|
|
}
|
|
|
|
int av_parse_time(int64_t *timeval, const char *timestr, int duration)
|
|
{
|
|
const char *p;
|
|
int64_t t;
|
|
struct tm dt;
|
|
int i;
|
|
static const char * const date_fmt[] = {
|
|
"%Y-%m-%d",
|
|
"%Y%m%d",
|
|
};
|
|
static const char * const time_fmt[] = {
|
|
"%H:%M:%S",
|
|
"%H%M%S",
|
|
};
|
|
const char *q;
|
|
int is_utc, len;
|
|
char lastch;
|
|
int negative = 0;
|
|
|
|
#undef time
|
|
time_t now = time(0);
|
|
|
|
len = strlen(timestr);
|
|
if (len > 0)
|
|
lastch = timestr[len - 1];
|
|
else
|
|
lastch = '\0';
|
|
is_utc = (lastch == 'z' || lastch == 'Z');
|
|
|
|
memset(&dt, 0, sizeof(dt));
|
|
|
|
p = timestr;
|
|
q = NULL;
|
|
if (!duration) {
|
|
if (!av_strncasecmp(timestr, "now", len)) {
|
|
*timeval = (int64_t) now * 1000000;
|
|
return 0;
|
|
}
|
|
|
|
/* parse the year-month-day part */
|
|
for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
|
|
q = small_strptime(p, date_fmt[i], &dt);
|
|
if (q) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if the year-month-day part is missing, then take the
|
|
* current year-month-day time */
|
|
if (!q) {
|
|
if (is_utc) {
|
|
dt = *gmtime(&now);
|
|
} else {
|
|
dt = *localtime(&now);
|
|
}
|
|
dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
|
|
} else {
|
|
p = q;
|
|
}
|
|
|
|
if (*p == 'T' || *p == 't' || *p == ' ')
|
|
p++;
|
|
|
|
/* parse the hour-minute-second part */
|
|
for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
|
|
q = small_strptime(p, time_fmt[i], &dt);
|
|
if (q) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/* parse timestr as a duration */
|
|
if (p[0] == '-') {
|
|
negative = 1;
|
|
++p;
|
|
}
|
|
/* parse timestr as HH:MM:SS */
|
|
q = small_strptime(p, time_fmt[0], &dt);
|
|
if (!q) {
|
|
/* parse timestr as S+ */
|
|
dt.tm_sec = strtol(p, (char **)&q, 10);
|
|
if (q == p) {
|
|
/* the parsing didn't succeed */
|
|
*timeval = INT64_MIN;
|
|
return AVERROR(EINVAL);
|
|
}
|
|
dt.tm_min = 0;
|
|
dt.tm_hour = 0;
|
|
}
|
|
}
|
|
|
|
/* Now we have all the fields that we can get */
|
|
if (!q) {
|
|
*timeval = INT64_MIN;
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (duration) {
|
|
t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
|
|
} else {
|
|
dt.tm_isdst = -1; /* unknown */
|
|
if (is_utc) {
|
|
t = av_timegm(&dt);
|
|
} else {
|
|
t = mktime(&dt);
|
|
}
|
|
}
|
|
|
|
t *= 1000000;
|
|
|
|
/* parse the .m... part */
|
|
if (*q == '.') {
|
|
int val, n;
|
|
q++;
|
|
for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
|
|
if (!isdigit(*q))
|
|
break;
|
|
val += n * (*q - '0');
|
|
}
|
|
t += val;
|
|
}
|
|
*timeval = negative ? -t : t;
|
|
return 0;
|
|
}
|
|
|
|
int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
|
|
{
|
|
const char *p;
|
|
char tag[128], *q;
|
|
|
|
p = info;
|
|
if (*p == '?')
|
|
p++;
|
|
for(;;) {
|
|
q = tag;
|
|
while (*p != '\0' && *p != '=' && *p != '&') {
|
|
if ((q - tag) < sizeof(tag) - 1)
|
|
*q++ = *p;
|
|
p++;
|
|
}
|
|
*q = '\0';
|
|
q = arg;
|
|
if (*p == '=') {
|
|
p++;
|
|
while (*p != '&' && *p != '\0') {
|
|
if ((q - arg) < arg_size - 1) {
|
|
if (*p == '+')
|
|
*q++ = ' ';
|
|
else
|
|
*q++ = *p;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
if (!strcmp(tag, tag1))
|
|
return 1;
|
|
if (*p != '&')
|
|
break;
|
|
p++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TEST
|
|
|
|
#undef printf
|
|
|
|
int main(void)
|
|
{
|
|
printf("Testing av_parse_video_rate()\n");
|
|
{
|
|
int i;
|
|
const char *rates[] = {
|
|
"-inf",
|
|
"inf",
|
|
"nan",
|
|
"123/0",
|
|
"-123 / 0",
|
|
"",
|
|
"/",
|
|
" 123 / 321",
|
|
"foo/foo",
|
|
"foo/1",
|
|
"1/foo",
|
|
"0/0",
|
|
"/0",
|
|
"1/",
|
|
"1",
|
|
"0",
|
|
"-123/123",
|
|
"-foo",
|
|
"123.23",
|
|
".23",
|
|
"-.23",
|
|
"-0.234",
|
|
"-0.0000001",
|
|
" 21332.2324 ",
|
|
" -21332.2324 ",
|
|
};
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
|
|
int ret;
|
|
AVRational q = (AVRational){0, 0};
|
|
ret = av_parse_video_rate(&q, rates[i]),
|
|
printf("'%s' -> %d/%d ret:%d\n",
|
|
rates[i], q.num, q.den, ret);
|
|
}
|
|
}
|
|
|
|
printf("\nTesting av_parse_color()\n");
|
|
{
|
|
int i;
|
|
uint8_t rgba[4];
|
|
const char *color_names[] = {
|
|
"bikeshed",
|
|
"RaNdOm",
|
|
"foo",
|
|
"red",
|
|
"Red ",
|
|
"RED",
|
|
"Violet",
|
|
"Yellow",
|
|
"Red",
|
|
"0x000000",
|
|
"0x0000000",
|
|
"0xff000000",
|
|
"0x3e34ff",
|
|
"0x3e34ffaa",
|
|
"0xffXXee",
|
|
"0xfoobar",
|
|
"0xffffeeeeeeee",
|
|
"#ff0000",
|
|
"#ffXX00",
|
|
"ff0000",
|
|
"ffXX00",
|
|
"red@foo",
|
|
"random@10",
|
|
"0xff0000@1.0",
|
|
"red@",
|
|
"red@0xfff",
|
|
"red@0xf",
|
|
"red@2",
|
|
"red@0.1",
|
|
"red@-1",
|
|
"red@0.5",
|
|
"red@1.0",
|
|
"red@256",
|
|
"red@10foo",
|
|
"red@-1.0",
|
|
"red@-0.0",
|
|
};
|
|
|
|
av_log_set_level(AV_LOG_DEBUG);
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(color_names); i++) {
|
|
if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
|
|
printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* TEST */
|