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  char *o;
617  /* parse timestr as S+ */
618  dt.tm_sec = strtol(p, &o, 10);
619  if (o == p) /* the parsing didn't succeed */
620  return AVERROR(EINVAL);
621  dt.tm_min = 0;
622  dt.tm_hour = 0;
623  q = o;
624  }
625  }
626 
627  /* Now we have all the fields that we can get */
628  if (!q)
629  return AVERROR(EINVAL);
630 
631  /* parse the .m... part */
632  if (*q == '.') {
633  int n;
634  q++;
635  for (n = 100000; n >= 1; n /= 10, q++) {
636  if (!av_isdigit(*q))
637  break;
638  microseconds += n * (*q - '0');
639  }
640  while (av_isdigit(*q))
641  q++;
642  }
643 
644  if (duration) {
645  t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
646  } else {
647  int is_utc = *q == 'Z' || *q == 'z';
648  q += is_utc;
649  if (today) { /* fill in today's date */
650  struct tm dt2 = is_utc ? *gmtime(&now) : *localtime(&now);
651  dt2.tm_hour = dt.tm_hour;
652  dt2.tm_min = dt.tm_min;
653  dt2.tm_sec = dt.tm_sec;
654  dt = dt2;
655  }
656  t = is_utc ? av_timegm(&dt) : mktime(&dt);
657  }
658 
659  /* Check that we are at the end of the string */
660  if (*q)
661  return AVERROR(EINVAL);
662 
663  t *= 1000000;
664  t += microseconds;
665  *timeval = negative ? -t : t;
666  return 0;
667 }
668 
669 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
670 {
671  const char *p;
672  char tag[128], *q;
673 
674  p = info;
675  if (*p == '?')
676  p++;
677  for(;;) {
678  q = tag;
679  while (*p != '\0' && *p != '=' && *p != '&') {
680  if ((q - tag) < sizeof(tag) - 1)
681  *q++ = *p;
682  p++;
683  }
684  *q = '\0';
685  q = arg;
686  if (*p == '=') {
687  p++;
688  while (*p != '&' && *p != '\0') {
689  if ((q - arg) < arg_size - 1) {
690  if (*p == '+')
691  *q++ = ' ';
692  else
693  *q++ = *p;
694  }
695  p++;
696  }
697  }
698  *q = '\0';
699  if (!strcmp(tag, tag1))
700  return 1;
701  if (*p != '&')
702  break;
703  p++;
704  }
705  return 0;
706 }
707 
708 #ifdef TEST
709 
710 static uint32_t randomv = MKTAG('L','A','V','U');
711 
712 static uint32_t av_get_random_seed_deterministic(void)
713 {
714  return randomv = randomv * 1664525 + 1013904223;
715 }
716 
717 int main(void)
718 {
719  printf("Testing av_parse_video_rate()\n");
720  {
721  int i;
722  static const char *const rates[] = {
723  "-inf",
724  "inf",
725  "nan",
726  "123/0",
727  "-123 / 0",
728  "",
729  "/",
730  " 123 / 321",
731  "foo/foo",
732  "foo/1",
733  "1/foo",
734  "0/0",
735  "/0",
736  "1/",
737  "1",
738  "0",
739  "-123/123",
740  "-foo",
741  "123.23",
742  ".23",
743  "-.23",
744  "-0.234",
745  "-0.0000001",
746  " 21332.2324 ",
747  " -21332.2324 ",
748  };
749 
750  for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
751  int ret;
752  AVRational q = { 0, 0 };
753  ret = av_parse_video_rate(&q, rates[i]);
754  printf("'%s' -> %d/%d %s\n",
755  rates[i], q.num, q.den, ret ? "ERROR" : "OK");
756  }
757  }
758 
759  printf("\nTesting av_parse_color()\n");
760  {
761  int i;
762  uint8_t rgba[4];
763  static const char *const color_names[] = {
764  "bikeshed",
765  "RaNdOm",
766  "foo",
767  "red",
768  "Red ",
769  "RED",
770  "Violet",
771  "Yellow",
772  "Red",
773  "0x000000",
774  "0x0000000",
775  "0xff000000",
776  "0x3e34ff",
777  "0x3e34ffaa",
778  "0xffXXee",
779  "0xfoobar",
780  "0xffffeeeeeeee",
781  "#ff0000",
782  "#ffXX00",
783  "ff0000",
784  "ffXX00",
785  "red@foo",
786  "random@10",
787  "0xff0000@1.0",
788  "red@",
789  "red@0xfff",
790  "red@0xf",
791  "red@2",
792  "red@0.1",
793  "red@-1",
794  "red@0.5",
795  "red@1.0",
796  "red@256",
797  "red@10foo",
798  "red@-1.0",
799  "red@-0.0",
800  };
801 
803 
804  for (i = 0; i < FF_ARRAY_ELEMS(color_names); i++) {
805  if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
806  printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
807  color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
808  else
809  printf("%s -> error\n", color_names[i]);
810  }
811  }
812 
813  printf("\nTesting av_small_strptime()\n");
814  {
815  int i;
816  struct tm tm = { 0 };
817  struct fmt_timespec_entry {
818  const char *fmt, *timespec;
819  } fmt_timespec_entries[] = {
820  { "%Y-%m-%d", "2012-12-21" },
821  { "%Y - %m - %d", "2012-12-21" },
822  { "%Y-%m-%d %H:%M:%S", "2012-12-21 20:12:21" },
823  { " %Y - %m - %d %H : %M : %S", " 2012 - 12 - 21 20 : 12 : 21" },
824  };
825 
827  for (i = 0; i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) {
828  char *p;
829  struct fmt_timespec_entry *e = &fmt_timespec_entries[i];
830  printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec);
831  p = av_small_strptime(e->timespec, e->fmt, &tm);
832  if (p) {
833  printf("%04d-%02d-%2d %02d:%02d:%02d\n",
834  1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
835  tm.tm_hour, tm.tm_min, tm.tm_sec);
836  } else {
837  printf("error\n");
838  }
839  }
840  }
841 
842  printf("\nTesting av_parse_time()\n");
843  {
844  int i;
845  int64_t tv;
846  time_t tvi;
847  struct tm *tm;
848  static char tzstr[] = "TZ=CET-1";
849  static const char * const time_string[] = {
850  "now",
851  "12:35:46",
852  "2000-12-20 0:02:47.5z",
853  "2000-12-20T010247.6",
854  };
855  static const char * const duration_string[] = {
856  "2:34:56.79",
857  "-1:23:45.67",
858  "42.1729",
859  "-1729.42",
860  "12:34",
861  };
862 
864  putenv(tzstr);
865  printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n");
866  for (i = 0; i < FF_ARRAY_ELEMS(time_string); i++) {
867  printf("%-24s -> ", time_string[i]);
868  if (av_parse_time(&tv, time_string[i], 0)) {
869  printf("error\n");
870  } else {
871  tvi = tv / 1000000;
872  tm = gmtime(&tvi);
873  printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
874  tv / 1000000, (int)(tv % 1000000),
875  tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
876  tm->tm_hour, tm->tm_min, tm->tm_sec);
877  }
878  }
879  for (i = 0; i < FF_ARRAY_ELEMS(duration_string); i++) {
880  printf("%-24s -> ", duration_string[i]);
881  if (av_parse_time(&tv, duration_string[i], 1)) {
882  printf("error\n");
883  } else {
884  printf("%+21"PRIi64"\n", tv);
885  }
886  }
887  }
888 
889  return 0;
890 }
891 
892 #endif /* TEST */