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