1/**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include <vdpau/vdpau.h>
29
30#include "util/u_memory.h"
31#include "util/u_debug.h"
32
33#include "vl/vl_csc.h"
34
35#include "vdpau_private.h"
36
37/**
38 * Create a VdpVideoMixer.
39 */
40VdpStatus
41vlVdpVideoMixerCreate(VdpDevice device,
42                      uint32_t feature_count,
43                      VdpVideoMixerFeature const *features,
44                      uint32_t parameter_count,
45                      VdpVideoMixerParameter const *parameters,
46                      void const *const *parameter_values,
47                      VdpVideoMixer *mixer)
48{
49   vlVdpVideoMixer *vmixer = NULL;
50   VdpStatus ret;
51   struct pipe_screen *screen;
52   uint32_t max_2d_texture_level;
53   unsigned max_size, i;
54
55   vlVdpDevice *dev = vlGetDataHTAB(device);
56   if (!dev)
57      return VDP_STATUS_INVALID_HANDLE;
58   screen = dev->vscreen->pscreen;
59
60   vmixer = CALLOC(1, sizeof(vlVdpVideoMixer));
61   if (!vmixer)
62      return VDP_STATUS_RESOURCES;
63
64   DeviceReference(&vmixer->device, dev);
65
66   mtx_lock(&dev->mutex);
67
68   if (!vl_compositor_init_state(&vmixer->cstate, dev->context)) {
69      ret = VDP_STATUS_ERROR;
70      goto no_compositor_state;
71   }
72
73   vl_csc_get_matrix(VL_CSC_COLOR_STANDARD_BT_601, NULL, true, &vmixer->csc);
74   if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE)) {
75      if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc, 1.0f, 0.0f)) {
76         ret = VDP_STATUS_ERROR;
77         goto err_csc_matrix;
78      }
79   }
80
81   *mixer = vlAddDataHTAB(vmixer);
82   if (*mixer == 0) {
83      ret = VDP_STATUS_ERROR;
84      goto no_handle;
85   }
86
87   ret = VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
88   for (i = 0; i < feature_count; ++i) {
89      switch (features[i]) {
90      /* they are valid, but we doesn't support them */
91      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
92      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
93      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
94      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
95      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
96      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
97      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
98      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
99      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
100      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
101         break;
102
103      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
104         vmixer->deint.supported = true;
105         break;
106
107      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
108         vmixer->sharpness.supported = true;
109         break;
110
111      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
112         vmixer->noise_reduction.supported = true;
113         break;
114
115      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
116         vmixer->luma_key.supported = true;
117         break;
118
119      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
120         vmixer->bicubic.supported = true;
121         break;
122      default: goto no_params;
123      }
124   }
125
126   vmixer->chroma_format = PIPE_VIDEO_CHROMA_FORMAT_420;
127   ret = VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER;
128   for (i = 0; i < parameter_count; ++i) {
129      switch (parameters[i]) {
130      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH:
131         vmixer->video_width = *(uint32_t*)parameter_values[i];
132         break;
133      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT:
134         vmixer->video_height = *(uint32_t*)parameter_values[i];
135         break;
136      case VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE:
137         vmixer->chroma_format = ChromaToPipe(*(VdpChromaType*)parameter_values[i]);
138         break;
139      case VDP_VIDEO_MIXER_PARAMETER_LAYERS:
140         vmixer->max_layers = *(uint32_t*)parameter_values[i];
141         break;
142      default: goto no_params;
143      }
144   }
145   ret = VDP_STATUS_INVALID_VALUE;
146   if (vmixer->max_layers > 4) {
147      VDPAU_MSG(VDPAU_WARN, "[VDPAU] Max layers > 4 not supported\n", vmixer->max_layers);
148      goto no_params;
149   }
150
151   max_2d_texture_level = screen->get_param(screen, PIPE_CAP_MAX_TEXTURE_2D_LEVELS);
152   max_size = pow(2, max_2d_texture_level-1);
153   if (vmixer->video_width < 48 || vmixer->video_width > max_size) {
154      VDPAU_MSG(VDPAU_WARN, "[VDPAU] 48 < %u < %u not valid for width\n",
155                vmixer->video_width, max_size);
156      goto no_params;
157   }
158   if (vmixer->video_height < 48 || vmixer->video_height > max_size) {
159      VDPAU_MSG(VDPAU_WARN, "[VDPAU] 48 < %u < %u  not valid for height\n",
160                vmixer->video_height, max_size);
161      goto no_params;
162   }
163   vmixer->luma_key.luma_min = 1.0f;
164   vmixer->luma_key.luma_max = 0.0f;
165   mtx_unlock(&dev->mutex);
166
167   return VDP_STATUS_OK;
168
169no_params:
170   vlRemoveDataHTAB(*mixer);
171
172no_handle:
173err_csc_matrix:
174   vl_compositor_cleanup_state(&vmixer->cstate);
175no_compositor_state:
176   mtx_unlock(&dev->mutex);
177   DeviceReference(&vmixer->device, NULL);
178   FREE(vmixer);
179   return ret;
180}
181
182/**
183 * Destroy a VdpVideoMixer.
184 */
185VdpStatus
186vlVdpVideoMixerDestroy(VdpVideoMixer mixer)
187{
188   vlVdpVideoMixer *vmixer;
189
190   vmixer = vlGetDataHTAB(mixer);
191   if (!vmixer)
192      return VDP_STATUS_INVALID_HANDLE;
193
194   mtx_lock(&vmixer->device->mutex);
195
196   vlRemoveDataHTAB(mixer);
197
198   vl_compositor_cleanup_state(&vmixer->cstate);
199
200   if (vmixer->deint.filter) {
201      vl_deint_filter_cleanup(vmixer->deint.filter);
202      FREE(vmixer->deint.filter);
203   }
204
205   if (vmixer->noise_reduction.filter) {
206      vl_median_filter_cleanup(vmixer->noise_reduction.filter);
207      FREE(vmixer->noise_reduction.filter);
208   }
209
210   if (vmixer->sharpness.filter) {
211      vl_matrix_filter_cleanup(vmixer->sharpness.filter);
212      FREE(vmixer->sharpness.filter);
213   }
214
215   if (vmixer->bicubic.filter) {
216      vl_bicubic_filter_cleanup(vmixer->bicubic.filter);
217      FREE(vmixer->bicubic.filter);
218   }
219   mtx_unlock(&vmixer->device->mutex);
220   DeviceReference(&vmixer->device, NULL);
221
222   FREE(vmixer);
223
224   return VDP_STATUS_OK;
225}
226
227/**
228 * Perform a video post-processing and compositing operation.
229 */
230VdpStatus vlVdpVideoMixerRender(VdpVideoMixer mixer,
231                                VdpOutputSurface background_surface,
232                                VdpRect const *background_source_rect,
233                                VdpVideoMixerPictureStructure current_picture_structure,
234                                uint32_t video_surface_past_count,
235                                VdpVideoSurface const *video_surface_past,
236                                VdpVideoSurface video_surface_current,
237                                uint32_t video_surface_future_count,
238                                VdpVideoSurface const *video_surface_future,
239                                VdpRect const *video_source_rect,
240                                VdpOutputSurface destination_surface,
241                                VdpRect const *destination_rect,
242                                VdpRect const *destination_video_rect,
243                                uint32_t layer_count,
244                                VdpLayer const *layers)
245{
246   enum vl_compositor_deinterlace deinterlace;
247   struct u_rect rect, clip, *prect, dirty_area;
248   unsigned i, layer = 0;
249   struct pipe_video_buffer *video_buffer;
250   struct pipe_sampler_view *sampler_view, sv_templ;
251   struct pipe_surface *surface, surf_templ;
252   struct pipe_context *pipe = NULL;
253   struct pipe_resource res_tmpl, *res;
254
255   vlVdpVideoMixer *vmixer;
256   vlVdpSurface *surf;
257   vlVdpOutputSurface *dst, *bg = NULL;
258
259   struct vl_compositor *compositor;
260
261   vmixer = vlGetDataHTAB(mixer);
262   if (!vmixer)
263      return VDP_STATUS_INVALID_HANDLE;
264
265   compositor = &vmixer->device->compositor;
266
267   surf = vlGetDataHTAB(video_surface_current);
268   if (!surf)
269      return VDP_STATUS_INVALID_HANDLE;
270   video_buffer = surf->video_buffer;
271
272   if (surf->device != vmixer->device)
273      return VDP_STATUS_HANDLE_DEVICE_MISMATCH;
274
275   if (vmixer->video_width > video_buffer->width ||
276       vmixer->video_height > video_buffer->height ||
277       vmixer->chroma_format != video_buffer->chroma_format)
278      return VDP_STATUS_INVALID_SIZE;
279
280   if (layer_count > vmixer->max_layers)
281      return VDP_STATUS_INVALID_VALUE;
282
283   dst = vlGetDataHTAB(destination_surface);
284   if (!dst)
285      return VDP_STATUS_INVALID_HANDLE;
286
287   if (background_surface != VDP_INVALID_HANDLE) {
288      bg = vlGetDataHTAB(background_surface);
289      if (!bg)
290         return VDP_STATUS_INVALID_HANDLE;
291   }
292
293   mtx_lock(&vmixer->device->mutex);
294
295   vl_compositor_clear_layers(&vmixer->cstate);
296
297   if (bg)
298      vl_compositor_set_rgba_layer(&vmixer->cstate, compositor, layer++, bg->sampler_view,
299                                   RectToPipe(background_source_rect, &rect), NULL, NULL);
300
301   switch (current_picture_structure) {
302   case VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD:
303      deinterlace = VL_COMPOSITOR_BOB_TOP;
304      break;
305
306   case VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
307      deinterlace = VL_COMPOSITOR_BOB_BOTTOM;
308      break;
309
310   case VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME:
311      deinterlace = VL_COMPOSITOR_WEAVE;
312      break;
313
314   default:
315      mtx_unlock(&vmixer->device->mutex);
316      return VDP_STATUS_INVALID_VIDEO_MIXER_PICTURE_STRUCTURE;
317   }
318
319   if (deinterlace != VL_COMPOSITOR_WEAVE && vmixer->deint.enabled &&
320       video_surface_past_count > 1 && video_surface_future_count > 0) {
321      vlVdpSurface *prevprev = vlGetDataHTAB(video_surface_past[1]);
322      vlVdpSurface *prev = vlGetDataHTAB(video_surface_past[0]);
323      vlVdpSurface *next = vlGetDataHTAB(video_surface_future[0]);
324      if (prevprev && prev && next &&
325          vl_deint_filter_check_buffers(vmixer->deint.filter,
326          prevprev->video_buffer, prev->video_buffer, surf->video_buffer, next->video_buffer)) {
327         vl_deint_filter_render(vmixer->deint.filter, prevprev->video_buffer,
328                                prev->video_buffer, surf->video_buffer,
329                                next->video_buffer,
330                                deinterlace == VL_COMPOSITOR_BOB_BOTTOM);
331         deinterlace = VL_COMPOSITOR_WEAVE;
332         video_buffer = vmixer->deint.filter->video_buffer;
333      }
334   }
335
336   prect = RectToPipe(video_source_rect, &rect);
337   if (!prect) {
338      rect.x0 = 0;
339      rect.y0 = 0;
340      rect.x1 = surf->templat.width;
341      rect.y1 = surf->templat.height;
342      prect = &rect;
343   }
344   vl_compositor_set_buffer_layer(&vmixer->cstate, compositor, layer, video_buffer, prect, NULL, deinterlace);
345
346   if (vmixer->bicubic.filter || vmixer->sharpness.filter || vmixer->noise_reduction.filter) {
347      pipe = vmixer->device->context;
348      memset(&res_tmpl, 0, sizeof(res_tmpl));
349
350      res_tmpl.target = PIPE_TEXTURE_2D;
351      res_tmpl.format = dst->sampler_view->format;
352      res_tmpl.depth0 = 1;
353      res_tmpl.array_size = 1;
354      res_tmpl.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
355      res_tmpl.usage = PIPE_USAGE_DEFAULT;
356
357      if (!vmixer->bicubic.filter) {
358         res_tmpl.width0 = dst->surface->width;
359         res_tmpl.height0 = dst->surface->height;
360      } else {
361         res_tmpl.width0 = surf->templat.width;
362         res_tmpl.height0 = surf->templat.height;
363      }
364
365      res = pipe->screen->resource_create(pipe->screen, &res_tmpl);
366
367      vlVdpDefaultSamplerViewTemplate(&sv_templ, res);
368      sampler_view = pipe->create_sampler_view(pipe, res, &sv_templ);
369
370      memset(&surf_templ, 0, sizeof(surf_templ));
371      surf_templ.format = res->format;
372      surface = pipe->create_surface(pipe, res, &surf_templ);
373
374      vl_compositor_reset_dirty_area(&dirty_area);
375      pipe_resource_reference(&res, NULL);
376   } else {
377      surface = dst->surface;
378      sampler_view = dst->sampler_view;
379      dirty_area = dst->dirty_area;
380   }
381
382   if (!vmixer->bicubic.filter) {
383      vl_compositor_set_layer_dst_area(&vmixer->cstate, layer++, RectToPipe(destination_video_rect, &rect));
384      vl_compositor_set_dst_clip(&vmixer->cstate, RectToPipe(destination_rect, &clip));
385   }
386
387   for (i = 0; i < layer_count; ++i) {
388      vlVdpOutputSurface *src = vlGetDataHTAB(layers->source_surface);
389      if (!src) {
390         mtx_unlock(&vmixer->device->mutex);
391         return VDP_STATUS_INVALID_HANDLE;
392      }
393
394      assert(layers->struct_version == VDP_LAYER_VERSION);
395
396      vl_compositor_set_rgba_layer(&vmixer->cstate, compositor, layer, src->sampler_view,
397                                   RectToPipe(layers->source_rect, &rect), NULL, NULL);
398      vl_compositor_set_layer_dst_area(&vmixer->cstate, layer++, RectToPipe(layers->destination_rect, &rect));
399
400      ++layers;
401   }
402
403   vl_compositor_render(&vmixer->cstate, compositor, surface, &dirty_area, true);
404
405   if (vmixer->noise_reduction.filter) {
406      if (!vmixer->sharpness.filter && !vmixer->bicubic.filter) {
407         vl_median_filter_render(vmixer->noise_reduction.filter,
408                                 sampler_view, dst->surface);
409      } else {
410         res = pipe->screen->resource_create(pipe->screen, &res_tmpl);
411         struct pipe_sampler_view *sampler_view_temp = pipe->create_sampler_view(pipe, res, &sv_templ);
412         struct pipe_surface *surface_temp = pipe->create_surface(pipe, res, &surf_templ);
413         pipe_resource_reference(&res, NULL);
414
415         vl_median_filter_render(vmixer->noise_reduction.filter,
416                                 sampler_view, surface_temp);
417
418         pipe_sampler_view_reference(&sampler_view, NULL);
419         pipe_surface_reference(&surface, NULL);
420
421         sampler_view = sampler_view_temp;
422         surface = surface_temp;
423      }
424   }
425
426   if (vmixer->sharpness.filter) {
427      if (!vmixer->bicubic.filter) {
428         vl_matrix_filter_render(vmixer->sharpness.filter,
429                                 sampler_view, dst->surface);
430      } else {
431         res = pipe->screen->resource_create(pipe->screen, &res_tmpl);
432         struct pipe_sampler_view *sampler_view_temp = pipe->create_sampler_view(pipe, res, &sv_templ);
433         struct pipe_surface *surface_temp = pipe->create_surface(pipe, res, &surf_templ);
434         pipe_resource_reference(&res, NULL);
435
436         vl_matrix_filter_render(vmixer->sharpness.filter,
437                                 sampler_view, surface_temp);
438
439         pipe_sampler_view_reference(&sampler_view, NULL);
440         pipe_surface_reference(&surface, NULL);
441
442         sampler_view = sampler_view_temp;
443         surface = surface_temp;
444      }
445   }
446
447   if (vmixer->bicubic.filter)
448      vl_bicubic_filter_render(vmixer->bicubic.filter,
449                               sampler_view, dst->surface,
450                               RectToPipe(destination_video_rect, &rect),
451                               RectToPipe(destination_rect, &clip));
452
453   if(surface != dst->surface) {
454      pipe_sampler_view_reference(&sampler_view, NULL);
455      pipe_surface_reference(&surface, NULL);
456   }
457   mtx_unlock(&vmixer->device->mutex);
458
459   return VDP_STATUS_OK;
460}
461
462static void
463vlVdpVideoMixerUpdateDeinterlaceFilter(vlVdpVideoMixer *vmixer)
464{
465   struct pipe_context *pipe = vmixer->device->context;
466   assert(vmixer);
467
468   /* remove existing filter */
469   if (vmixer->deint.filter) {
470      vl_deint_filter_cleanup(vmixer->deint.filter);
471      FREE(vmixer->deint.filter);
472      vmixer->deint.filter = NULL;
473   }
474
475   /* create a new filter if requested */
476   if (vmixer->deint.enabled && vmixer->chroma_format == PIPE_VIDEO_CHROMA_FORMAT_420) {
477      vmixer->deint.filter = MALLOC(sizeof(struct vl_deint_filter));
478      vmixer->deint.enabled = vl_deint_filter_init(vmixer->deint.filter, pipe,
479            vmixer->video_width, vmixer->video_height,
480            vmixer->skip_chroma_deint, vmixer->deint.spatial);
481      if (!vmixer->deint.enabled) {
482         FREE(vmixer->deint.filter);
483      }
484   }
485}
486
487/**
488 * Update the noise reduction setting
489 */
490static void
491vlVdpVideoMixerUpdateNoiseReductionFilter(vlVdpVideoMixer *vmixer)
492{
493   assert(vmixer);
494
495   /* if present remove the old filter first */
496   if (vmixer->noise_reduction.filter) {
497      vl_median_filter_cleanup(vmixer->noise_reduction.filter);
498      FREE(vmixer->noise_reduction.filter);
499      vmixer->noise_reduction.filter = NULL;
500   }
501
502   /* and create a new filter as needed */
503   if (vmixer->noise_reduction. enabled && vmixer->noise_reduction.level > 0) {
504      vmixer->noise_reduction.filter = MALLOC(sizeof(struct vl_median_filter));
505      vl_median_filter_init(vmixer->noise_reduction.filter, vmixer->device->context,
506                            vmixer->video_width, vmixer->video_height,
507                            vmixer->noise_reduction.level + 1,
508                            VL_MEDIAN_FILTER_CROSS);
509   }
510}
511
512static void
513vlVdpVideoMixerUpdateSharpnessFilter(vlVdpVideoMixer *vmixer)
514{
515   assert(vmixer);
516
517   /* if present remove the old filter first */
518   if (vmixer->sharpness.filter) {
519      vl_matrix_filter_cleanup(vmixer->sharpness.filter);
520      FREE(vmixer->sharpness.filter);
521      vmixer->sharpness.filter = NULL;
522   }
523
524   /* and create a new filter as needed */
525   if (vmixer->sharpness.enabled && vmixer->sharpness.value != 0.0f) {
526      float matrix[9];
527      unsigned i;
528
529      if (vmixer->sharpness.value > 0.0f) {
530         matrix[0] = -1.0f; matrix[1] = -1.0f; matrix[2] = -1.0f;
531         matrix[3] = -1.0f; matrix[4] =  8.0f; matrix[5] = -1.0f;
532         matrix[6] = -1.0f; matrix[7] = -1.0f; matrix[8] = -1.0f;
533
534         for (i = 0; i < 9; ++i)
535            matrix[i] *= vmixer->sharpness.value;
536
537         matrix[4] += 1.0f;
538
539      } else {
540         matrix[0] = 1.0f; matrix[1] = 2.0f; matrix[2] = 1.0f;
541         matrix[3] = 2.0f; matrix[4] = 4.0f; matrix[5] = 2.0f;
542         matrix[6] = 1.0f; matrix[7] = 2.0f; matrix[8] = 1.0f;
543
544         for (i = 0; i < 9; ++i)
545               matrix[i] *= fabsf(vmixer->sharpness.value) / 16.0f;
546
547         matrix[4] += 1.0f - fabsf(vmixer->sharpness.value);
548      }
549
550      vmixer->sharpness.filter = MALLOC(sizeof(struct vl_matrix_filter));
551      vl_matrix_filter_init(vmixer->sharpness.filter, vmixer->device->context,
552                            vmixer->video_width, vmixer->video_height,
553                            3, 3, matrix);
554   }
555}
556
557/**
558 * Update the bicubic filter
559 */
560static void
561vlVdpVideoMixerUpdateBicubicFilter(vlVdpVideoMixer *vmixer)
562{
563   assert(vmixer);
564
565   /* if present remove the old filter first */
566   if (vmixer->bicubic.filter) {
567      vl_bicubic_filter_cleanup(vmixer->bicubic.filter);
568      FREE(vmixer->bicubic.filter);
569      vmixer->bicubic.filter = NULL;
570   }
571   /* and create a new filter as needed */
572   if (vmixer->bicubic.enabled) {
573      vmixer->bicubic.filter = MALLOC(sizeof(struct vl_bicubic_filter));
574      vl_bicubic_filter_init(vmixer->bicubic.filter, vmixer->device->context,
575                            vmixer->video_width, vmixer->video_height);
576   }
577}
578
579/**
580 * Retrieve whether features were requested at creation time.
581 */
582VdpStatus
583vlVdpVideoMixerGetFeatureSupport(VdpVideoMixer mixer,
584                                 uint32_t feature_count,
585                                 VdpVideoMixerFeature const *features,
586                                 VdpBool *feature_supports)
587{
588   vlVdpVideoMixer *vmixer;
589   unsigned i;
590
591   if (!(features && feature_supports))
592      return VDP_STATUS_INVALID_POINTER;
593
594   vmixer = vlGetDataHTAB(mixer);
595   if (!vmixer)
596      return VDP_STATUS_INVALID_HANDLE;
597
598   for (i = 0; i < feature_count; ++i) {
599      switch (features[i]) {
600      /* they are valid, but we doesn't support them */
601      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
602      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
603      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
604      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
605      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
606      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
607      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
608      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
609      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
610      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
611         feature_supports[i] = false;
612         break;
613
614      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
615         feature_supports[i] = vmixer->deint.supported;
616         break;
617
618      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
619         feature_supports[i] = vmixer->sharpness.supported;
620         break;
621
622      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
623         feature_supports[i] = vmixer->noise_reduction.supported;
624         break;
625
626      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
627         feature_supports[i] = vmixer->luma_key.supported;
628         break;
629
630      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
631         feature_supports[i] = vmixer->bicubic.supported;
632         break;
633
634      default:
635         return VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
636      }
637   }
638
639   return VDP_STATUS_OK;
640}
641
642/**
643 * Enable or disable features.
644 */
645VdpStatus
646vlVdpVideoMixerSetFeatureEnables(VdpVideoMixer mixer,
647                                 uint32_t feature_count,
648                                 VdpVideoMixerFeature const *features,
649                                 VdpBool const *feature_enables)
650{
651   vlVdpVideoMixer *vmixer;
652   unsigned i;
653
654   if (!(features && feature_enables))
655      return VDP_STATUS_INVALID_POINTER;
656
657   vmixer = vlGetDataHTAB(mixer);
658   if (!vmixer)
659      return VDP_STATUS_INVALID_HANDLE;
660
661   mtx_lock(&vmixer->device->mutex);
662   for (i = 0; i < feature_count; ++i) {
663      switch (features[i]) {
664      /* they are valid, but we doesn't support them */
665      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
666      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
667      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
668      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
669      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
670      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
671      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
672      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
673      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
674      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
675         break;
676
677      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
678         vmixer->deint.enabled = feature_enables[i];
679         vlVdpVideoMixerUpdateDeinterlaceFilter(vmixer);
680         break;
681
682      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
683         vmixer->sharpness.enabled = feature_enables[i];
684         vlVdpVideoMixerUpdateSharpnessFilter(vmixer);
685         break;
686
687      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
688         vmixer->noise_reduction.enabled = feature_enables[i];
689         vlVdpVideoMixerUpdateNoiseReductionFilter(vmixer);
690         break;
691
692      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
693         vmixer->luma_key.enabled = feature_enables[i];
694         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
695            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
696                        vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
697               mtx_unlock(&vmixer->device->mutex);
698               return VDP_STATUS_ERROR;
699            }
700         break;
701
702      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
703         vmixer->bicubic.enabled = feature_enables[i];
704         vlVdpVideoMixerUpdateBicubicFilter(vmixer);
705         break;
706
707      default:
708         mtx_unlock(&vmixer->device->mutex);
709         return VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
710      }
711   }
712   mtx_unlock(&vmixer->device->mutex);
713
714   return VDP_STATUS_OK;
715}
716
717/**
718 * Retrieve whether features are enabled.
719 */
720VdpStatus
721vlVdpVideoMixerGetFeatureEnables(VdpVideoMixer mixer,
722                                 uint32_t feature_count,
723                                 VdpVideoMixerFeature const *features,
724                                 VdpBool *feature_enables)
725{
726   vlVdpVideoMixer *vmixer;
727   unsigned i;
728
729   if (!(features && feature_enables))
730      return VDP_STATUS_INVALID_POINTER;
731
732   vmixer = vlGetDataHTAB(mixer);
733   if (!vmixer)
734      return VDP_STATUS_INVALID_HANDLE;
735
736   for (i = 0; i < feature_count; ++i) {
737      switch (features[i]) {
738      /* they are valid, but we doesn't support them */
739      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL:
740      case VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL:
741      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2:
742      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3:
743      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4:
744      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5:
745      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6:
746      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7:
747      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8:
748      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9:
749      case VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE:
750         break;
751
752      case VDP_VIDEO_MIXER_FEATURE_SHARPNESS:
753         feature_enables[i] = vmixer->sharpness.enabled;
754         break;
755
756      case VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION:
757         feature_enables[i] = vmixer->noise_reduction.enabled;
758         break;
759
760      case VDP_VIDEO_MIXER_FEATURE_LUMA_KEY:
761         feature_enables[i] = vmixer->luma_key.enabled;
762         break;
763
764      case VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1:
765         feature_enables[i] = vmixer->bicubic.enabled;
766         break;
767
768      default:
769         return VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE;
770      }
771   }
772
773   return VDP_STATUS_OK;
774}
775
776/**
777 * Set attribute values.
778 */
779VdpStatus
780vlVdpVideoMixerSetAttributeValues(VdpVideoMixer mixer,
781                                  uint32_t attribute_count,
782                                  VdpVideoMixerAttribute const *attributes,
783                                  void const *const *attribute_values)
784{
785   const VdpColor *background_color;
786   union pipe_color_union color;
787   const float *vdp_csc;
788   float val;
789   unsigned i;
790   VdpStatus ret;
791
792   if (!(attributes && attribute_values))
793      return VDP_STATUS_INVALID_POINTER;
794
795   vlVdpVideoMixer *vmixer = vlGetDataHTAB(mixer);
796   if (!vmixer)
797      return VDP_STATUS_INVALID_HANDLE;
798
799   mtx_lock(&vmixer->device->mutex);
800   for (i = 0; i < attribute_count; ++i) {
801      switch (attributes[i]) {
802      case VDP_VIDEO_MIXER_ATTRIBUTE_BACKGROUND_COLOR:
803         background_color = attribute_values[i];
804         color.f[0] = background_color->red;
805         color.f[1] = background_color->green;
806         color.f[2] = background_color->blue;
807         color.f[3] = background_color->alpha;
808         vl_compositor_set_clear_color(&vmixer->cstate, &color);
809         break;
810      case VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX:
811         vdp_csc = attribute_values[i];
812         vmixer->custom_csc = !!vdp_csc;
813         if (!vdp_csc)
814            vl_csc_get_matrix(VL_CSC_COLOR_STANDARD_BT_601, NULL, 1, &vmixer->csc);
815         else
816            memcpy(vmixer->csc, vdp_csc, sizeof(vl_csc_matrix));
817         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
818            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
819                                         vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
820               ret = VDP_STATUS_ERROR;
821               goto fail;
822            }
823         break;
824
825      case VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL:
826
827         val = *(float*)attribute_values[i];
828         if (val < 0.0f || val > 1.0f) {
829            ret = VDP_STATUS_INVALID_VALUE;
830            goto fail;
831         }
832
833         vmixer->noise_reduction.level = val * 10;
834         vlVdpVideoMixerUpdateNoiseReductionFilter(vmixer);
835         break;
836
837      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MIN_LUMA:
838         val = *(float*)attribute_values[i];
839         if (val < 0.0f || val > 1.0f) {
840            ret = VDP_STATUS_INVALID_VALUE;
841            goto fail;
842         }
843         vmixer->luma_key.luma_min = val;
844         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
845            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
846                        vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
847               ret = VDP_STATUS_ERROR;
848               goto fail;
849            }
850         break;
851
852      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MAX_LUMA:
853         val = *(float*)attribute_values[i];
854         if (val < 0.0f || val > 1.0f) {
855            ret = VDP_STATUS_INVALID_VALUE;
856            goto fail;
857         }
858         vmixer->luma_key.luma_max = val;
859         if (!debug_get_bool_option("G3DVL_NO_CSC", FALSE))
860            if (!vl_compositor_set_csc_matrix(&vmixer->cstate, (const vl_csc_matrix *)&vmixer->csc,
861                        vmixer->luma_key.luma_min, vmixer->luma_key.luma_max)) {
862               ret = VDP_STATUS_ERROR;
863               goto fail;
864            }
865         break;
866
867      case VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL:
868
869         val = *(float*)attribute_values[i];
870         if (val < -1.0f || val > 1.0f) {
871            ret = VDP_STATUS_INVALID_VALUE;
872            goto fail;
873         }
874
875         vmixer->sharpness.value = val;
876         vlVdpVideoMixerUpdateSharpnessFilter(vmixer);
877         break;
878
879      case VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE:
880         if (*(uint8_t*)attribute_values[i] > 1) {
881            ret = VDP_STATUS_INVALID_VALUE;
882            goto fail;
883         }
884         vmixer->skip_chroma_deint = *(uint8_t*)attribute_values[i];
885         vlVdpVideoMixerUpdateDeinterlaceFilter(vmixer);
886         break;
887      default:
888         ret = VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE;
889         goto fail;
890      }
891   }
892   mtx_unlock(&vmixer->device->mutex);
893
894   return VDP_STATUS_OK;
895fail:
896   mtx_unlock(&vmixer->device->mutex);
897   return ret;
898}
899
900/**
901 * Retrieve parameter values given at creation time.
902 */
903VdpStatus
904vlVdpVideoMixerGetParameterValues(VdpVideoMixer mixer,
905                                  uint32_t parameter_count,
906                                  VdpVideoMixerParameter const *parameters,
907                                  void *const *parameter_values)
908{
909   vlVdpVideoMixer *vmixer = vlGetDataHTAB(mixer);
910   unsigned i;
911   if (!vmixer)
912      return VDP_STATUS_INVALID_HANDLE;
913
914   if (!parameter_count)
915      return VDP_STATUS_OK;
916   if (!(parameters && parameter_values))
917      return VDP_STATUS_INVALID_POINTER;
918   for (i = 0; i < parameter_count; ++i) {
919      switch (parameters[i]) {
920      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH:
921         *(uint32_t*)parameter_values[i] = vmixer->video_width;
922         break;
923      case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT:
924         *(uint32_t*)parameter_values[i] = vmixer->video_height;
925         break;
926      case VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE:
927         *(VdpChromaType*)parameter_values[i] = PipeToChroma(vmixer->chroma_format);
928         break;
929      case VDP_VIDEO_MIXER_PARAMETER_LAYERS:
930         *(uint32_t*)parameter_values[i] = vmixer->max_layers;
931         break;
932      default:
933         return VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER;
934      }
935   }
936   return VDP_STATUS_OK;
937}
938
939/**
940 * Retrieve current attribute values.
941 */
942VdpStatus
943vlVdpVideoMixerGetAttributeValues(VdpVideoMixer mixer,
944                                  uint32_t attribute_count,
945                                  VdpVideoMixerAttribute const *attributes,
946                                  void *const *attribute_values)
947{
948   unsigned i;
949   VdpCSCMatrix **vdp_csc;
950
951   if (!(attributes && attribute_values))
952      return VDP_STATUS_INVALID_POINTER;
953
954   vlVdpVideoMixer *vmixer = vlGetDataHTAB(mixer);
955   if (!vmixer)
956      return VDP_STATUS_INVALID_HANDLE;
957
958   mtx_lock(&vmixer->device->mutex);
959   for (i = 0; i < attribute_count; ++i) {
960      switch (attributes[i]) {
961      case VDP_VIDEO_MIXER_ATTRIBUTE_BACKGROUND_COLOR:
962         vl_compositor_get_clear_color(&vmixer->cstate, attribute_values[i]);
963         break;
964      case VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX:
965         vdp_csc = attribute_values[i];
966         if (!vmixer->custom_csc) {
967             *vdp_csc = NULL;
968            break;
969         }
970         memcpy(*vdp_csc, vmixer->csc, sizeof(float)*12);
971         break;
972
973      case VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL:
974         *(float*)attribute_values[i] = (float)vmixer->noise_reduction.level / 10.0f;
975         break;
976
977      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MIN_LUMA:
978         *(float*)attribute_values[i] = vmixer->luma_key.luma_min;
979         break;
980      case VDP_VIDEO_MIXER_ATTRIBUTE_LUMA_KEY_MAX_LUMA:
981         *(float*)attribute_values[i] = vmixer->luma_key.luma_max;
982         break;
983      case VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL:
984         *(float*)attribute_values[i] = vmixer->sharpness.value;
985         break;
986      case VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE:
987         *(uint8_t*)attribute_values[i] = vmixer->skip_chroma_deint;
988         break;
989      default:
990         mtx_unlock(&vmixer->device->mutex);
991         return VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE;
992      }
993   }
994   mtx_unlock(&vmixer->device->mutex);
995   return VDP_STATUS_OK;
996}
997
998/**
999 * Generate a color space conversion matrix.
1000 */
1001VdpStatus
1002vlVdpGenerateCSCMatrix(VdpProcamp *procamp,
1003                       VdpColorStandard standard,
1004                       VdpCSCMatrix *csc_matrix)
1005{
1006   enum VL_CSC_COLOR_STANDARD vl_std;
1007   struct vl_procamp camp;
1008
1009   if (!csc_matrix)
1010      return VDP_STATUS_INVALID_POINTER;
1011
1012   switch (standard) {
1013      case VDP_COLOR_STANDARD_ITUR_BT_601: vl_std = VL_CSC_COLOR_STANDARD_BT_601; break;
1014      case VDP_COLOR_STANDARD_ITUR_BT_709: vl_std = VL_CSC_COLOR_STANDARD_BT_709; break;
1015      case VDP_COLOR_STANDARD_SMPTE_240M:  vl_std = VL_CSC_COLOR_STANDARD_SMPTE_240M; break;
1016      default: return VDP_STATUS_INVALID_COLOR_STANDARD;
1017   }
1018
1019   if (procamp) {
1020      if (procamp->struct_version > VDP_PROCAMP_VERSION)
1021         return VDP_STATUS_INVALID_STRUCT_VERSION;
1022      camp.brightness = procamp->brightness;
1023      camp.contrast = procamp->contrast;
1024      camp.saturation = procamp->saturation;
1025      camp.hue = procamp->hue;
1026   }
1027
1028   vl_csc_get_matrix(vl_std, procamp ? &camp : NULL, true, csc_matrix);
1029   return VDP_STATUS_OK;
1030}
1031