FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
parseutils.c
Go to the documentation of this file.
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 /**
20  * @file
21  * misc parsing utilities
22  */
23 
24 #include <time.h>
25 
26 #include "avstring.h"
27 #include "avutil.h"
28 #include "common.h"
29 #include "eval.h"
30 #include "log.h"
31 #include "random_seed.h"
32 #include "parseutils.h"
33 
34 #ifdef TEST
35 
36 #define av_get_random_seed av_get_random_seed_deterministic
37 static uint32_t av_get_random_seed_deterministic(void);
38 
39 #define time(t) 1331972053
40 
41 #endif
42 
43 int av_parse_ratio(AVRational *q, const char *str, int max,
44  int log_offset, void *log_ctx)
45 {
46  char c;
47  int ret;
48 
49  if (sscanf(str, "%d:%d%c", &q->num, &q->den, &c) != 2) {
50  double d;
51  ret = av_expr_parse_and_eval(&d, str, NULL, NULL,
52  NULL, NULL, NULL, NULL,
53  NULL, log_offset, log_ctx);
54  if (ret < 0)
55  return ret;
56  *q = av_d2q(d, max);
57  } else {
58  av_reduce(&q->num, &q->den, q->num, q->den, max);
59  }
60 
61  return 0;
62 }
63 
64 typedef struct {
65  const char *abbr;
66  int width, height;
68 
69 typedef struct {
70  const char *abbr;
73 
74 static const VideoSizeAbbr video_size_abbrs[] = {
75  { "ntsc", 720, 480 },
76  { "pal", 720, 576 },
77  { "qntsc", 352, 240 }, /* VCD compliant NTSC */
78  { "qpal", 352, 288 }, /* VCD compliant PAL */
79  { "sntsc", 640, 480 }, /* square pixel NTSC */
80  { "spal", 768, 576 }, /* square pixel PAL */
81  { "film", 352, 240 },
82  { "ntsc-film", 352, 240 },
83  { "sqcif", 128, 96 },
84  { "qcif", 176, 144 },
85  { "cif", 352, 288 },
86  { "4cif", 704, 576 },
87  { "16cif", 1408,1152 },
88  { "qqvga", 160, 120 },
89  { "qvga", 320, 240 },
90  { "vga", 640, 480 },
91  { "svga", 800, 600 },
92  { "xga", 1024, 768 },
93  { "uxga", 1600,1200 },
94  { "qxga", 2048,1536 },
95  { "sxga", 1280,1024 },
96  { "qsxga", 2560,2048 },
97  { "hsxga", 5120,4096 },
98  { "wvga", 852, 480 },
99  { "wxga", 1366, 768 },
100  { "wsxga", 1600,1024 },
101  { "wuxga", 1920,1200 },
102  { "woxga", 2560,1600 },
103  { "wqsxga", 3200,2048 },
104  { "wquxga", 3840,2400 },
105  { "whsxga", 6400,4096 },
106  { "whuxga", 7680,4800 },
107  { "cga", 320, 200 },
108  { "ega", 640, 350 },
109  { "hd480", 852, 480 },
110  { "hd720", 1280, 720 },
111  { "hd1080", 1920,1080 },
112  { "2k", 2048,1080 }, /* Digital Cinema System Specification */
113  { "2kflat", 1998,1080 },
114  { "2kscope", 2048, 858 },
115  { "4k", 4096,2160 }, /* Digital Cinema System Specification */
116  { "4kflat", 3996,2160 },
117  { "4kscope", 4096,1716 },
118 };
119 
121  { "ntsc", { 30000, 1001 } },
122  { "pal", { 25, 1 } },
123  { "qntsc", { 30000, 1001 } }, /* VCD compliant NTSC */
124  { "qpal", { 25, 1 } }, /* VCD compliant PAL */
125  { "sntsc", { 30000, 1001 } }, /* square pixel NTSC */
126  { "spal", { 25, 1 } }, /* square pixel PAL */
127  { "film", { 24, 1 } },
128  { "ntsc-film", { 24000, 1001 } },
129 };
130 
131 int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
132 {
133  int i;
134  int n = FF_ARRAY_ELEMS(video_size_abbrs);
135  const char *p;
136  int width = 0, height = 0;
137 
138  for (i = 0; i < n; i++) {
139  if (!strcmp(video_size_abbrs[i].abbr, str)) {
140  width = video_size_abbrs[i].width;
141  height = video_size_abbrs[i].height;
142  break;
143  }
144  }
145  if (i == n) {
146  width = strtol(str, (void*)&p, 10);
147  if (*p)
148  p++;
149  height = strtol(p, (void*)&p, 10);
150 
151  /* trailing extraneous data detected, like in 123x345foobar */
152  if (*p)
153  return AVERROR(EINVAL);
154  }
155  if (width <= 0 || height <= 0)
156  return AVERROR(EINVAL);
157  *width_ptr = width;
158  *height_ptr = height;
159  return 0;
160 }
161 
162 int av_parse_video_rate(AVRational *rate, const char *arg)
163 {
164  int i, ret;
165  int n = FF_ARRAY_ELEMS(video_rate_abbrs);
166 
167  /* First, we check our abbreviation table */
168  for (i = 0; i < n; ++i)
169  if (!strcmp(video_rate_abbrs[i].abbr, arg)) {
170  *rate = video_rate_abbrs[i].rate;
171  return 0;
172  }
173 
174  /* Then, we try to parse it as fraction */
175  if ((ret = av_parse_ratio_quiet(rate, arg, 1001000)) < 0)
176  return ret;
177  if (rate->num <= 0 || rate->den <= 0)
178  return AVERROR(EINVAL);
179  return 0;
180 }
181 
182 typedef struct {
183  const char *name; ///< a string representing the name of the color
184  uint8_t rgb_color[3]; ///< RGB values for the color
185 } ColorEntry;
186 
187 static const ColorEntry color_table[] = {
188  { "AliceBlue", { 0xF0, 0xF8, 0xFF } },
189  { "AntiqueWhite", { 0xFA, 0xEB, 0xD7 } },
190  { "Aqua", { 0x00, 0xFF, 0xFF } },
191  { "Aquamarine", { 0x7F, 0xFF, 0xD4 } },
192  { "Azure", { 0xF0, 0xFF, 0xFF } },
193  { "Beige", { 0xF5, 0xF5, 0xDC } },
194  { "Bisque", { 0xFF, 0xE4, 0xC4 } },
195  { "Black", { 0x00, 0x00, 0x00 } },
196  { "BlanchedAlmond", { 0xFF, 0xEB, 0xCD } },
197  { "Blue", { 0x00, 0x00, 0xFF } },
198  { "BlueViolet", { 0x8A, 0x2B, 0xE2 } },
199  { "Brown", { 0xA5, 0x2A, 0x2A } },
200  { "BurlyWood", { 0xDE, 0xB8, 0x87 } },
201  { "CadetBlue", { 0x5F, 0x9E, 0xA0 } },
202  { "Chartreuse", { 0x7F, 0xFF, 0x00 } },
203  { "Chocolate", { 0xD2, 0x69, 0x1E } },
204  { "Coral", { 0xFF, 0x7F, 0x50 } },
205  { "CornflowerBlue", { 0x64, 0x95, 0xED } },
206  { "Cornsilk", { 0xFF, 0xF8, 0xDC } },
207  { "Crimson", { 0xDC, 0x14, 0x3C } },
208  { "Cyan", { 0x00, 0xFF, 0xFF } },
209  { "DarkBlue", { 0x00, 0x00, 0x8B } },
210  { "DarkCyan", { 0x00, 0x8B, 0x8B } },
211  { "DarkGoldenRod", { 0xB8, 0x86, 0x0B } },
212  { "DarkGray", { 0xA9, 0xA9, 0xA9 } },
213  { "DarkGreen", { 0x00, 0x64, 0x00 } },
214  { "DarkKhaki", { 0xBD, 0xB7, 0x6B } },
215  { "DarkMagenta", { 0x8B, 0x00, 0x8B } },
216  { "DarkOliveGreen", { 0x55, 0x6B, 0x2F } },
217  { "Darkorange", { 0xFF, 0x8C, 0x00 } },
218  { "DarkOrchid", { 0x99, 0x32, 0xCC } },
219  { "DarkRed", { 0x8B, 0x00, 0x00 } },
220  { "DarkSalmon", { 0xE9, 0x96, 0x7A } },
221  { "DarkSeaGreen", { 0x8F, 0xBC, 0x8F } },
222  { "DarkSlateBlue", { 0x48, 0x3D, 0x8B } },
223  { "DarkSlateGray", { 0x2F, 0x4F, 0x4F } },
224  { "DarkTurquoise", { 0x00, 0xCE, 0xD1 } },
225  { "DarkViolet", { 0x94, 0x00, 0xD3 } },
226  { "DeepPink", { 0xFF, 0x14, 0x93 } },
227  { "DeepSkyBlue", { 0x00, 0xBF, 0xFF } },
228  { "DimGray", { 0x69, 0x69, 0x69 } },
229  { "DodgerBlue", { 0x1E, 0x90, 0xFF } },
230  { "FireBrick", { 0xB2, 0x22, 0x22 } },
231  { "FloralWhite", { 0xFF, 0xFA, 0xF0 } },
232  { "ForestGreen", { 0x22, 0x8B, 0x22 } },
233  { "Fuchsia", { 0xFF, 0x00, 0xFF } },
234  { "Gainsboro", { 0xDC, 0xDC, 0xDC } },
235  { "GhostWhite", { 0xF8, 0xF8, 0xFF } },
236  { "Gold", { 0xFF, 0xD7, 0x00 } },
237  { "GoldenRod", { 0xDA, 0xA5, 0x20 } },
238  { "Gray", { 0x80, 0x80, 0x80 } },
239  { "Green", { 0x00, 0x80, 0x00 } },
240  { "GreenYellow", { 0xAD, 0xFF, 0x2F } },
241  { "HoneyDew", { 0xF0, 0xFF, 0xF0 } },
242  { "HotPink", { 0xFF, 0x69, 0xB4 } },
243  { "IndianRed", { 0xCD, 0x5C, 0x5C } },
244  { "Indigo", { 0x4B, 0x00, 0x82 } },
245  { "Ivory", { 0xFF, 0xFF, 0xF0 } },
246  { "Khaki", { 0xF0, 0xE6, 0x8C } },
247  { "Lavender", { 0xE6, 0xE6, 0xFA } },
248  { "LavenderBlush", { 0xFF, 0xF0, 0xF5 } },
249  { "LawnGreen", { 0x7C, 0xFC, 0x00 } },
250  { "LemonChiffon", { 0xFF, 0xFA, 0xCD } },
251  { "LightBlue", { 0xAD, 0xD8, 0xE6 } },
252  { "LightCoral", { 0xF0, 0x80, 0x80 } },
253  { "LightCyan", { 0xE0, 0xFF, 0xFF } },
254  { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
255  { "LightGreen", { 0x90, 0xEE, 0x90 } },
256  { "LightGrey", { 0xD3, 0xD3, 0xD3 } },
257  { "LightPink", { 0xFF, 0xB6, 0xC1 } },
258  { "LightSalmon", { 0xFF, 0xA0, 0x7A } },
259  { "LightSeaGreen", { 0x20, 0xB2, 0xAA } },
260  { "LightSkyBlue", { 0x87, 0xCE, 0xFA } },
261  { "LightSlateGray", { 0x77, 0x88, 0x99 } },
262  { "LightSteelBlue", { 0xB0, 0xC4, 0xDE } },
263  { "LightYellow", { 0xFF, 0xFF, 0xE0 } },
264  { "Lime", { 0x00, 0xFF, 0x00 } },
265  { "LimeGreen", { 0x32, 0xCD, 0x32 } },
266  { "Linen", { 0xFA, 0xF0, 0xE6 } },
267  { "Magenta", { 0xFF, 0x00, 0xFF } },
268  { "Maroon", { 0x80, 0x00, 0x00 } },
269  { "MediumAquaMarine", { 0x66, 0xCD, 0xAA } },
270  { "MediumBlue", { 0x00, 0x00, 0xCD } },
271  { "MediumOrchid", { 0xBA, 0x55, 0xD3 } },
272  { "MediumPurple", { 0x93, 0x70, 0xD8 } },
273  { "MediumSeaGreen", { 0x3C, 0xB3, 0x71 } },
274  { "MediumSlateBlue", { 0x7B, 0x68, 0xEE } },
275  { "MediumSpringGreen", { 0x00, 0xFA, 0x9A } },
276  { "MediumTurquoise", { 0x48, 0xD1, 0xCC } },
277  { "MediumVioletRed", { 0xC7, 0x15, 0x85 } },
278  { "MidnightBlue", { 0x19, 0x19, 0x70 } },
279  { "MintCream", { 0xF5, 0xFF, 0xFA } },
280  { "MistyRose", { 0xFF, 0xE4, 0xE1 } },
281  { "Moccasin", { 0xFF, 0xE4, 0xB5 } },
282  { "NavajoWhite", { 0xFF, 0xDE, 0xAD } },
283  { "Navy", { 0x00, 0x00, 0x80 } },
284  { "OldLace", { 0xFD, 0xF5, 0xE6 } },
285  { "Olive", { 0x80, 0x80, 0x00 } },
286  { "OliveDrab", { 0x6B, 0x8E, 0x23 } },
287  { "Orange", { 0xFF, 0xA5, 0x00 } },
288  { "OrangeRed", { 0xFF, 0x45, 0x00 } },
289  { "Orchid", { 0xDA, 0x70, 0xD6 } },
290  { "PaleGoldenRod", { 0xEE, 0xE8, 0xAA } },
291  { "PaleGreen", { 0x98, 0xFB, 0x98 } },
292  { "PaleTurquoise", { 0xAF, 0xEE, 0xEE } },
293  { "PaleVioletRed", { 0xD8, 0x70, 0x93 } },
294  { "PapayaWhip", { 0xFF, 0xEF, 0xD5 } },
295  { "PeachPuff", { 0xFF, 0xDA, 0xB9 } },
296  { "Peru", { 0xCD, 0x85, 0x3F } },
297  { "Pink", { 0xFF, 0xC0, 0xCB } },
298  { "Plum", { 0xDD, 0xA0, 0xDD } },
299  { "PowderBlue", { 0xB0, 0xE0, 0xE6 } },
300  { "Purple", { 0x80, 0x00, 0x80 } },
301  { "Red", { 0xFF, 0x00, 0x00 } },
302  { "RosyBrown", { 0xBC, 0x8F, 0x8F } },
303  { "RoyalBlue", { 0x41, 0x69, 0xE1 } },
304  { "SaddleBrown", { 0x8B, 0x45, 0x13 } },
305  { "Salmon", { 0xFA, 0x80, 0x72 } },
306  { "SandyBrown", { 0xF4, 0xA4, 0x60 } },
307  { "SeaGreen", { 0x2E, 0x8B, 0x57 } },
308  { "SeaShell", { 0xFF, 0xF5, 0xEE } },
309  { "Sienna", { 0xA0, 0x52, 0x2D } },
310  { "Silver", { 0xC0, 0xC0, 0xC0 } },
311  { "SkyBlue", { 0x87, 0xCE, 0xEB } },
312  { "SlateBlue", { 0x6A, 0x5A, 0xCD } },
313  { "SlateGray", { 0x70, 0x80, 0x90 } },
314  { "Snow", { 0xFF, 0xFA, 0xFA } },
315  { "SpringGreen", { 0x00, 0xFF, 0x7F } },
316  { "SteelBlue", { 0x46, 0x82, 0xB4 } },
317  { "Tan", { 0xD2, 0xB4, 0x8C } },
318  { "Teal", { 0x00, 0x80, 0x80 } },
319  { "Thistle", { 0xD8, 0xBF, 0xD8 } },
320  { "Tomato", { 0xFF, 0x63, 0x47 } },
321  { "Turquoise", { 0x40, 0xE0, 0xD0 } },
322  { "Violet", { 0xEE, 0x82, 0xEE } },
323  { "Wheat", { 0xF5, 0xDE, 0xB3 } },
324  { "White", { 0xFF, 0xFF, 0xFF } },
325  { "WhiteSmoke", { 0xF5, 0xF5, 0xF5 } },
326  { "Yellow", { 0xFF, 0xFF, 0x00 } },
327  { "YellowGreen", { 0x9A, 0xCD, 0x32 } },
328 };
329 
330 static int color_table_compare(const void *lhs, const void *rhs)
331 {
332  return av_strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
333 }
334 
335 #define ALPHA_SEP '@'
336 
337 int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen,
338  void *log_ctx)
339 {
340  char *tail, color_string2[128];
341  const ColorEntry *entry;
342  int len, hex_offset = 0;
343 
344  if (color_string[0] == '#') {
345  hex_offset = 1;
346  } else if (!strncmp(color_string, "0x", 2))
347  hex_offset = 2;
348 
349  if (slen < 0)
350  slen = strlen(color_string);
351  av_strlcpy(color_string2, color_string + hex_offset,
352  FFMIN(slen-hex_offset+1, sizeof(color_string2)));
353  if ((tail = strchr(color_string2, ALPHA_SEP)))
354  *tail++ = 0;
355  len = strlen(color_string2);
356  rgba_color[3] = 255;
357 
358  if (!av_strcasecmp(color_string2, "random") || !av_strcasecmp(color_string2, "bikeshed")) {
359  int rgba = av_get_random_seed();
360  rgba_color[0] = rgba >> 24;
361  rgba_color[1] = rgba >> 16;
362  rgba_color[2] = rgba >> 8;
363  rgba_color[3] = rgba;
364  } else if (hex_offset ||
365  strspn(color_string2, "0123456789ABCDEFabcdef") == len) {
366  char *tail;
367  unsigned int rgba = strtoul(color_string2, &tail, 16);
368 
369  if (*tail || (len != 6 && len != 8)) {
370  av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
371  return AVERROR(EINVAL);
372  }
373  if (len == 8) {
374  rgba_color[3] = rgba;
375  rgba >>= 8;
376  }
377  rgba_color[0] = rgba >> 16;
378  rgba_color[1] = rgba >> 8;
379  rgba_color[2] = rgba;
380  } else {
381  entry = bsearch(color_string2,
382  color_table,
383  FF_ARRAY_ELEMS(color_table),
384  sizeof(ColorEntry),
386  if (!entry) {
387  av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
388  return AVERROR(EINVAL);
389  }
390  memcpy(rgba_color, entry->rgb_color, 3);
391  }
392 
393  if (tail) {
394  double alpha;
395  const char *alpha_string = tail;
396  if (!strncmp(alpha_string, "0x", 2)) {
397  alpha = strtoul(alpha_string, &tail, 16);
398  } else {
399  double norm_alpha = strtod(alpha_string, &tail);
400  if (norm_alpha < 0.0 || norm_alpha > 1.0)
401  alpha = 256;
402  else
403  alpha = 255 * norm_alpha;
404  }
405 
406  if (tail == alpha_string || *tail || alpha > 255 || alpha < 0) {
407  av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n",
408  alpha_string, color_string);
409  return AVERROR(EINVAL);
410  }
411  rgba_color[3] = alpha;
412  }
413 
414  return 0;
415 }
416 
417 /* get a positive number between n_min and n_max, for a maximum length
418  of len_max. Return -1 if error. */
419 static int date_get_num(const char **pp,
420  int n_min, int n_max, int len_max)
421 {
422  int i, val, c;
423  const char *p;
424 
425  p = *pp;
426  val = 0;
427  for(i = 0; i < len_max; i++) {
428  c = *p;
429  if (!av_isdigit(c))
430  break;
431  val = (val * 10) + c - '0';
432  p++;
433  }
434  /* no number read ? */
435  if (p == *pp)
436  return -1;
437  if (val < n_min || val > n_max)
438  return -1;
439  *pp = p;
440  return val;
441 }
442 
443 char *av_small_strptime(const char *p, const char *fmt, struct tm *dt)
444 {
445  int c, val;
446 
447  for(;;) {
448  /* consume time string until a non whitespace char is found */
449  while (av_isspace(*fmt)) {
450  while (av_isspace(*p))
451  p++;
452  fmt++;
453  }
454  c = *fmt++;
455  if (c == '\0') {
456  return (char *)p;
457  } else if (c == '%') {
458  c = *fmt++;
459  switch(c) {
460  case 'H':
461  case 'J':
462  val = date_get_num(&p, 0, c == 'H' ? 23 : INT_MAX, 2);
463  if (val == -1)
464  return NULL;
465  dt->tm_hour = val;
466  break;
467  case 'M':
468  val = date_get_num(&p, 0, 59, 2);
469  if (val == -1)
470  return NULL;
471  dt->tm_min = val;
472  break;
473  case 'S':
474  val = date_get_num(&p, 0, 59, 2);
475  if (val == -1)
476  return NULL;
477  dt->tm_sec = val;
478  break;
479  case 'Y':
480  val = date_get_num(&p, 0, 9999, 4);
481  if (val == -1)
482  return NULL;
483  dt->tm_year = val - 1900;
484  break;
485  case 'm':
486  val = date_get_num(&p, 1, 12, 2);
487  if (val == -1)
488  return NULL;
489  dt->tm_mon = val - 1;
490  break;
491  case 'd':
492  val = date_get_num(&p, 1, 31, 2);
493  if (val == -1)
494  return NULL;
495  dt->tm_mday = val;
496  break;
497  case '%':
498  goto match;
499  default:
500  return NULL;
501  }
502  } else {
503  match:
504  if (c != *p)
505  return NULL;
506  p++;
507  }
508  }
509 }
510 
511 time_t av_timegm(struct tm *tm)
512 {
513  time_t t;
514 
515  int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
516 
517  if (m < 3) {
518  m += 12;
519  y--;
520  }
521 
522  t = 86400LL *
523  (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
524 
525  t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
526 
527  return t;
528 }
529 
530 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
531 {
532  const char *p, *q;
533  int64_t t;
534  time_t now;
535  struct tm dt = { 0 };
536  int today = 0, negative = 0, microseconds = 0;
537  int i;
538  static const char * const date_fmt[] = {
539  "%Y-%m-%d",
540  "%Y%m%d",
541  };
542  static const char * const time_fmt[] = {
543  "%H:%M:%S",
544  "%H%M%S",
545  };
546 
547  p = timestr;
548  q = NULL;
549  *timeval = INT64_MIN;
550  if (!duration) {
551  now = time(0);
552 
553  if (!av_strcasecmp(timestr, "now")) {
554  *timeval = (int64_t) now * 1000000;
555  return 0;
556  }
557 
558  /* parse the year-month-day part */
559  for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
560  q = av_small_strptime(p, date_fmt[i], &dt);
561  if (q)
562  break;
563  }
564 
565  /* if the year-month-day part is missing, then take the
566  * current year-month-day time */
567  if (!q) {
568  today = 1;
569  q = p;
570  }
571  p = q;
572 
573  if (*p == 'T' || *p == 't' || *p == ' ')
574  p++;
575 
576  /* parse the hour-minute-second part */
577  for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
578  q = av_small_strptime(p, time_fmt[i], &dt);
579  if (q)
580  break;
581  }
582  } else {
583  /* parse timestr as a duration */
584  if (p[0] == '-') {
585  negative = 1;
586  ++p;
587  }
588  /* parse timestr as HH:MM:SS */
589  q = av_small_strptime(p, "%J:%M:%S", &dt);
590  if (!q) {
591  /* parse timestr as MM:SS */
592  q = av_small_strptime(p, "%M:%S", &dt);
593  dt.tm_hour = 0;
594  }
595  if (!q) {
596  /* parse timestr as S+ */
597  dt.tm_sec = strtol(p, (void *)&q, 10);
598  if (q == p) /* the parsing didn't succeed */
599  return AVERROR(EINVAL);
600  dt.tm_min = 0;
601  dt.tm_hour = 0;
602  }
603  }
604 
605  /* Now we have all the fields that we can get */
606  if (!q)
607  return AVERROR(EINVAL);
608 
609  /* parse the .m... part */
610  if (*q == '.') {
611  int n;
612  q++;
613  for (n = 100000; n >= 1; n /= 10, q++) {
614  if (!av_isdigit(*q))
615  break;
616  microseconds += n * (*q - '0');
617  }
618  while (av_isdigit(*q))
619  q++;
620  }
621 
622  if (duration) {
623  t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
624  } else {
625  int is_utc = *q == 'Z' || *q == 'z';
626  q += is_utc;
627  if (today) { /* fill in today's date */
628  struct tm dt2 = is_utc ? *gmtime(&now) : *localtime(&now);
629  dt2.tm_hour = dt.tm_hour;
630  dt2.tm_min = dt.tm_min;
631  dt2.tm_sec = dt.tm_sec;
632  dt = dt2;
633  }
634  t = is_utc ? av_timegm(&dt) : mktime(&dt);
635  }
636 
637  /* Check that we are at the end of the string */
638  if (*q)
639  return AVERROR(EINVAL);
640 
641  t *= 1000000;
642  t += microseconds;
643  *timeval = negative ? -t : t;
644  return 0;
645 }
646 
647 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
648 {
649  const char *p;
650  char tag[128], *q;
651 
652  p = info;
653  if (*p == '?')
654  p++;
655  for(;;) {
656  q = tag;
657  while (*p != '\0' && *p != '=' && *p != '&') {
658  if ((q - tag) < sizeof(tag) - 1)
659  *q++ = *p;
660  p++;
661  }
662  *q = '\0';
663  q = arg;
664  if (*p == '=') {
665  p++;
666  while (*p != '&' && *p != '\0') {
667  if ((q - arg) < arg_size - 1) {
668  if (*p == '+')
669  *q++ = ' ';
670  else
671  *q++ = *p;
672  }
673  p++;
674  }
675  }
676  *q = '\0';
677  if (!strcmp(tag, tag1))
678  return 1;
679  if (*p != '&')
680  break;
681  p++;
682  }
683  return 0;
684 }
685 
686 #ifdef TEST
687 
688 static uint32_t randomv = MKTAG('L','A','V','U');
689 
690 static uint32_t av_get_random_seed_deterministic(void)
691 {
692  return randomv = randomv * 1664525 + 1013904223;
693 }
694 
695 int main(void)
696 {
697  printf("Testing av_parse_video_rate()\n");
698  {
699  int i;
700  static const char *const rates[] = {
701  "-inf",
702  "inf",
703  "nan",
704  "123/0",
705  "-123 / 0",
706  "",
707  "/",
708  " 123 / 321",
709  "foo/foo",
710  "foo/1",
711  "1/foo",
712  "0/0",
713  "/0",
714  "1/",
715  "1",
716  "0",
717  "-123/123",
718  "-foo",
719  "123.23",
720  ".23",
721  "-.23",
722  "-0.234",
723  "-0.0000001",
724  " 21332.2324 ",
725  " -21332.2324 ",
726  };
727 
728  for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
729  int ret;
730  AVRational q = { 0, 0 };
731  ret = av_parse_video_rate(&q, rates[i]);
732  printf("'%s' -> %d/%d %s\n",
733  rates[i], q.num, q.den, ret ? "ERROR" : "OK");
734  }
735  }
736 
737  printf("\nTesting av_parse_color()\n");
738  {
739  int i;
740  uint8_t rgba[4];
741  static const char *const color_names[] = {
742  "bikeshed",
743  "RaNdOm",
744  "foo",
745  "red",
746  "Red ",
747  "RED",
748  "Violet",
749  "Yellow",
750  "Red",
751  "0x000000",
752  "0x0000000",
753  "0xff000000",
754  "0x3e34ff",
755  "0x3e34ffaa",
756  "0xffXXee",
757  "0xfoobar",
758  "0xffffeeeeeeee",
759  "#ff0000",
760  "#ffXX00",
761  "ff0000",
762  "ffXX00",
763  "red@foo",
764  "random@10",
765  "0xff0000@1.0",
766  "red@",
767  "red@0xfff",
768  "red@0xf",
769  "red@2",
770  "red@0.1",
771  "red@-1",
772  "red@0.5",
773  "red@1.0",
774  "red@256",
775  "red@10foo",
776  "red@-1.0",
777  "red@-0.0",
778  };
779 
781 
782  for (i = 0; i < FF_ARRAY_ELEMS(color_names); i++) {
783  if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
784  printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
785  color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
786  else
787  printf("%s -> error\n", color_names[i]);
788  }
789  }
790 
791  printf("\nTesting av_small_strptime()\n");
792  {
793  int i;
794  struct tm tm = { 0 };
795  struct fmt_timespec_entry {
796  const char *fmt, *timespec;
797  } fmt_timespec_entries[] = {
798  { "%Y-%m-%d", "2012-12-21" },
799  { "%Y - %m - %d", "2012-12-21" },
800  { "%Y-%m-%d %H:%M:%S", "2012-12-21 20:12:21" },
801  { " %Y - %m - %d %H : %M : %S", " 2012 - 12 - 21 20 : 12 : 21" },
802  };
803 
805  for (i = 0; i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) {
806  char *p;
807  struct fmt_timespec_entry *e = &fmt_timespec_entries[i];
808  printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec);
809  p = av_small_strptime(e->timespec, e->fmt, &tm);
810  if (p) {
811  printf("%04d-%02d-%2d %02d:%02d:%02d\n",
812  1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
813  tm.tm_hour, tm.tm_min, tm.tm_sec);
814  } else {
815  printf("error\n");
816  }
817  }
818  }
819 
820  printf("\nTesting av_parse_time()\n");
821  {
822  int i;
823  int64_t tv;
824  time_t tvi;
825  struct tm *tm;
826  static char tzstr[] = "TZ=CET-1";
827  const char *time_string[] = {
828  "now",
829  "12:35:46",
830  "2000-12-20 0:02:47.5z",
831  "2000-12-20T010247.6",
832  };
833  const char *duration_string[] = {
834  "2:34:56.79",
835  "-1:23:45.67",
836  "42.1729",
837  "-1729.42",
838  "12:34",
839  };
840 
842  putenv(tzstr);
843  printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n");
844  for (i = 0; i < FF_ARRAY_ELEMS(time_string); i++) {
845  printf("%-24s -> ", time_string[i]);
846  if (av_parse_time(&tv, time_string[i], 0)) {
847  printf("error\n");
848  } else {
849  tvi = tv / 1000000;
850  tm = gmtime(&tvi);
851  printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
852  tv / 1000000, (int)(tv % 1000000),
853  tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
854  tm->tm_hour, tm->tm_min, tm->tm_sec);
855  }
856  }
857  for (i = 0; i < FF_ARRAY_ELEMS(duration_string); i++) {
858  printf("%-24s -> ", duration_string[i]);
859  if (av_parse_time(&tv, duration_string[i], 1)) {
860  printf("error\n");
861  } else {
862  printf("%+21"PRIi64"\n", tv);
863  }
864  }
865  }
866 
867  return 0;
868 }
869 
870 #endif /* TEST */