mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-01 20:18:15 +01:00
The ffmpeg submodule held prebuilt libraries for every platform next to the sources (about 1.5 GB checked out), and nothing uses those any more. Only the needed sources are copied in, from the submodule's commit, along with every header and template they include. The path is new, so checking out commits on either side of this works. It's also cut down to what PPSSPP uses: the H.264, AAC, MP3, ATRAC3 and ATRAC3+ decoders, the H.264 parser, the demuxers as before, and the FFV1 encoder, AVI muxer and file protocol for AVI dumping. config.h takes its CONFIG_ values from FFmpeg 3.0.2's own configure run with just those enabled. The raw H.264 demuxer stays even though nothing opens raw H.264: probing PSMF data uses it to identify the video stream's codec, which otherwise comes out as none. On top of that: - The files nothing links (configure would build them, but they're never pulled out of the static library, going by the GCC link map) are left out: mostly libavutil's crypto and hashing and the alternative DCT/IDCT/FFT versions. - libswresample isn't built, now that nothing uses it. - The H.264 decoder only does 8-bit video, which is all the PSP has. The SPS parser rejects any other bit depth, and the DSP files no longer build their 9- to 16-bit variants, which the linker kept because the init functions pick one by bit depth at run time. That's 160 files to compile instead of 277, and 391 of FFmpeg's vendored, about 5.8 MB. The README in ext/ffmpeg has what was done, the PPSSPP patches in the copy, how to add a component, and the tradeoffs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
931 lines
29 KiB
C
931 lines
29 KiB
C
/*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg 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.
|
|
*
|
|
* FFmpeg 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 FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* misc parsing utilities
|
|
*/
|
|
|
|
#include <time.h>
|
|
|
|
#include "avstring.h"
|
|
#include "avutil.h"
|
|
#include "common.h"
|
|
#include "eval.h"
|
|
#include "log.h"
|
|
#include "random_seed.h"
|
|
#include "time_internal.h"
|
|
#include "parseutils.h"
|
|
#include "time.h"
|
|
|
|
#ifdef TEST
|
|
|
|
#define av_get_random_seed av_get_random_seed_deterministic
|
|
static uint32_t av_get_random_seed_deterministic(void);
|
|
|
|
#define av_gettime() 1331972053200000
|
|
|
|
#endif
|
|
|
|
int av_parse_ratio(AVRational *q, const char *str, int max,
|
|
int log_offset, void *log_ctx)
|
|
{
|
|
char c;
|
|
int ret;
|
|
|
|
if (sscanf(str, "%d:%d%c", &q->num, &q->den, &c) != 2) {
|
|
double d;
|
|
ret = av_expr_parse_and_eval(&d, str, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, log_offset, log_ctx);
|
|
if (ret < 0)
|
|
return ret;
|
|
*q = av_d2q(d, max);
|
|
} else {
|
|
av_reduce(&q->num, &q->den, q->num, q->den, max);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef struct VideoSizeAbbr {
|
|
const char *abbr;
|
|
int width, height;
|
|
} VideoSizeAbbr;
|
|
|
|
typedef struct VideoRateAbbr {
|
|
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 },
|
|
{ "2k", 2048,1080 }, /* Digital Cinema System Specification */
|
|
{ "2kdci", 2048,1080 },
|
|
{ "2kflat", 1998,1080 },
|
|
{ "2kscope", 2048, 858 },
|
|
{ "4k", 4096,2160 }, /* Digital Cinema System Specification */
|
|
{ "4kdci", 4096,2160 },
|
|
{ "4kflat", 3996,2160 },
|
|
{ "4kscope", 4096,1716 },
|
|
{ "nhd", 640,360 },
|
|
{ "hqvga", 240,160 },
|
|
{ "wqvga", 400,240 },
|
|
{ "fwqvga", 432,240 },
|
|
{ "hvga", 480,320 },
|
|
{ "qhd", 960,540 },
|
|
{ "uhd2160", 3840,2160 },
|
|
{ "uhd4320", 7680,4320 },
|
|
};
|
|
|
|
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);
|
|
const 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) {
|
|
width = strtol(str, (void*)&p, 10);
|
|
if (*p)
|
|
p++;
|
|
height = strtol(p, (void*)&p, 10);
|
|
|
|
/* trailing extraneous data detected, like in 123x345foobar */
|
|
if (*p)
|
|
return AVERROR(EINVAL);
|
|
}
|
|
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);
|
|
|
|
/* 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_parse_ratio_quiet(rate, arg, 1001000)) < 0)
|
|
return ret;
|
|
if (rate->num <= 0 || rate->den <= 0)
|
|
return AVERROR(EINVAL);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct ColorEntry {
|
|
const char *name; ///< a string representing the name of the color
|
|
uint8_t rgb_color[3]; ///< RGB values for the color
|
|
} ColorEntry;
|
|
|
|
static const 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 } },
|
|
{ "LightGreen", { 0x90, 0xEE, 0x90 } },
|
|
{ "LightGrey", { 0xD3, 0xD3, 0xD3 } },
|
|
{ "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) {
|
|
double alpha;
|
|
const char *alpha_string = tail;
|
|
if (!strncmp(alpha_string, "0x", 2)) {
|
|
alpha = strtoul(alpha_string, &tail, 16);
|
|
} else {
|
|
double norm_alpha = strtod(alpha_string, &tail);
|
|
if (norm_alpha < 0.0 || norm_alpha > 1.0)
|
|
alpha = 256;
|
|
else
|
|
alpha = 255 * norm_alpha;
|
|
}
|
|
|
|
if (tail == alpha_string || *tail || alpha > 255 || alpha < 0) {
|
|
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;
|
|
}
|
|
|
|
const char *av_get_known_color_name(int color_idx, const uint8_t **rgbp)
|
|
{
|
|
const ColorEntry *color;
|
|
|
|
if ((unsigned)color_idx >= FF_ARRAY_ELEMS(color_table))
|
|
return NULL;
|
|
|
|
color = &color_table[color_idx];
|
|
if (rgbp)
|
|
*rgbp = color->rgb_color;
|
|
|
|
return color->name;
|
|
}
|
|
|
|
/* 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 (!av_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;
|
|
}
|
|
|
|
char *av_small_strptime(const char *p, const char *fmt, struct tm *dt)
|
|
{
|
|
int c, val;
|
|
|
|
while((c = *fmt++)) {
|
|
if (c != '%') {
|
|
if (av_isspace(c))
|
|
for (; *p && av_isspace(*p); p++);
|
|
else if (*p != c)
|
|
return NULL;
|
|
else p++;
|
|
continue;
|
|
}
|
|
|
|
c = *fmt++;
|
|
switch(c) {
|
|
case 'H':
|
|
case 'J':
|
|
val = date_get_num(&p, 0, c == 'H' ? 23 : INT_MAX, 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 'T':
|
|
p = av_small_strptime(p, "%H:%M:%S", dt);
|
|
if (!p)
|
|
return NULL;
|
|
break;
|
|
case '%':
|
|
if (*p++ != '%')
|
|
return NULL;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return (char*)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 = 86400LL *
|
|
(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, *q;
|
|
int64_t t, now64;
|
|
time_t now;
|
|
struct tm dt = { 0 }, tmbuf;
|
|
int today = 0, negative = 0, microseconds = 0;
|
|
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",
|
|
};
|
|
static const char * const tz_fmt[] = {
|
|
"%H:%M",
|
|
"%H%M",
|
|
"%H",
|
|
};
|
|
|
|
p = timestr;
|
|
q = NULL;
|
|
*timeval = INT64_MIN;
|
|
if (!duration) {
|
|
now64 = av_gettime();
|
|
now = now64 / 1000000;
|
|
|
|
if (!av_strcasecmp(timestr, "now")) {
|
|
*timeval = now64;
|
|
return 0;
|
|
}
|
|
|
|
/* parse the year-month-day part */
|
|
for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
|
|
q = av_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) {
|
|
today = 1;
|
|
q = p;
|
|
}
|
|
p = q;
|
|
|
|
if (*p == 'T' || *p == 't')
|
|
p++;
|
|
else
|
|
while (av_isspace(*p))
|
|
p++;
|
|
|
|
/* parse the hour-minute-second part */
|
|
for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
|
|
q = av_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 = av_small_strptime(p, "%J:%M:%S", &dt);
|
|
if (!q) {
|
|
/* parse timestr as MM:SS */
|
|
q = av_small_strptime(p, "%M:%S", &dt);
|
|
dt.tm_hour = 0;
|
|
}
|
|
if (!q) {
|
|
char *o;
|
|
/* parse timestr as S+ */
|
|
dt.tm_sec = strtol(p, &o, 10);
|
|
if (o == p) /* the parsing didn't succeed */
|
|
return AVERROR(EINVAL);
|
|
dt.tm_min = 0;
|
|
dt.tm_hour = 0;
|
|
q = o;
|
|
}
|
|
}
|
|
|
|
/* Now we have all the fields that we can get */
|
|
if (!q)
|
|
return AVERROR(EINVAL);
|
|
|
|
/* parse the .m... part */
|
|
if (*q == '.') {
|
|
int n;
|
|
q++;
|
|
for (n = 100000; n >= 1; n /= 10, q++) {
|
|
if (!av_isdigit(*q))
|
|
break;
|
|
microseconds += n * (*q - '0');
|
|
}
|
|
while (av_isdigit(*q))
|
|
q++;
|
|
}
|
|
|
|
if (duration) {
|
|
t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
|
|
} else {
|
|
int is_utc = *q == 'Z' || *q == 'z';
|
|
int tzoffset = 0;
|
|
q += is_utc;
|
|
if (!today && !is_utc && (*q == '+' || *q == '-')) {
|
|
struct tm tz = { 0 };
|
|
int sign = (*q == '+' ? -1 : 1);
|
|
q++;
|
|
p = q;
|
|
for (i = 0; i < FF_ARRAY_ELEMS(tz_fmt); i++) {
|
|
q = av_small_strptime(p, tz_fmt[i], &tz);
|
|
if (q)
|
|
break;
|
|
}
|
|
if (!q)
|
|
return AVERROR(EINVAL);
|
|
tzoffset = sign * (tz.tm_hour * 60 + tz.tm_min) * 60;
|
|
is_utc = 1;
|
|
}
|
|
if (today) { /* fill in today's date */
|
|
struct tm dt2 = is_utc ? *gmtime_r(&now, &tmbuf) : *localtime_r(&now, &tmbuf);
|
|
dt2.tm_hour = dt.tm_hour;
|
|
dt2.tm_min = dt.tm_min;
|
|
dt2.tm_sec = dt.tm_sec;
|
|
dt = dt2;
|
|
}
|
|
t = is_utc ? av_timegm(&dt) : mktime(&dt);
|
|
t += tzoffset;
|
|
}
|
|
|
|
/* Check that we are at the end of the string */
|
|
if (*q)
|
|
return AVERROR(EINVAL);
|
|
|
|
t *= 1000000;
|
|
t += microseconds;
|
|
*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
|
|
|
|
static uint32_t randomv = MKTAG('L','A','V','U');
|
|
|
|
static uint32_t av_get_random_seed_deterministic(void)
|
|
{
|
|
return randomv = randomv * 1664525 + 1013904223;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
printf("Testing av_parse_video_rate()\n");
|
|
{
|
|
int i;
|
|
static const char *const 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 = { 0, 0 };
|
|
ret = av_parse_video_rate(&q, rates[i]);
|
|
printf("'%s' -> %d/%d %s\n",
|
|
rates[i], q.num, q.den, ret ? "ERROR" : "OK");
|
|
}
|
|
}
|
|
|
|
printf("\nTesting av_parse_color()\n");
|
|
{
|
|
int i;
|
|
uint8_t rgba[4];
|
|
static const char *const 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]);
|
|
else
|
|
printf("%s -> error\n", color_names[i]);
|
|
}
|
|
}
|
|
|
|
printf("\nTesting av_small_strptime()\n");
|
|
{
|
|
int i;
|
|
struct tm tm = { 0 };
|
|
struct fmt_timespec_entry {
|
|
const char *fmt, *timespec;
|
|
} fmt_timespec_entries[] = {
|
|
{ "%Y-%m-%d", "2012-12-21" },
|
|
{ "%Y - %m - %d", "2012-12-21" },
|
|
{ "%Y-%m-%d %H:%M:%S", "2012-12-21 20:12:21" },
|
|
{ " %Y - %m - %d %H : %M : %S", " 2012 - 12 - 21 20 : 12 : 21" },
|
|
};
|
|
|
|
av_log_set_level(AV_LOG_DEBUG);
|
|
for (i = 0; i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) {
|
|
char *p;
|
|
struct fmt_timespec_entry *e = &fmt_timespec_entries[i];
|
|
printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec);
|
|
p = av_small_strptime(e->timespec, e->fmt, &tm);
|
|
if (p) {
|
|
printf("%04d-%02d-%2d %02d:%02d:%02d\n",
|
|
1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
} else {
|
|
printf("error\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("\nTesting av_parse_time()\n");
|
|
{
|
|
int i;
|
|
int64_t tv;
|
|
time_t tvi;
|
|
struct tm *tm;
|
|
static char tzstr[] = "TZ=CET-1";
|
|
static const char * const time_string[] = {
|
|
"now",
|
|
"12:35:46",
|
|
"2000-12-20 0:02:47.5z",
|
|
"2012 - 02-22 17:44:07",
|
|
"2000-12-20T010247.6",
|
|
"2000-12-12 1:35:46+05:30",
|
|
"2002-12-12 22:30:40-02",
|
|
};
|
|
static const char * const duration_string[] = {
|
|
"2:34:56.79",
|
|
"-1:23:45.67",
|
|
"42.1729",
|
|
"-1729.42",
|
|
"12:34",
|
|
};
|
|
|
|
av_log_set_level(AV_LOG_DEBUG);
|
|
putenv(tzstr);
|
|
printf("(now is 2012-03-17 09:14:13.2 +0100, local time is UTC+1)\n");
|
|
for (i = 0; i < FF_ARRAY_ELEMS(time_string); i++) {
|
|
printf("%-24s -> ", time_string[i]);
|
|
if (av_parse_time(&tv, time_string[i], 0)) {
|
|
printf("error\n");
|
|
} else {
|
|
tvi = tv / 1000000;
|
|
tm = gmtime(&tvi);
|
|
printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
|
|
tv / 1000000, (int)(tv % 1000000),
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
}
|
|
}
|
|
for (i = 0; i < FF_ARRAY_ELEMS(duration_string); i++) {
|
|
printf("%-24s -> ", duration_string[i]);
|
|
if (av_parse_time(&tv, duration_string[i], 1)) {
|
|
printf("error\n");
|
|
} else {
|
|
printf("%+21"PRIi64"\n", tv);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* TEST */
|