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