2ac13d7e4c4a5d0a095dc6a88d60f838fe73321f
[vlc.git] / modules / hw / vdpau / chroma.c
1 /*****************************************************************************
2  * chroma.c: VLC picture import into VDPAU
3  *****************************************************************************
4  * Copyright (C) 2013 RĂ©mi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <stdlib.h>
26 #include <inttypes.h>
27 #include <assert.h>
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_filter.h>
32 #include "vlc_vdpau.h"
33
34 /* Picture history as recommended by VDPAU documentation */
35 #define MAX_PAST   2
36 #define MAX_FUTURE 1
37
38 struct filter_sys_t
39 {
40     vdp_t *vdp;
41     VdpDevice device;
42     VdpVideoMixer mixer;
43     VdpChromaType chroma;
44     VdpYCbCrFormat format;
45     picture_t *(*import)(filter_t *, picture_t *);
46
47     struct
48     {
49         vlc_vdp_video_field_t *field;
50         mtime_t date;
51         bool force;
52     } history[MAX_PAST + 1 + MAX_FUTURE];
53
54     struct
55     {
56         float brightness;
57         float contrast;
58         float saturation;
59         float hue;
60     } procamp;
61 };
62
63 /** Initialize the colour space conversion matrix */
64 static VdpStatus MixerSetupColors(filter_t *filter, const VdpProcamp *procamp,
65                                   VdpCSCMatrix *restrict csc)
66 {
67     filter_sys_t *sys = filter->p_sys;
68     VdpStatus err;
69     VdpColorStandard std = (filter->fmt_in.video.i_height > 576)
70                          ? VDP_COLOR_STANDARD_ITUR_BT_709
71                          : VDP_COLOR_STANDARD_ITUR_BT_601;
72
73     err = vdp_generate_csc_matrix(sys->vdp, procamp, std, csc);
74     if (err != VDP_STATUS_OK)
75     {
76         msg_Err(filter, "video %s failure: %s", "color space matrix",
77                 vdp_get_error_string(sys->vdp, err));
78         return err;
79     }
80
81     if (procamp != NULL)
82     {
83         sys->procamp.brightness = procamp->brightness;
84         sys->procamp.contrast = procamp->contrast;
85         sys->procamp.saturation = procamp->saturation;
86         sys->procamp.hue = procamp->hue;
87     }
88     else
89     {
90         sys->procamp.brightness = 0.f;
91         sys->procamp.contrast = 1.f;
92         sys->procamp.saturation = 1.f;
93         sys->procamp.hue = 0.f;
94     }
95     return VDP_STATUS_OK;
96 }
97
98 /** Create VDPAU video mixer */
99 static VdpVideoMixer MixerCreate(filter_t *filter)
100 {
101     filter_sys_t *sys = filter->p_sys;
102     VdpVideoMixer mixer;
103     VdpStatus err;
104     VdpBool ok;
105
106     /* Check for potentially useful features */
107     VdpVideoMixerFeature featv[5];
108     unsigned featc = 0;
109
110     int algo = var_InheritInteger(filter, "vdpau-deinterlace");
111     bool ivtc = false;
112     if (algo == VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL)
113     {
114         err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
115                                                     algo, &ok);
116         if (err == VDP_STATUS_OK && ok == VDP_TRUE)
117             msg_Dbg(filter, "using video mixer %s feature",
118                     "temporal-spatial deinterlace");
119         else
120             algo = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL; /* fallback */
121     }
122     if (algo == VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL)
123     {
124         err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
125                                                     algo, &ok);
126         if (err == VDP_STATUS_OK && ok == VDP_TRUE)
127             msg_Dbg(filter, "using video mixer %s feature",
128                     "temporal deinterlace");
129         else
130             algo = -1;
131     }
132     if (algo >= 0)
133     {
134         featv[featc++] = algo;
135         ivtc = var_InheritBool(filter, "vdpau-ivtc");
136         if (ivtc)
137         {
138             err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
139                                 VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE, &ok);
140             if (err == VDP_STATUS_OK && ok == VDP_TRUE)
141                 msg_Dbg(filter, "using video mixer %s feature",
142                         "inverse telecine");
143             featv[featc++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
144         }
145     }
146
147     const float noise = var_InheritFloat(filter, "vdpau-noise-reduction");
148     if (noise > 0.f)
149     {
150         err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
151                                  VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION, &ok);
152         if (err == VDP_STATUS_OK && ok == VDP_TRUE)
153         {
154             msg_Dbg(filter, "using video mixer %s feature", "noise reduction");
155             featv[featc++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
156         }
157     }
158
159     err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
160                                        VDP_VIDEO_MIXER_FEATURE_SHARPNESS, &ok);
161     if (err == VDP_STATUS_OK && ok == VDP_TRUE)
162     {
163         msg_Dbg(filter, "using video mixer %s feature", "sharpness");
164         featv[featc++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
165     }
166
167     const int offset = VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 - 1;
168     unsigned level = var_InheritInteger(filter, "vdpau-scaling");
169     while (level > 0)
170     {
171
172         err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
173                                                     offset + level, &ok);
174         if (err == VDP_STATUS_OK && ok == VDP_TRUE)
175         {
176             msg_Dbg(filter, "using video mixer high quality scaling L%u",
177                     level);
178             featv[featc++] = offset + level;
179             break;
180         }
181         level--; /* fallback to lower quality */
182     }
183
184     /* Create the mixer */
185     VdpVideoMixerParameter parms[3] = {
186         VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
187         VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
188         VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
189     };
190     uint32_t width = filter->fmt_in.video.i_width;
191     uint32_t height = filter->fmt_in.video.i_height;
192     const void *values[3] = { &width, &height, &sys->chroma, };
193
194     err = vdp_video_mixer_create(sys->vdp, sys->device, featc, featv,
195                                  3, parms, values, &mixer);
196     if (err != VDP_STATUS_OK)
197     {
198         msg_Err(filter, "video %s %s failure: %s", "mixer", "creation",
199                 vdp_get_error_string(sys->vdp, err));
200         mixer = VDP_INVALID_HANDLE;
201     }
202
203     /* Set initial features and attributes */
204     VdpVideoMixerAttribute attrv[3];
205     const void *valv[3];
206     unsigned attrc = 0;
207     VdpCSCMatrix csc;
208     uint8_t chroma_skip;
209
210     featc = 0;
211
212     if (MixerSetupColors(filter, NULL, &csc) == VDP_STATUS_OK)
213     {
214         attrv[attrc] = VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX;
215         valv[attrc] = &csc;
216         attrc++;
217     }
218
219     if (algo >= 0)
220     {
221         featv[featc++] = algo;
222         if (ivtc)
223             featv[featc++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
224
225         chroma_skip = var_InheritBool(filter, "vdpau-chroma-skip");
226         attrv[attrc] = VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE;
227         valv[attrc] = &chroma_skip;
228         attrc++;
229     }
230
231     if (noise > 0.f)
232     {
233         featv[featc++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
234
235         attrv[attrc] = VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL;
236         valv[attrc] = &noise;
237         attrc++;
238     }
239
240     if (level > 0)
241         featv[featc++] = offset + level;
242
243     if (featc > 0)
244     {
245         VdpBool enablev[featc];
246
247         for (unsigned i = 0; i < featc; i++)
248             enablev[i] = VDP_TRUE;
249
250         err = vdp_video_mixer_set_feature_enables(sys->vdp, mixer,
251                                                   featc, featv, enablev);
252         if (err != VDP_STATUS_OK)
253             msg_Err(filter, "video %s %s failure: %s", "mixer", "features",
254                     vdp_get_error_string(sys->vdp, err));
255     }
256
257     if (attrc > 0)
258     {
259         err = vdp_video_mixer_set_attribute_values(sys->vdp, mixer,
260                                                    attrc, attrv, valv);
261         if (err != VDP_STATUS_OK)
262             msg_Err(filter, "video %s %s failure: %s", "mixer", "attributes",
263                     vdp_get_error_string(sys->vdp, err));
264     }
265
266     return mixer;
267 }
268
269 static void Flush(filter_t *filter)
270 {
271     filter_sys_t *sys = filter->p_sys;
272
273     for (unsigned i = 0; i < MAX_PAST + MAX_FUTURE; i++)
274         if (sys->history[i].field != NULL)
275         {
276             sys->history[i].field->destroy(sys->history[i].field);
277             sys->history[i].field = NULL;
278         }
279 }
280
281 /** Get a VLC picture for a VDPAU output surface */
282 static picture_t *OutputAllocate(filter_t *filter)
283 {
284     filter_sys_t *sys = filter->p_sys;
285
286     picture_t *pic = filter_NewPicture(filter);
287     if (pic == NULL)
288         return NULL;
289
290     picture_sys_t *psys = pic->p_sys;
291     assert(psys->vdp != NULL);
292
293     if (unlikely(sys->vdp != psys->vdp) && (sys->mixer != VDP_INVALID_HANDLE))
294     {
295         Flush(filter); /* release surfaces from the old device */
296         vdp_video_mixer_destroy(sys->vdp, sys->mixer);
297         vdp_release_x11(sys->vdp);
298         sys->mixer = VDP_INVALID_HANDLE;
299     }
300
301     if (unlikely(sys->mixer == VDP_INVALID_HANDLE))
302     {
303         sys->vdp = vdp_hold_x11(psys->vdp, NULL);
304         sys->device = psys->device;
305         sys->mixer = MixerCreate(filter);
306         if (sys->mixer == VDP_INVALID_HANDLE)
307             goto error;
308         msg_Dbg(filter, "using video mixer %"PRIu32, sys->mixer);
309     }
310     return pic;
311 error:
312     picture_Release(pic);
313     return NULL;
314 }
315
316 /** Export a VDPAU video surface picture to a normal VLC picture */
317 static picture_t *VideoExport(filter_t *filter, picture_t *src, picture_t *dst)
318 {
319     filter_sys_t *sys = filter->p_sys;
320     vlc_vdp_video_field_t *field = src->context;
321     vlc_vdp_video_frame_t *psys = field->frame;
322     VdpStatus err;
323     VdpVideoSurface surface = psys->surface;
324     void *planes[3];
325     uint32_t pitches[3];
326
327     for (int i = 0; i < dst->i_planes; i++)
328     {
329         planes[i] = dst->p[i].p_pixels;
330         pitches[i] = dst->p[i].i_pitch;
331     }
332     if (dst->format.i_chroma == VLC_CODEC_I420
333      || dst->format.i_chroma == VLC_CODEC_I422)
334     {
335         planes[1] = dst->p[2].p_pixels;
336         planes[2] = dst->p[1].p_pixels;
337         pitches[1] = dst->p[2].i_pitch;
338         pitches[2] = dst->p[1].i_pitch;
339     }
340     err = vdp_video_surface_get_bits_y_cb_cr(psys->vdp, surface, sys->format,
341                                              planes, pitches);
342     if (err != VDP_STATUS_OK)
343     {
344         msg_Err(filter, "video %s %s failure: %s", "surface", "export",
345                 vdp_get_error_string(psys->vdp, err));
346         picture_Release(dst);
347         dst = NULL;
348     }
349     picture_CopyProperties(dst, src);
350     picture_Release(src);
351     return dst;
352 }
353
354 /** Import VLC picture into VDPAU video surface */
355 static picture_t *VideoImport(filter_t *filter, picture_t *src)
356 {
357     filter_sys_t *sys = filter->p_sys;
358     VdpVideoSurface surface;
359     VdpStatus err;
360
361     if (sys->vdp == NULL)
362         goto drop;
363
364     /* Create surface (TODO: reuse?) */
365     err = vdp_video_surface_create(sys->vdp, sys->device, sys->chroma,
366                                    filter->fmt_in.video.i_width,
367                                    filter->fmt_in.video.i_height, &surface);
368     if (err != VDP_STATUS_OK)
369     {
370         msg_Err(filter, "video %s %s failure: %s", "surface", "creation",
371                 vdp_get_error_string(sys->vdp, err));
372         goto drop;
373     }
374
375     /* Put bits */
376     const void *planes[3];
377     uint32_t pitches[3];
378     for (int i = 0; i < src->i_planes; i++)
379     {
380         planes[i] = src->p[i].p_pixels;
381         pitches[i] = src->p[i].i_pitch;
382     }
383     if (src->format.i_chroma == VLC_CODEC_I420)
384     {
385         planes[1] = src->p[2].p_pixels;
386         planes[2] = src->p[1].p_pixels;
387         pitches[1] = src->p[2].i_pitch;
388         pitches[2] = src->p[1].i_pitch;
389     }
390     err = vdp_video_surface_put_bits_y_cb_cr(sys->vdp, surface, sys->format,
391                                              planes, pitches);
392     if (err != VDP_STATUS_OK)
393     {
394         msg_Err(filter, "video %s %s failure: %s", "surface", "import",
395                 vdp_get_error_string(sys->vdp, err));
396         goto error;
397     }
398
399     /* Wrap surface into a picture */
400     video_format_t fmt = src->format;
401     fmt.i_chroma = (sys->chroma == VDP_CHROMA_TYPE_420)
402         ? VLC_CODEC_VDPAU_VIDEO_420 : VLC_CODEC_VDPAU_VIDEO_422;
403
404     picture_t *dst = picture_NewFromFormat(&fmt);
405     if (unlikely(dst == NULL))
406         goto error;
407     picture_CopyProperties(dst, src);
408     picture_Release(src);
409
410     err = vlc_vdp_video_attach(sys->vdp, surface, dst);
411     if (unlikely(err != VDP_STATUS_OK))
412     {
413         picture_Release(dst);
414         dst = NULL;
415     }
416     return dst;
417 error:
418     vdp_video_surface_destroy(sys->vdp, surface);
419 drop:
420     picture_Release(src);
421     return NULL;
422 }
423
424 static picture_t *VideoPassthrough(filter_t *filter, picture_t *src)
425 {
426     filter_sys_t *sys = filter->p_sys;
427     vlc_vdp_video_field_t *field = src->context;
428
429     if (unlikely(field == NULL))
430     {
431         msg_Err(filter, "corrupt VDPAU video surface");
432         return NULL;
433     }
434
435     vlc_vdp_video_frame_t *psys = field->frame;
436
437     /* Corner case: different VDPAU instances decoding and rendering */
438     if (psys->vdp != sys->vdp)
439     {
440         video_format_t fmt = src->format;
441         switch (sys->chroma)
442         {
443              case VDP_CHROMA_TYPE_420: fmt.i_chroma = VLC_CODEC_NV12; break;
444              case VDP_CHROMA_TYPE_422: fmt.i_chroma = VLC_CODEC_UYVY; break;
445              case VDP_CHROMA_TYPE_444: fmt.i_chroma = VLC_CODEC_NV24; break;
446              default: assert(0);
447         }
448
449         picture_t *pic = picture_NewFromFormat(&fmt);
450         if (unlikely(pic == NULL))
451             return NULL;
452
453         pic = VideoExport(filter, src, pic);
454         if (pic == NULL)
455             return NULL;
456
457         src = VideoImport(filter, pic);
458     }
459     return src;
460 }
461
462 static inline VdpVideoSurface picture_GetVideoSurface(const picture_t *pic)
463 {
464     vlc_vdp_video_field_t *field = pic->context;
465     return field->frame->surface;
466 }
467
468 static picture_t *MixerRender(filter_t *filter, picture_t *src)
469 {
470     filter_sys_t *sys = filter->p_sys;
471     VdpStatus err;
472
473     picture_t *dst = OutputAllocate(filter);
474
475     src = sys->import(filter, src);
476     /* Update history and take "present" picture field */
477     if (likely(src != NULL))
478     {
479         sys->history[MAX_PAST + MAX_FUTURE].field =
480                                               vlc_vdp_video_copy(src->context);
481         sys->history[MAX_PAST + MAX_FUTURE].date = src->date;
482         sys->history[MAX_PAST + MAX_FUTURE].force = src->b_force;
483         picture_Release(src);
484     }
485     else
486         sys->history[MAX_PAST + MAX_FUTURE].field = NULL;
487
488     if (dst == NULL)
489         goto skip;
490
491     vlc_vdp_video_field_t *f = sys->history[MAX_PAST].field;
492     if (f == NULL)
493     {
494         picture_Release(dst);
495         dst = NULL;
496         goto skip;
497     }
498
499     dst->date = sys->history[MAX_PAST].date;
500     dst->b_force = sys->history[MAX_PAST].force;
501
502     /* Enable/Disable features */
503     const VdpVideoMixerFeature features[] = {
504         VDP_VIDEO_MIXER_FEATURE_SHARPNESS,
505     };
506     const VdpBool enables[] = {
507         f->sharpen != 0.f,
508     };
509
510     err = vdp_video_mixer_set_feature_enables(sys->vdp, sys->mixer,
511                   sizeof (features) / sizeof (features[0]), features, enables);
512     if (err != VDP_STATUS_OK)
513         msg_Err(filter, "video %s %s failure: %s", "mixer", "features",
514                 vdp_get_error_string(sys->vdp, err));
515
516     /* Configure mixer depending on upstream video filters */
517     VdpVideoMixerAttribute attrs[2] = {
518         VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL,
519     };
520     const void *values[2] = {
521         &f->sharpen,
522     };
523     unsigned count = 1;
524     VdpCSCMatrix csc;
525
526     if ((sys->procamp.brightness != f->procamp.brightness
527       || sys->procamp.contrast != f->procamp.contrast
528       || sys->procamp.saturation != f->procamp.saturation
529       || sys->procamp.hue != f->procamp.hue)
530      && (MixerSetupColors(filter, &f->procamp, &csc) == VDP_STATUS_OK))
531     {
532         attrs[count] = VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX;
533         values[count] = &csc;
534         count++;
535     }
536
537     err = vdp_video_mixer_set_attribute_values(sys->vdp, sys->mixer,
538                                                count, attrs, values);
539     if (err != VDP_STATUS_OK)
540         msg_Err(filter, "video %s %s failure: %s", "mixer", "attributes",
541                 vdp_get_error_string(sys->vdp, err));
542
543     /* Render video into output */
544     VdpVideoMixerPictureStructure structure = f->structure;
545     VdpVideoSurface past[MAX_PAST];
546     VdpVideoSurface surface = f->frame->surface;
547     VdpVideoSurface future[MAX_FUTURE];
548     VdpRect src_rect = {
549         filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
550         filter->fmt_in.video.i_visible_width + filter->fmt_in.video.i_x_offset,
551         filter->fmt_in.video.i_visible_height + filter->fmt_in.video.i_y_offset
552     };
553     VdpOutputSurface output = dst->p_sys->surface;
554     VdpRect dst_rect = {
555         0, 0,
556         filter->fmt_out.video.i_visible_width,
557         filter->fmt_out.video.i_visible_height
558     };
559
560     for (unsigned i = 0; i < MAX_PAST; i++)
561     {
562         f = sys->history[(MAX_PAST - 1) - i].field;
563         past[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
564     }
565     for (unsigned i = 0; i < MAX_FUTURE; i++)
566     {
567         f = sys->history[(MAX_PAST + 1) + i].field;
568         future[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
569     }
570
571     err = vdp_video_mixer_render(sys->vdp, sys->mixer, VDP_INVALID_HANDLE,
572                                  NULL, structure,
573                                  MAX_PAST, past, surface, MAX_FUTURE, future,
574                                  &src_rect, output, &dst_rect, NULL, 0, NULL);
575     if (err != VDP_STATUS_OK)
576     {
577         msg_Err(filter, "video %s %s failure: %s", "mixer", "rendering",
578                 vdp_get_error_string(sys->vdp, err));
579         picture_Release(dst);
580         dst = NULL;
581     }
582
583 skip:
584     f = sys->history[0].field;
585     if (f != NULL)
586         f->destroy(f); /* Release oldest field */
587     memmove(sys->history, sys->history + 1, /* Advance history */
588             sizeof (sys->history[0]) * (MAX_PAST + MAX_FUTURE));
589
590     return dst;
591 }
592
593 static int OutputOpen(vlc_object_t *obj)
594 {
595     filter_t *filter = (filter_t *)obj;
596     if (filter->fmt_out.video.i_chroma != VLC_CODEC_VDPAU_OUTPUT)
597         return VLC_EGENERIC;
598
599     filter_sys_t *sys = malloc(sizeof (*sys));
600     if (unlikely(sys == NULL))
601         return VLC_ENOMEM;
602
603     sys->vdp = NULL;
604     sys->mixer = VDP_INVALID_HANDLE;
605
606     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_444)
607     {
608         sys->chroma = VDP_CHROMA_TYPE_444;
609         sys->format = VDP_YCBCR_FORMAT_NV12;
610         sys->import = VideoPassthrough;
611     }
612     else
613     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_422)
614     {
615         sys->chroma = VDP_CHROMA_TYPE_422;
616         /* TODO: check if the drivery supports NV12 or UYVY */
617         sys->format = VDP_YCBCR_FORMAT_UYVY;
618         sys->import = VideoPassthrough;
619     }
620     else
621     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_420)
622     {
623         sys->chroma = VDP_CHROMA_TYPE_420;
624         sys->format = VDP_YCBCR_FORMAT_NV12;
625         sys->import = VideoPassthrough;
626     }
627     else
628     if (vlc_fourcc_to_vdp_ycc(filter->fmt_in.video.i_chroma,
629                               &sys->chroma, &sys->format))
630         sys->import = VideoImport;
631     else
632     {
633         free(sys);
634         return VLC_EGENERIC;
635     }
636
637     /* NOTE: The video mixer capabilities should be checked here, and the
638      * then video mixer set up. But:
639      * 1) The VDPAU back-end is accessible only once the first picture
640      *    gets filtered. Thus the video mixer is created later.
641      * 2) Bailing out due to insufficient capabilities would break the
642      *    video pipeline. Thus capabilities should be checked earlier. */
643
644     for (unsigned i = 0; i < MAX_PAST + MAX_FUTURE; i++)
645         sys->history[i].field = NULL;
646
647     sys->procamp.brightness = 0.f;
648     sys->procamp.contrast = 1.f;
649     sys->procamp.saturation = 1.f;
650     sys->procamp.hue = 0.f;
651
652     filter->pf_video_filter = MixerRender;
653     filter->pf_video_flush = Flush;
654     filter->p_sys = sys;
655     return VLC_SUCCESS;
656 }
657
658 static void OutputClose(vlc_object_t *obj)
659 {
660     filter_t *filter = (filter_t *)obj;
661     filter_sys_t *sys = filter->p_sys;
662
663     Flush(filter);
664     if (sys->mixer != VDP_INVALID_HANDLE)
665     {
666         vdp_video_mixer_destroy(sys->vdp, sys->mixer);
667         vdp_release_x11(sys->vdp);
668     }
669     free(sys);
670 }
671
672 static picture_t *VideoExport_Filter(filter_t *filter, picture_t *src)
673 {
674     if (unlikely(src->context == NULL))
675     {
676         msg_Err(filter, "corrupt VDPAU video surface %p", src);
677         picture_Release(src);
678         return NULL;
679     }
680
681     picture_t *dst = filter_NewPicture(filter);
682     if (dst == NULL)
683         return NULL;
684
685     return VideoExport(filter, src, dst);
686 }
687
688 static int YCbCrOpen(vlc_object_t *obj)
689 {
690     filter_t *filter = (filter_t *)obj;
691     if (filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_420
692      && filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_422
693      && filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_444)
694         return VLC_EGENERIC;
695
696     if (filter->fmt_in.video.i_visible_width
697                                        != filter->fmt_out.video.i_visible_width
698      || filter->fmt_in.video.i_visible_height
699                                       != filter->fmt_out.video.i_visible_height
700      || filter->fmt_in.video.i_x_offset != filter->fmt_out.video.i_x_offset
701      || filter->fmt_in.video.i_y_offset != filter->fmt_out.video.i_y_offset
702      || (filter->fmt_in.video.i_sar_num * filter->fmt_out.video.i_sar_den
703           != filter->fmt_in.video.i_sar_den * filter->fmt_out.video.i_sar_num))
704         return VLC_EGENERIC;
705
706     filter_sys_t *sys = malloc(sizeof (*sys));
707     if (unlikely(sys == NULL))
708         return VLC_ENOMEM;
709
710     if (!vlc_fourcc_to_vdp_ycc(filter->fmt_out.video.i_chroma,
711                                &sys->chroma, &sys->format))
712     {
713         free(sys);
714         return VLC_EGENERIC;
715     }
716
717     filter->pf_video_filter = VideoExport_Filter;
718     filter->p_sys = sys;
719     return VLC_SUCCESS;
720 }
721
722 static void YCbCrClose(vlc_object_t *obj)
723 {
724     filter_t *filter = (filter_t *)obj;
725     filter_sys_t *sys = filter->p_sys;
726
727     free(sys);
728 }
729
730 static const int algo_values[] = {
731     -1,
732     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
733     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
734 };
735
736 static const char *const algo_names[] = {
737     N_("Bob"), N_("Temporal"), N_("Temporal-spatial"),
738 };
739
740 vlc_module_begin()
741     set_shortname(N_("VDPAU"))
742     set_description(N_("VDPAU surface conversions"))
743     set_capability("video filter2", 10)
744     set_category(CAT_VIDEO)
745     set_subcategory(SUBCAT_VIDEO_VFILTER)
746     set_callbacks(OutputOpen, OutputClose)
747
748     add_integer("vdpau-deinterlace",
749                 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
750                 N_("Deinterlace"), N_("Deinterlacing algorithm"), true)
751         change_integer_list(algo_values, algo_names)
752     add_bool("vdpau-ivtc", false,
753              N_("Inverse telecine"), N_("Inverse telecine"), true)
754     add_bool("vdpau-chroma-skip", false,
755              N_("Deinterlace chroma skip"),
756              N_("Whether temporal deinterlacing applies to luma only"), true)
757     add_float_with_range("vdpau-noise-reduction", 0., 0., 1.,
758         N_("Noise reduction level"), N_("Noise reduction level"), true)
759     add_integer_with_range("vdpau-scaling", 0, 0, 9,
760        N_("Scaling quality"), N_("High quality scaling level"), true)
761
762     add_submodule()
763     set_callbacks(YCbCrOpen, YCbCrClose)
764 vlc_module_end()