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