vaapi: add support for global-alpha subpictures.
[hwdecode-demos:vj-hwdecode-demos.git] / src / common.c
1 /*
2  *  common.c - Common code
3  *
4  *  hwdecode-demos (C) 2009-2010 Splitted-Desktop Systems
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define _GNU_SOURCE 1 /* strtof_l() */
22 #include "sysdeps.h"
23 #include "common.h"
24 #include "utils.h"
25 #include <strings.h> /* strcasecmp() [POSIX.1-2001] */
26 #include <stddef.h>
27 #include <stdarg.h>
28 #include <locale.h>
29 #include <errno.h>
30
31 #ifdef USE_VAAPI
32 #include "vaapi.h"
33 #include "vaapi_compat.h"
34 #endif
35
36 #define DEBUG 1
37 #include "debug.h"
38
39 #ifndef USE_FFMPEG
40 #ifdef USE_VAAPI
41 #define HWACCEL_DEFAULT HWACCEL_VAAPI
42 #endif
43 #ifdef USE_VDPAU
44 #define HWACCEL_DEFAULT HWACCEL_VDPAU
45 #endif
46 #ifdef USE_XVBA
47 #define HWACCEL_DEFAULT HWACCEL_XVBA
48 #endif
49 #endif
50 #ifndef HWACCEL_DEFAULT
51 #define HWACCEL_DEFAULT HWACCEL_NONE
52 #endif
53
54 static CommonContext g_common_context = {
55     .hwaccel_type       = HWACCEL_DEFAULT,
56     .display_type       = DISPLAY_X11,
57     .window_size.width  = 640,
58     .window_size.height = 480,
59     .rotation           = ROTATION_NONE,
60     .genimage_type      = GENIMAGE_AUTO,
61     .getimage_mode      = GETIMAGE_NONE,
62     .getimage_format    = 0,
63     .putimage_mode      = PUTIMAGE_NONE,
64     .putimage_format    = 0,
65 };
66
67 CommonContext *common_get_context(void)
68 {
69     return &g_common_context;
70 }
71
72 static inline void ensure_bounds(Rectangle *r, unsigned int w, unsigned int h)
73 {
74     if (r->x < 0)
75         r->x = 0;
76     if (r->y < 0)
77         r->y = 0;
78     if (r->width > w - r->x)
79         r->width = w - r->x;
80     if (r->height > h - r->y)
81         r->height = h - r->y;
82 }
83
84 int common_init_decoder(unsigned int picture_width, unsigned int picture_height)
85 {
86     CommonContext * const common = common_get_context();
87
88     D(bug("Decoded surface size: %ux%u\n", picture_width, picture_height));
89
90     if (common->putimage_size.width == 0 ||
91         common->putimage_size.height == 0) {
92         common->putimage_size.width  = picture_width;
93         common->putimage_size.height = picture_height;
94     }
95
96 #if USE_VAAPI
97     /* vaPutSurface() source region */
98     if (common->use_vaapi_putsurface_source_rect)
99         ensure_bounds(&common->vaapi_putsurface_source_rect,
100                       picture_width,
101                       picture_height);
102
103     /* vaAssociateSubpicture() source region */
104     if (common->use_vaapi_subpicture_source_rect)
105         ensure_bounds(&common->vaapi_subpicture_source_rect,
106                       common->putimage_size.width,
107                       common->putimage_size.height);
108
109     /* vaAssociateSubpicture() target region */
110     if (common->use_vaapi_subpicture_target_rect) {
111         if (common->use_vaapi_subpicture_flags &&
112             (common->vaapi_subpicture_flags & VA_SUBPICTURE_DESTINATION_IS_SCREEN_COORD))
113             ensure_bounds(&common->vaapi_subpicture_target_rect,
114                           common->window_size.width,
115                           common->window_size.height);
116         else
117             ensure_bounds(&common->vaapi_subpicture_target_rect,
118                           picture_width,
119                           picture_height);
120     }
121 #endif
122     return 0;
123 }
124
125 int common_display(void)
126 {
127     printf("press any key to exit\n");
128     getchar();
129     return 0;
130 }
131
132 enum HWAccelType hwaccel_type(void)
133 {
134     return common_get_context()->hwaccel_type;
135 }
136
137 enum DisplayType display_type(void)
138 {
139     return common_get_context()->display_type;
140 }
141
142 enum GetImageMode getimage_mode(void)
143 {
144     return common_get_context()->getimage_mode;
145 }
146
147 uint32_t getimage_format(void)
148 {
149     return common_get_context()->getimage_format;
150 }
151
152 enum PutImageMode putimage_mode(void)
153 {
154     return common_get_context()->putimage_mode;
155 }
156
157 uint32_t putimage_format(void)
158 {
159     return common_get_context()->putimage_format;
160 }
161
162 typedef struct {
163     unsigned int value;
164     const char  *str;
165 } map_t;
166
167 static const map_t map_hwaccel_types[] = {
168     { HWACCEL_NONE,             "none"          },
169     { HWACCEL_VAAPI,            "vaapi"         },
170     { HWACCEL_VDPAU,            "vdpau"         },
171     { HWACCEL_XVBA,             "xvba"          },
172     { 0, }
173 };
174
175 static const map_t map_display_types[] = {
176     { DISPLAY_X11,              "x11"           },
177 #if USE_GLX
178     { DISPLAY_GLX,              "glx"           },
179 #endif
180     { 0, }
181 };
182
183 static const map_t map_rotation_modes[] = {
184     { ROTATION_NONE,            "none"          },
185     { ROTATION_90,              "90"            },
186     { ROTATION_180,             "180"           },
187     { ROTATION_270,             "270"           },
188     { 0, }
189 };
190
191 static const map_t map_genimage_types[] = {
192     { GENIMAGE_AUTO,            "auto"          },
193     { GENIMAGE_RECTS,           "rects"         },
194     { GENIMAGE_RGB_RECTS,       "rgb-rects"     },
195     { GENIMAGE_FLOWERS,         "flowers"       },
196     { 0, }
197 };
198
199 static const map_t map_getimage_modes[] = {
200     { GETIMAGE_NONE,            "none"          },
201     { GETIMAGE_FROM_VIDEO,      "video"         },
202     { GETIMAGE_FROM_OUTPUT,     "output"        },
203     { GETIMAGE_FROM_PIXMAP,     "pixmap"        },
204     { 0, }
205 };
206
207 static const map_t map_putimage_modes[] = {
208     { PUTIMAGE_NONE,            "none"          },
209     { PUTIMAGE_OVERRIDE,        "override"      },
210     { PUTIMAGE_BLEND,           "blend"         },
211     { 0, }
212 };
213
214 static const map_t map_image_formats[] = {
215     { IMAGE_NV12,               "nv12"          },
216     { IMAGE_YV12,               "yv12"          },
217     { IMAGE_IYUV,               "iyuv"          },
218     { IMAGE_I420,               "i420"          },
219     { IMAGE_AYUV,               "ayuv"          },
220     { IMAGE_UYVY,               "uyvy"          },
221     { IMAGE_YUY2,               "yuy2"          },
222     { IMAGE_YUYV,               "yuyv"          },
223     { IMAGE_RGB32,              "rgb32"         },
224     { IMAGE_ARGB,               "argb"          },
225     { IMAGE_BGRA,               "bgra"          },
226     { IMAGE_RGBA,               "rgba"          },
227     { IMAGE_ABGR,               "abgr"          },
228     { 0, }
229 };
230
231 static const map_t map_texture_formats[] = {
232     { IMAGE_RGBA,               "rgba"          },
233     { IMAGE_BGRA,               "bgra"          },
234     { 0, }
235 };
236
237 static const map_t map_texture_targets[] = {
238     { TEXTURE_TARGET_2D,        "2d"            },
239     { TEXTURE_TARGET_RECT,      "rect"          },
240     { 0, }
241 };
242
243 static const map_t map_vaapi_putsurface_flags[] = {
244 #ifdef USE_VAAPI
245     { VA_FRAME_PICTURE,                 "frame"                 },
246     { VA_TOP_FIELD,                     "top-field"             },
247     { VA_BOTTOM_FIELD,                  "bottom-field"          },
248     { VA_CLEAR_DRAWABLE,                "clear-drawable"        },
249 #ifdef VA_SRC_BT601
250     { VA_SRC_BT601,                     "bt.601"                },
251 #endif
252 #ifdef VA_SRC_BT709
253     { VA_SRC_BT709,                     "bt.709"                },
254 #endif
255 #ifdef VA_SRC_SMPTE_240
256     { VA_SRC_SMPTE_240,                 "smpte.240"             },
257 #endif
258 #ifdef VA_FILTER_SCALING_DEFAULT
259     { VA_FILTER_SCALING_DEFAULT,        "scaling_default"       },
260 #endif
261 #ifdef VA_FILTER_SCALING_FAST
262     { VA_FILTER_SCALING_FAST,           "scaling_fast"          },
263 #endif
264 #ifdef VA_FILTER_SCALING_HQ
265     { VA_FILTER_SCALING_HQ,             "scaling_hq"            },
266 #endif
267 #ifdef VA_FILTER_SCALING_NL_ANAMORPHIC
268     { VA_FILTER_SCALING_NL_ANAMORPHIC,  "scaling_nla"           },
269 #endif
270 #endif
271     { 0, }
272 };
273
274 static const map_t map_vaapi_subpicture_flags[] = {
275 #ifdef USE_VAAPI
276     { VA_SUBPICTURE_CHROMA_KEYING,                  "chroma-key"        },
277     { VA_SUBPICTURE_GLOBAL_ALPHA,                   "global-alpha"      },
278 #ifdef VA_SUBPICTURE_DESTINATION_IS_SCREEN_COORD
279     { VA_SUBPICTURE_DESTINATION_IS_SCREEN_COORD,    "screen-coords"     },
280 #endif
281 #endif
282     { 0, }
283 };
284
285 static const char *map_get_string(const map_t m[], uint32_t value)
286 {
287     unsigned int i;
288     for (i = 0; m[i].str; i++) {
289         if (m[i].value == value)
290             return m[i].str;
291     }
292     return "<unknown>";
293 }
294
295 static int map_get_value(const map_t m[], const char *str, uint32_t *value)
296 {
297     unsigned int i;
298     for (i = 0; m[i].str; i++) {
299         if (strcasecmp(m[i].str, str) == 0) {
300             *value = m[i].value;
301             return 1;
302         }
303     }
304     return 0;
305 }
306
307 static void error(const char *format, ...)
308 {
309     va_list args;
310
311     va_start(args, format);
312     fprintf(stderr, "ERROR: ");
313     vfprintf(stderr, format, args);
314     fprintf(stderr, "\n");
315     va_end(args);
316     exit(1);
317 }
318
319 static void append_cliprect(int x, int y, unsigned int w, unsigned int h)
320 {
321     CommonContext * const common = common_get_context();
322     Rectangle *r;
323
324     r = fast_realloc(common->cliprects,
325                      &common->cliprects_size,
326                      sizeof(r[0]) * (1 + common->cliprects_count));
327     if (!r)
328         return;
329
330     common->cliprects    = r;
331     common->use_clipping = 1;
332
333     r = &r[common->cliprects_count++];
334     r->x      = x;
335     r->y      = y;
336     r->width  = w;
337     r->height = h;
338 }
339
340 typedef enum {
341     OPT_TYPE_UINT = 1,
342     OPT_TYPE_FLOAT,
343     OPT_TYPE_ENUM,
344     OPT_TYPE_STRUCT,
345     OPT_TYPE_FLAGS, /* separator is ':' */
346 } opt_type_t;
347
348 enum {
349     OPT_FLAG_NEGATE           = 1 << 0, /* accept --no- form args */
350     OPT_FLAG_OPTIONAL_SUBARG  = 1 << 1, /* option sub-argument is optional */
351     OPT_FLAG_DEPRECATED       = 1 << 2, /* deprecated option */
352 };
353
354 typedef struct opt opt_t;
355
356 typedef int (*opt_subparse_func_t)(const char *arg, const opt_t *opt);
357
358 struct opt {
359     const char         *name;
360     const char         *desc;
361     opt_type_t          type;
362     unsigned int        flags;
363     void               *var;
364     unsigned int       *varflag;
365     unsigned int        value;
366     const map_t        *enum_map;
367     opt_subparse_func_t subparse_func;
368 };
369
370 static int get_size(const char *arg, Size *s, unsigned int *pflag)
371 {
372     unsigned int w, h;
373
374     if (sscanf(arg, "%ux%u", &w, &h) == 2) {
375         s->width  = w;
376         s->height = h;
377         if (pflag)
378             *pflag = 1;
379         return 0;
380     }
381     return -1;
382 }
383
384 static int get_rect(const char *arg, Rectangle *r, unsigned int *pflag)
385 {
386     int x, y;
387     unsigned int w, h;
388     Size size;
389
390     if (get_size(arg, &size, NULL) == 0) {
391         r->x      = 0;
392         r->y      = 0;
393         r->width  = size.width;
394         r->height = size.height;
395         if (pflag)
396             *pflag = 1;
397         return 0;
398     }
399     if (sscanf(arg, "(%d,%d):%ux%u", &x, &y, &w, &h) == 4 ||
400         sscanf(arg, "%d,%d:%ux%u",   &x, &y, &w, &h) == 4) {
401         r->x      = x;
402         r->y      = y;
403         r->width  = w;
404         r->height = h;
405         if (pflag)
406             *pflag = 1;
407         return 0;
408     }
409     return -1;
410 }
411
412 static int get_color(const char *arg, unsigned int *pcolor, unsigned int *pflag)
413 {
414     char *end;
415     unsigned long v;
416
417     v = strtoul(arg, &end, 16);
418     if (*arg != '\0' && end && *end == '\0') {
419         *pcolor = v;
420         if (pflag)
421             *pflag = 1;
422         return 0;
423     }
424     return -1;
425 }
426
427 static int opt_subparse_float(const char *arg, const opt_t *opt)
428 {
429     float * const pval = opt->var;
430     float v;
431     char *end_ptr;
432
433     static locale_t C_locale = NULL;
434
435     if (!C_locale) {
436         C_locale = newlocale(LC_ALL_MASK, "C", NULL);
437         if (!C_locale)
438             return -1;
439     }
440
441     assert(pval);
442
443     errno = 0;
444     v = strtof_l(arg, &end_ptr, C_locale);
445     if (end_ptr == arg || errno == ERANGE)
446         return -1;
447     *pval = v;
448     return 0;
449 }
450
451 static int opt_subparse_size(const char *arg, const opt_t *opt)
452 {
453     return get_size(arg, opt->var, opt->varflag);
454 }
455
456 static int opt_subparse_rect(const char *arg, const opt_t *opt)
457 {
458     return get_rect(arg, opt->var, opt->varflag);
459 }
460
461 static int opt_subparse_color(const char *arg, const opt_t *opt)
462 {
463     return get_color(arg, opt->var, opt->varflag);
464 }
465
466 static int opt_subparse_cliprect(const char *arg, const opt_t *opt)
467 {
468     Rectangle r;
469
470     if (get_rect(arg, &r, NULL) < 0)
471         return -1;
472
473     append_cliprect(r.x, r.y, r.width, r.height);
474     return 0;
475 }
476
477 static int opt_subparse_enum(const char *arg, const opt_t *opt)
478 {
479     uint32_t v;
480     int * const pval = opt->var;
481
482     assert(pval);
483     assert(opt->enum_map);
484     if (map_get_value(opt->enum_map, arg, &v)) {
485         *pval = v;
486         return 0;
487     }
488     return -1;
489 }
490
491 static int opt_subparse_flags(const char *arg, const opt_t *opt)
492 {
493     unsigned int * const pval = opt->var;
494     const char *str;
495     unsigned int flags = 0;
496     char flag_str[256];
497     uint32_t v;
498
499     assert(pval);
500     assert(opt->enum_map);
501     do {
502         str = strchr(arg, ':');
503         if (str) {
504             const int len = str - arg;
505             if (len >= sizeof(flag_str))
506                 return -1;
507             strncpy(flag_str, arg, len);
508             flag_str[len] = '\0';
509             ++str;
510         }
511         else
512             strcpy(flag_str, arg);
513         if (!map_get_value(opt->enum_map, flag_str, &v))
514             return -1;
515         flags |= v;
516         arg = str;
517     } while (arg && *arg);
518     if (pval)
519         *pval = flags;
520     return 0;
521 }
522
523 #define UINT_VALUE(VAR, VALUE)                  \
524     .type          = OPT_TYPE_UINT,             \
525     .var           = &g_common_context.VAR,     \
526     .value         = VALUE
527
528 #define BOOL_VALUE(VAR)                         \
529     UINT_VALUE(VAR, 1),                         \
530     .flags         = OPT_FLAG_NEGATE
531
532 #define FLOAT_VALUE(VAR, VALUE)                 \
533     .type          = OPT_TYPE_FLOAT,            \
534     .var           = &g_common_context.VAR,     \
535     .value         = VALUE
536
537 #define ENUM_VALUE(VAR, MAP, VALUE)             \
538     .type          = OPT_TYPE_ENUM,             \
539     .var           = &g_common_context.VAR,     \
540     .value         = VALUE,                     \
541     .enum_map      = map_##MAP
542
543 #define FLAGS_VALUE(VAR, MAP, VALUE)            \
544     .type          = OPT_TYPE_FLAGS,            \
545     .var           = &g_common_context.VAR,     \
546     .value         = VALUE,                     \
547     .enum_map      = map_##MAP,                 \
548     .varflag       = &g_common_context.use_##VAR
549
550 #define STRUCT_VALUE_RAW(FUNC)                  \
551     .type          = OPT_TYPE_STRUCT,           \
552     .subparse_func = opt_subparse_##FUNC
553
554 #define STRUCT_VALUE(FUNC, VAR)                 \
555     STRUCT_VALUE_RAW(FUNC),                     \
556     .var           = &g_common_context.VAR
557
558 #define STRUCT_VALUE_WITH_FLAG(FUNC, VAR)       \
559     STRUCT_VALUE(FUNC, VAR),                    \
560     .varflag       = &g_common_context.use_##VAR
561
562 static const opt_t g_options[] = {
563     { /* Specify the size of the toplevel window */
564       "size",
565       "Specify the size of the toplevel window",
566       STRUCT_VALUE(size, window_size),
567     },
568     { /* Use X11 windowing system */
569       "x11",
570       "Use X11 windowing system",
571       UINT_VALUE(display_type, DISPLAY_X11),
572     },
573 #if USE_GLX
574     { /* Use OpenGL/X11 rendering */
575       "glx",
576       "Use OpenGL/X11 rendering",
577       UINT_VALUE(display_type, DISPLAY_GLX),
578     },
579 #endif
580     { /* Select the windowing system: "x11", "glx" */
581       "display",
582       "Select the windowing system",
583       ENUM_VALUE(display_type, display_types, 0),
584     },
585     { /* Allow rendering of the decoded video frame into a child window */
586       "subwindow",
587       "Allow rendering of the decoded video frame into a child window",
588       BOOL_VALUE(use_subwindow_rect),
589     },
590     { /* Specify the location and size of the child window */
591       "subwindow-rect",
592       "Specify the location and size of the child window",
593       STRUCT_VALUE_WITH_FLAG(rect, subwindow_rect),
594     },
595     { /* Select the display rotation mode: 0 ("none"), 90, 180, 270 */
596       "rotation",
597       "Select the rotation mode",
598       ENUM_VALUE(rotation, rotation_modes, ROTATION_NONE),
599     },
600     { /* Select type of generated image: "rects", "rgb-rects", "flowers" */
601       "genimage",
602       "Select type of generated image",
603       ENUM_VALUE(genimage_type, genimage_types, 0),
604     },
605     { /* Download the decoded frame from an HW video surface */
606       "getimage",
607       "Download the decoded frame from an HW video surface",
608       ENUM_VALUE(getimage_mode, getimage_modes, GETIMAGE_FROM_VIDEO),
609       .flags = OPT_FLAG_NEGATE | OPT_FLAG_OPTIONAL_SUBARG,
610     },
611     { /* Specify the target image format used for "GetImage" demos */
612       "getimage-format",
613       "Specify the target image format used for \"GetImage\" demos",
614       ENUM_VALUE(getimage_format, image_formats, 0),
615     },
616     { /* Allow vaGetImage() from a specific region */
617       "getimage-rect",
618       "Allow vaGetImage() from a specific region",
619       STRUCT_VALUE_WITH_FLAG(rect, getimage_rect),
620     },
621     { /* Upload a generated image to a HW video surface */
622       "putimage",
623       "Upload a generated image to a HW video surface",
624       ENUM_VALUE(putimage_mode, putimage_modes, PUTIMAGE_OVERRIDE),
625       .flags = OPT_FLAG_NEGATE | OPT_FLAG_OPTIONAL_SUBARG,
626     },
627     { /* Specify the source image format used for "PutImage" demos */
628       "putimage-format",
629       "Specify the source image format used for \"PutImage\" demos",
630       ENUM_VALUE(putimage_format, image_formats, 0),
631     },
632     { /* Specify the source image size used for "PutImage" demos */
633       "putimage-size",
634       "Specify the source image size used for \"PutImage\" demos",
635       STRUCT_VALUE(size, putimage_size),
636     },
637 #if USE_FFMPEG
638     { /* Select the HW acceleration API. e.g. for FFmpeg demos */
639       "hwaccel",
640       "Select the HW acceleration API. e.g. for FFmpeg demos",
641       ENUM_VALUE(hwaccel_type, hwaccel_types, HWACCEL_DEFAULT),
642       .flags = OPT_FLAG_NEGATE,
643     },
644 #endif
645     { /* Enable clipping the video surface by several predefined rectangles */
646       "clipping",
647       "Enable clipping the video surface by several predefined rectangles",
648       BOOL_VALUE(use_clipping),
649     },
650     { /* Specify an additional clip rectangle of the form X,Y:WxH */
651       "cliprect",
652       "Specify an additional clip rectangle",
653       STRUCT_VALUE_RAW(cliprect),
654     },
655     { /* Render a single surface to multiple locations within the same window */
656       "multi-rendering",
657       "Render a single surface to multiple locations within the same window",
658       BOOL_VALUE(multi_rendering),
659     },
660 #if USE_VAAPI
661     { /* Allow use of vaDeriveImage() in GetImage or PutImage tests */
662       "vaapi-derive-image",
663       "Allow use of vaDeriveImage() in GetInmage or PutImage tests (default)",
664       BOOL_VALUE(vaapi_derive_image),
665     },
666     { /* Specify vaPutSurface() source rectangle */
667       "vaapi-putsurface-source-rect",
668       "Specify vaPutSurface() source rectangle",
669       STRUCT_VALUE_WITH_FLAG(rect, vaapi_putsurface_source_rect),
670     },
671     { /* Specify vaPutSurface() target rectangle */
672       "vaapi-putsurface-target-rect",
673       "Specify vaPutSurface() target rectangle",
674       STRUCT_VALUE_WITH_FLAG(rect, vaapi_putsurface_target_rect),
675     },
676     { /* Specify vaPutSurface() flags, separated by ':' */
677       "vaapi-putsurface-flags",
678       "Specify vaPutSurface() flags",
679       FLAGS_VALUE(vaapi_putsurface_flags, vaapi_putsurface_flags, 0),
680     },
681     { /* Specify vaAssociateSubpicture() source rectangle [--putimage blend] */
682       "vaapi-subpicture-source-rect",
683       "Specify vaAssociateSubpicture() source rectangle",
684       STRUCT_VALUE_WITH_FLAG(rect, vaapi_subpicture_source_rect),
685     },
686     { /* Specify vaAssociateSubpicture() target rectangle [--putimage blend] */
687       "vaapi-subpicture-target-rect",
688       "Specify vaAssociateSubpicture() target rectangle",
689       STRUCT_VALUE_WITH_FLAG(rect, vaapi_subpicture_target_rect),
690     },
691     { /* Specify vaAssociateSubpicture() flags, separated by ':' */
692       "vaapi-subpicture-flags",
693       "Specify vaAssociateSubpicture() flags",
694       FLAGS_VALUE(vaapi_subpicture_flags, vaapi_subpicture_flags, 0),
695     },
696     { /* Specify vaSetSubpictureGlobalAlpha() value */
697       "vaapi-subpicture-alpha",
698       "Specify vaSetSubpictureGlobalAlpha() value",
699       FLOAT_VALUE(vaapi_subpicture_alpha, 1.0),
700     },
701     { /* Render a single surface to multiple locations within the same window */
702       /* DEPRECATED: use --multi-rendering */
703       "vaapi-multirendering",
704       "Render a single surface to multiple locations within the same window",
705       BOOL_VALUE(multi_rendering) | OPT_FLAG_DEPRECATED,
706     },
707     { /* Let vaPutImage() scale the source image into the destination surface */
708       "vaapi-putimage-scaled",
709       "Allow vaPutImage() to scale the source image into the destination surface",
710       BOOL_VALUE(vaapi_putimage_scaled),
711     },
712     { /* Set background color via a VA-API display attribute */
713       "vaapi-background-color",
714       "Set background color via a VA-API display attribute",
715       STRUCT_VALUE_WITH_FLAG(color, vaapi_background_color),
716     },
717     { /* Use multiple (5) subpictures to render --putimage blend scene */
718       "vaapi-multi-subpictures",
719       "Use multiple (5) subpictures to render --putimage blend scene",
720       BOOL_VALUE(vaapi_multi_subpictures),
721     },
722 #if USE_GLX
723     { /* Use vaCopySurfaceGLX() to transfer surface to a GL texture (default) */
724       "vaapi-glx-use-copy",
725       "Use vaCopySurfaceGLX() to transfer surface to a GL texture (default)",
726       BOOL_VALUE(vaapi_glx_use_copy),
727     },
728 #endif
729 #endif
730 #if USE_VDPAU
731     { /* Use VDPAU layers for subpictures [--putimage blend] */
732       "vdpau-layers",
733       "Use VDPAU layers for subpictures",
734       BOOL_VALUE(vdpau_layers),
735     },
736     { /* Enable high-quality scaling */
737       "vdpau-hqscaling",
738       "Enable high-quality scaling",
739       BOOL_VALUE(vdpau_hqscaling),
740     },
741 #if USE_GLX
742     { /* Enable VDPAU/GL interop through a VdpVideoSurface */
743       "vdpau-glx-video-surface",
744       "Enable VDPAU/GL interop through a VdpVideoSurface",
745       BOOL_VALUE(vdpau_glx_video_surface),
746     },
747     { /* Enable VDPAU/GL interop through a VdpOutputSurface */
748       "vdpau-glx-output-surface",
749       "Enable VDPAU/GL interop through a VdpOutputSurface",
750       BOOL_VALUE(vdpau_glx_output_surface),
751     },
752 #endif
753 #endif
754 #if USE_GLX
755     { /* Specify the GLX texture target to use for rendering */
756       "glx-texture-target",
757       "Specify the GLX texture target to use for rendering",
758       ENUM_VALUE(glx_texture_target, texture_targets, 0),
759     },
760     { /* Specify the GLX texture format to use for rendering */
761       "glx-texture-format",
762       "Specify the GLX texture format to use for rendering",
763       ENUM_VALUE(glx_texture_format, texture_formats, 0),
764     },
765     { /* Specify the GLX texture size to use for rendering */
766       "glx-texture-size",
767       "Specify the GLX texture size to use for rendering",
768       STRUCT_VALUE_WITH_FLAG(size, glx_texture_size),
769     },
770     { /* Render the surface to an FBO prior to rendering to screen */
771       "glx-use-fbo",
772       "Render the surface to an FBO prior to rendering to screen",
773       BOOL_VALUE(glx_use_fbo),
774     },
775     { /* Render the surface with a reflection effect */
776       "glx-use-reflection",
777       "Render the surface with a reflection effect",
778       BOOL_VALUE(glx_use_reflection),
779     },
780 #endif
781 #if USE_CRYSTALHD
782     { /* Use DtsProcOutputNoCopy() to get the surface from Crystal HD */
783       "crystalhd-output-nocopy",
784       "Use DtsProcOutputNoCopy() to get the surface from Crystal HD",
785       BOOL_VALUE(crystalhd_output_nocopy),
786     },
787     { /* Use DtsFlushInput() to commit encoded or decoded frames */
788       "crystalhd-flush",
789       "Use DtsFlushInput() to commit encoded or decoded frames",
790       BOOL_VALUE(crystalhd_flush),
791     },
792 #endif
793     { NULL, }
794 };
795
796 #undef UINT_VALUE
797 #undef FLOAT_VALUE
798 #undef ENUM_VALUE
799 #undef STRUCT_VALUE
800 #undef STRUCT_VALUE_RAW
801 #undef STRUCT_VALUE_WITH_FLAG
802
803 static inline void print_bool(const opt_t *o)
804 {
805     unsigned int v = *(unsigned int *)o->var;
806
807     printf("%s", v ? "true" : "false");
808 }
809
810 static void print_enum(const opt_t *o)
811 {
812     unsigned int v = *(unsigned int *)o->var;
813
814     const char *vs = map_get_string(o->enum_map, v);
815     if (!vs)
816         vs = "<unknown>";
817     printf("\"%s\"", vs);
818 }
819
820 static void print_var(const opt_t *o)
821 {
822     switch (o->type) {
823     case OPT_TYPE_UINT:
824         if (o->flags & OPT_FLAG_NEGATE)
825             print_bool(o);
826         break;
827     case OPT_TYPE_ENUM:
828         print_enum(o);
829         break;
830     default:
831         break;
832     }
833 }
834
835 static void show_help(const char *prog)
836 {
837     int i, j;
838
839     printf("%s - version %s\n", PACKAGE, PACKAGE_VERSION);
840     printf("\n");
841     printf("Usage: %s [<option>]*\n", prog);
842     printf("Where <option> is any of the following:\n");
843     printf("\n");
844
845     for (i = 0; g_options[i].name; i++) {
846         const opt_t * const o = &g_options[i];
847         int show_default = 0;
848         int show_values  = 0;
849
850         switch (o->type) {
851         case OPT_TYPE_UINT:
852             if (o->flags & OPT_FLAG_NEGATE)
853                 show_default = 1;
854             break;
855         case OPT_TYPE_ENUM:
856             if (*(unsigned int *)o->var)
857                 show_default = 1;
858             show_values = 1;
859             break;
860         case OPT_TYPE_FLAGS:
861             show_values = 1;
862             break;
863         default:
864             break;
865         }
866
867         printf("--%s", o->name);
868         printf(" (");
869         if (o->flags & OPT_FLAG_DEPRECATED)
870             printf("deprecated ");
871         switch (o->type) {
872         case OPT_TYPE_UINT:
873             if (o->flags & OPT_FLAG_NEGATE)
874                 printf("bool");
875             break;
876         case OPT_TYPE_FLOAT:
877             printf("float");
878             break;
879         case OPT_TYPE_ENUM:
880             printf("enum");
881             break;
882         case OPT_TYPE_STRUCT:
883             printf("struct");
884             break;
885         case OPT_TYPE_FLAGS:
886             printf("flags");
887             break;
888         }
889         printf(")");
890         printf("\n");
891
892         if (o->desc)
893             printf("  %s.", o->desc);
894         if (show_default) {
895             printf(" Default: ");
896             print_var(o);
897             printf(".");
898         }
899         printf("\n");
900
901         if (show_values) {
902             printf("\n");
903             printf("  ");
904             switch (o->type) {
905             case OPT_TYPE_ENUM:
906                 printf("Possible values:\n");
907                 break;
908             case OPT_TYPE_FLAGS:
909                 printf("Flags are a combination of the following values "
910                        ", with \":\" as the separator:\n");
911                 break;
912             default:
913                 ASSERT(o->type == OPT_TYPE_ENUM ||
914                        o->type == OPT_TYPE_FLAGS);
915                 break;
916             }
917
918             const map_t * const m = o->enum_map;
919             for (j = 0; m[j].str; j++)
920                 printf("    \"%s\"\n", m[j].str);
921         }
922         printf("\n");
923     }
924     exit(0);
925 }
926
927 static int options_parse(int argc, char *argv[])
928 {
929     int i, j;
930
931     for (i = 1; i < argc; i++) {
932         const char *arg = argv[i];
933         if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
934             show_help(argv[0]);
935             return -1;
936         }
937
938         if (*arg++ != '-' || *arg++ != '-')
939             continue;
940
941         int negate = 0;
942         if (strncmp(arg, "no-", 3) == 0) {
943             arg += 3;
944             negate = 1;
945         }
946
947         for (j = 0; g_options[j].name; j++) {
948             const opt_t *opt = &g_options[j];
949             if (strcmp(arg, opt->name) == 0) {
950                 unsigned int * const pval = opt->var;
951                 unsigned int * const pvar = opt->varflag;
952                 if (negate) {
953                     assert(pval);
954                     *pval = 0;
955                 }
956                 else {
957                     switch (opt->type) {
958                     case OPT_TYPE_UINT:
959                         assert(pval);
960                         *pval = opt->value;
961                         break;
962                     case OPT_TYPE_FLOAT:
963                         assert(pval);
964                         if (++i >= argc || opt_subparse_float(argv[i], opt) < 0)
965                             error("could not parse %s argument", arg);
966                         break;
967                         break;
968                     case OPT_TYPE_ENUM:
969                         assert(pval);
970                         if (i + 1 < argc && opt_subparse_enum(argv[i + 1], opt) == 0)
971                             i++;
972                         else if (opt->flags & OPT_FLAG_OPTIONAL_SUBARG)
973                             *pval = opt->value;
974                         else
975                             error("unknown %s value '%s'", arg, argv[i + 1]);
976                         break;
977                     case OPT_TYPE_STRUCT:
978                         assert(opt->subparse_func);
979                         if (++i >= argc || opt->subparse_func(argv[i], opt) < 0)
980                             error("could not parse %s argument", arg);
981                         break;
982                     case OPT_TYPE_FLAGS:
983                         assert(pval);
984                         if (pvar)
985                             *pvar = 0;
986                         if (i + 1 < argc && opt_subparse_flags(argv[i + 1], opt) == 0)
987                             i++;
988                         else if (opt->flags & OPT_FLAG_OPTIONAL_SUBARG)
989                             *pval = opt->value;
990                         else
991                             error("could not parse %s flags '%s'", arg, argv[i + 1]);
992                         if (pvar)
993                             *pvar = 1;
994                         break;
995                     default:
996                         error("invalid option type %d", opt->type);
997                         break;
998                     }
999                 }
1000                 break;
1001             }
1002         }
1003         if (!g_options[j].name)
1004             error("unknown option '%s'", argv[i]);
1005     }
1006     return 0;
1007 }
1008
1009 int main(int argc, char *argv[])
1010 {
1011     CommonContext * const common = common_get_context();
1012     int i, is_error = 1;
1013
1014     /* Option defaults */
1015     common->vaapi_derive_image          = 1;
1016     common->vaapi_glx_use_copy          = 1;
1017     common->vaapi_subpicture_alpha      = 1.0;
1018     common->glx_texture_target          = TEXTURE_TARGET_2D;
1019     common->glx_texture_format          = IMAGE_BGRA;
1020     common->glx_use_fbo                 = 0;
1021     common->glx_use_reflection          = 1;
1022
1023     if (options_parse(argc, argv) < 0)
1024         goto end;
1025
1026     if (common->use_clipping && common->cliprects_count == 0) {
1027         /* add some default clip rectangles */
1028         const unsigned int ww = common->window_size.width;
1029         const unsigned int wh = common->window_size.height;
1030         const unsigned int gw = ww / 32;
1031         const unsigned int gh = wh / 32;
1032         append_cliprect(4*gw, 4*gh, 4*gw, 4*gh);
1033         append_cliprect(ww - 8*gw, 4*gh, 4*gw, 4*gh);
1034         append_cliprect(ww/2 - 2*gw, 4*gh, 4*gw, 4*gh);
1035         append_cliprect(ww/2 - 8*gw, wh - 8*gh, 16*gw, 4*gh);
1036         append_cliprect(ww/2 - 2*gw, wh - 12*gh, 4*gw, 4*gh);
1037         append_cliprect(ww/2 - 12*gw, wh - 20*gh, 24*gw, 4*gh);
1038     }
1039
1040     if (common->use_subwindow_rect) {
1041         Rectangle * const r = &common->subwindow_rect;
1042         if (r->width == 0 || r->height == 0) {
1043             r->x      = common->window_size.width/2;
1044             r->y      = common->window_size.height/2;
1045             r->width  = common->window_size.width/2;
1046             r->height = common->window_size.height/2;
1047         }
1048         ensure_bounds(r, common->window_size.width, common->window_size.height);
1049     }
1050
1051     if (common->use_vaapi_putsurface_target_rect)
1052         ensure_bounds(&common->vaapi_putsurface_target_rect,
1053                       common->window_size.width,
1054                       common->window_size.height);
1055
1056     printf("Display type '%s'\n",
1057            map_get_string(map_display_types, common->display_type));
1058
1059     if (common->use_subwindow_rect)
1060         printf("Render to subwindow at (%d,%d) with size %ux%u\n",
1061                common->subwindow_rect.x,
1062                common->subwindow_rect.y,
1063                common->subwindow_rect.width,
1064                common->subwindow_rect.height);
1065
1066     if (common->hwaccel_type != HWACCEL_NONE)
1067         printf("Hardware accelerator '%s'\n",
1068                map_get_string(map_hwaccel_types, common->hwaccel_type));
1069
1070     if (common->getimage_mode != GETIMAGE_NONE)
1071         printf("Readback decoded pixels from '%s' surface, in %s format\n",
1072                map_get_string(map_getimage_modes, common->getimage_mode),
1073                (common->getimage_format
1074                 ? map_get_string(map_image_formats, common->getimage_format)
1075                 : "hardware default"));
1076
1077     if (common->putimage_mode != PUTIMAGE_NONE)
1078         printf("Transfer custom pixels to surface in '%s' mode, in %s format\n",
1079                map_get_string(map_putimage_modes, common->putimage_mode),
1080                (common->putimage_format
1081                 ? map_get_string(map_image_formats, common->putimage_format)
1082                 : "hardware default"));
1083
1084     if (common->cliprects_count > 0) {
1085         printf("%d clip rects\n", common->cliprects_count);
1086         for (i = 0; i < common->cliprects_count; i++)
1087             printf("  %d: location (%d,%d), size %ux%u\n",
1088                    i + 1,
1089                    common->cliprects[i].x,
1090                    common->cliprects[i].y,
1091                    common->cliprects[i].width,
1092                    common->cliprects[i].height);
1093     }
1094
1095     common->cliprects_image = image_create(common->window_size.width,
1096                                            common->window_size.height,
1097                                            IMAGE_RGB32);
1098     if (!common->cliprects_image) {
1099         fprintf(stderr, "ERROR: cliprects image allocation failed\n");
1100         goto end;
1101     }
1102
1103     image_draw_rectangle(common->cliprects_image,
1104                          0, 0, common->window_size.width, common->window_size.height,
1105                          0xffffffff);
1106
1107     for (i = 0; i < common->cliprects_count; i++)
1108         image_draw_rectangle(common->cliprects_image,
1109                              common->cliprects[i].x,
1110                              common->cliprects[i].y,
1111                              common->cliprects[i].width,
1112                              common->cliprects[i].height,
1113                              0xffff5400);
1114
1115     common->image = image_create(common->window_size.width,
1116                                  common->window_size.height,
1117                                  IMAGE_RGB32);
1118     if (!common->image) {
1119         fprintf(stderr, "ERROR: image allocation failed\n");
1120         goto end;
1121     }
1122
1123     if (pre() < 0) {
1124         fprintf(stderr, "ERROR: initialization failed\n");
1125         goto end;
1126     }
1127
1128     if (decode() < 0) {
1129         fprintf(stderr, "ERROR: decode failed\n");
1130         goto end;
1131     }
1132
1133     if (display() < 0) {
1134         fprintf(stderr, "ERROR: display failed\n");
1135         goto end;
1136     }
1137
1138     if (post() < 0) {
1139         fprintf(stderr, "ERROR: deinitialization failed\n");
1140         goto end;
1141     }
1142
1143     is_error = 0;
1144 end:
1145     free(common->cliprects);
1146     image_destroy(common->cliprects_image);
1147     image_destroy(common->image);
1148     return is_error;
1149 }