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