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