]> git.sesse.net Git - vlc/blob - modules/hw/vdpau/chroma.c
mediacodec: Fix freeze when seeking on pause
[vlc] / 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 /** Export a VDPAU video surface picture to a normal VLC picture */
283 static picture_t *VideoExport(filter_t *filter, picture_t *src, picture_t *dst)
284 {
285     filter_sys_t *sys = filter->p_sys;
286     vlc_vdp_video_field_t *field = src->context;
287     vlc_vdp_video_frame_t *psys = field->frame;
288     VdpStatus err;
289     VdpVideoSurface surface = psys->surface;
290     void *planes[3];
291     uint32_t pitches[3];
292
293     picture_CopyProperties(dst, src);
294
295     for (int i = 0; i < dst->i_planes; i++)
296     {
297         planes[i] = dst->p[i].p_pixels;
298         pitches[i] = dst->p[i].i_pitch;
299     }
300     if (dst->format.i_chroma == VLC_CODEC_I420
301      || dst->format.i_chroma == VLC_CODEC_I422
302      || dst->format.i_chroma == VLC_CODEC_I444)
303     {
304         planes[1] = dst->p[2].p_pixels;
305         planes[2] = dst->p[1].p_pixels;
306         pitches[1] = dst->p[2].i_pitch;
307         pitches[2] = dst->p[1].i_pitch;
308     }
309     err = vdp_video_surface_get_bits_y_cb_cr(psys->vdp, surface, sys->format,
310                                              planes, pitches);
311     if (err != VDP_STATUS_OK)
312     {
313         msg_Err(filter, "video %s %s failure: %s", "surface", "export",
314                 vdp_get_error_string(psys->vdp, err));
315         picture_Release(dst);
316         dst = NULL;
317     }
318     picture_Release(src);
319     return dst;
320 }
321
322 /** Import VLC picture into VDPAU video surface */
323 static picture_t *VideoImport(filter_t *filter, picture_t *src)
324 {
325     filter_sys_t *sys = filter->p_sys;
326     VdpVideoSurface surface;
327     VdpStatus err;
328
329     if (sys->vdp == NULL)
330         goto drop;
331
332     /* Create surface (TODO: reuse?) */
333     err = vdp_video_surface_create(sys->vdp, sys->device, sys->chroma,
334                                    filter->fmt_in.video.i_width,
335                                    filter->fmt_in.video.i_height, &surface);
336     if (err != VDP_STATUS_OK)
337     {
338         msg_Err(filter, "video %s %s failure: %s", "surface", "creation",
339                 vdp_get_error_string(sys->vdp, err));
340         goto drop;
341     }
342
343     /* Put bits */
344     const void *planes[3];
345     uint32_t pitches[3];
346     for (int i = 0; i < src->i_planes; i++)
347     {
348         planes[i] = src->p[i].p_pixels;
349         pitches[i] = src->p[i].i_pitch;
350     }
351     if (src->format.i_chroma == VLC_CODEC_I420
352      || src->format.i_chroma == VLC_CODEC_I422
353      || src->format.i_chroma == VLC_CODEC_I444)
354     {
355         planes[1] = src->p[2].p_pixels;
356         planes[2] = src->p[1].p_pixels;
357         pitches[1] = src->p[2].i_pitch;
358         pitches[2] = src->p[1].i_pitch;
359     }
360     err = vdp_video_surface_put_bits_y_cb_cr(sys->vdp, surface, sys->format,
361                                              planes, pitches);
362     if (err != VDP_STATUS_OK)
363     {
364         msg_Err(filter, "video %s %s failure: %s", "surface", "import",
365                 vdp_get_error_string(sys->vdp, err));
366         goto error;
367     }
368
369     /* Wrap surface into a picture */
370     video_format_t fmt = src->format;
371
372     switch (sys->chroma)
373     {
374         case VDP_CHROMA_TYPE_420:
375             fmt.i_chroma = VLC_CODEC_VDPAU_VIDEO_420;
376             break;
377         case VDP_CHROMA_TYPE_422:
378             fmt.i_chroma = VLC_CODEC_VDPAU_VIDEO_422;
379             break;
380         case VDP_CHROMA_TYPE_444:
381             fmt.i_chroma = VLC_CODEC_VDPAU_VIDEO_444;
382             break;
383         default:
384             vlc_assert_unreachable();
385     }
386
387     picture_t *dst = picture_NewFromFormat(&fmt);
388     if (unlikely(dst == NULL))
389         goto error;
390     picture_CopyProperties(dst, src);
391     picture_Release(src);
392
393     err = vlc_vdp_video_attach(sys->vdp, surface, dst);
394     if (unlikely(err != VDP_STATUS_OK))
395     {
396         picture_Release(dst);
397         dst = NULL;
398     }
399     return dst;
400 error:
401     vdp_video_surface_destroy(sys->vdp, surface);
402 drop:
403     picture_Release(src);
404     return NULL;
405 }
406
407 static inline VdpVideoSurface picture_GetVideoSurface(const picture_t *pic)
408 {
409     vlc_vdp_video_field_t *field = pic->context;
410     return field->frame->surface;
411 }
412
413 static picture_t *VideoRender(filter_t *filter, picture_t *src)
414 {
415     filter_sys_t *sys = filter->p_sys;
416     picture_t *dst = NULL;
417     VdpStatus err;
418
419     if (unlikely(src->context == NULL))
420     {
421         msg_Err(filter, "corrupt VDPAU video surface %p", src);
422         picture_Release(src);
423         return NULL;
424     }
425
426     /* Corner case: different VDPAU instances decoding and rendering */
427     vlc_vdp_video_field_t *field = src->context;
428     if (field->frame->vdp != sys->vdp)
429     {
430         video_format_t fmt = src->format;
431         switch (sys->chroma)
432         {
433              case VDP_CHROMA_TYPE_420: fmt.i_chroma = VLC_CODEC_NV12; break;
434              case VDP_CHROMA_TYPE_422: fmt.i_chroma = VLC_CODEC_UYVY; break;
435              case VDP_CHROMA_TYPE_444: fmt.i_chroma = VLC_CODEC_NV24; break;
436              default: vlc_assert_unreachable();
437         }
438
439         picture_t *pic = picture_NewFromFormat(&fmt);
440         if (likely(pic != NULL))
441         {
442             pic = VideoExport(filter, src, pic);
443             if (pic != NULL)
444                 src = VideoImport(filter, pic);
445             else
446                 src = NULL;
447         }
448         else
449         {
450             picture_Release(src);
451             src = NULL;
452         }
453     }
454
455     /* Update history and take "present" picture field */
456     if (likely(src != NULL))
457     {
458         sys->history[MAX_PAST + MAX_FUTURE].field =
459                                               vlc_vdp_video_copy(src->context);
460         sys->history[MAX_PAST + MAX_FUTURE].date = src->date;
461         sys->history[MAX_PAST + MAX_FUTURE].force = src->b_force;
462         picture_Release(src);
463     }
464     else
465     {
466         sys->history[MAX_PAST + MAX_FUTURE].field = NULL;
467         sys->history[MAX_PAST + MAX_FUTURE].force = false;
468     }
469
470     vlc_vdp_video_field_t *f = sys->history[MAX_PAST].field;
471     if (f == NULL)
472     {   /* There is no present field, probably just starting playback. */
473         if (!sys->history[MAX_PAST + MAX_FUTURE].force)
474             goto skip;
475
476         /* If the picture is forced, ignore deinterlacing and fast forward. */
477         /* FIXME: Remove the forced hack pictures in video output core and
478          * allow the last field of a video to be rendered properly. */
479         while (sys->history[MAX_PAST].field == NULL)
480         {
481             f = sys->history[0].field;
482             if (f != NULL)
483                 f->destroy(f);
484
485             memmove(sys->history, sys->history + 1,
486                     sizeof (sys->history[0]) * (MAX_PAST + MAX_FUTURE));
487             sys->history[MAX_PAST + MAX_FUTURE].field = NULL;
488         }
489         f = sys->history[MAX_PAST].field;
490     }
491
492     /* Get a VLC picture for a VDPAU output surface */
493     dst = filter_NewPicture(filter);
494     if (dst == NULL)
495         goto skip;
496
497     assert(dst->p_sys != NULL && dst->p_sys->vdp ==sys->vdp);
498     dst->date = sys->history[MAX_PAST].date;
499     dst->b_force = sys->history[MAX_PAST].force;
500
501     /* Enable/Disable features */
502     const VdpVideoMixerFeature features[] = {
503         VDP_VIDEO_MIXER_FEATURE_SHARPNESS,
504     };
505     const VdpBool enables[] = {
506         f->sharpen != 0.f,
507     };
508
509     err = vdp_video_mixer_set_feature_enables(sys->vdp, sys->mixer,
510                   sizeof (features) / sizeof (features[0]), features, enables);
511     if (err != VDP_STATUS_OK)
512         msg_Err(filter, "video %s %s failure: %s", "mixer", "features",
513                 vdp_get_error_string(sys->vdp, err));
514
515     /* Configure mixer depending on upstream video filters */
516     VdpVideoMixerAttribute attrs[2] = {
517         VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL,
518     };
519     const void *values[2] = {
520         &f->sharpen,
521     };
522     unsigned count = 1;
523     VdpCSCMatrix csc;
524
525     if ((sys->procamp.brightness != f->procamp.brightness
526       || sys->procamp.contrast != f->procamp.contrast
527       || sys->procamp.saturation != f->procamp.saturation
528       || sys->procamp.hue != f->procamp.hue)
529      && (MixerSetupColors(filter, &f->procamp, &csc) == VDP_STATUS_OK))
530     {
531         attrs[count] = VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX;
532         values[count] = &csc;
533         count++;
534     }
535
536     err = vdp_video_mixer_set_attribute_values(sys->vdp, sys->mixer,
537                                                count, attrs, values);
538     if (err != VDP_STATUS_OK)
539         msg_Err(filter, "video %s %s failure: %s", "mixer", "attributes",
540                 vdp_get_error_string(sys->vdp, err));
541
542     /* Check video orientation, allocate intermediate surface if needed */
543     bool swap = ORIENT_IS_SWAP(filter->fmt_in.video.orientation);
544     bool hflip = false, vflip = false;
545
546     switch (filter->fmt_in.video.orientation)
547     {
548         case ORIENT_TOP_LEFT:
549         case ORIENT_RIGHT_TOP:
550             break;
551         case ORIENT_TOP_RIGHT:
552         case ORIENT_RIGHT_BOTTOM:
553             hflip = true;
554             break;
555         case ORIENT_BOTTOM_LEFT:
556         case ORIENT_LEFT_TOP:
557             vflip = true;
558             break;
559         case ORIENT_BOTTOM_RIGHT:
560         case ORIENT_LEFT_BOTTOM:
561             vflip = hflip = true;
562             break;
563     }
564
565     VdpOutputSurface output = dst->p_sys->surface;
566
567     if (swap)
568     {
569         VdpRGBAFormat fmt;
570         uint32_t width, height;
571
572         err = vdp_output_surface_get_parameters(sys->vdp, output,
573                                                 &fmt, &width, &height);
574         if (err != VDP_STATUS_OK)
575         {
576             msg_Err(filter, "output %s %s failure: %s", "surface", "query",
577                     vdp_get_error_string(sys->vdp, err));
578             goto error;
579         }
580
581         err = vdp_output_surface_create(sys->vdp, sys->device,
582                                         fmt, height, width, &output);
583         if (err != VDP_STATUS_OK)
584         {
585             msg_Err(filter, "output %s %s failure: %s", "surface", "creation",
586                     vdp_get_error_string(sys->vdp, err));
587             goto error;
588         }
589     }
590
591     /* Render video into output */
592     VdpVideoMixerPictureStructure structure = f->structure;
593     VdpVideoSurface past[MAX_PAST];
594     VdpVideoSurface surface = f->frame->surface;
595     VdpVideoSurface future[MAX_FUTURE];
596     VdpRect src_rect = {
597         filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
598         filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset
599     };
600
601     if (hflip)
602         src_rect.x0 += filter->fmt_in.video.i_visible_width;
603     else
604         src_rect.x1 += filter->fmt_in.video.i_visible_width;
605     if (vflip)
606         src_rect.y0 += filter->fmt_in.video.i_visible_height;
607     else
608         src_rect.y1 += filter->fmt_in.video.i_visible_height;
609
610     VdpRect dst_rect = {
611         0, 0,
612         swap ? filter->fmt_out.video.i_visible_height
613              : filter->fmt_out.video.i_visible_width,
614         swap ? filter->fmt_out.video.i_visible_width
615              : filter->fmt_out.video.i_visible_height,
616     };
617
618     for (unsigned i = 0; i < MAX_PAST; i++)
619     {
620         f = sys->history[(MAX_PAST - 1) - i].field;
621         past[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
622     }
623     for (unsigned i = 0; i < MAX_FUTURE; i++)
624     {
625         f = sys->history[(MAX_PAST + 1) + i].field;
626         future[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
627     }
628
629     err = vdp_video_mixer_render(sys->vdp, sys->mixer, VDP_INVALID_HANDLE,
630                                  NULL, structure,
631                                  MAX_PAST, past, surface, MAX_FUTURE, future,
632                                  &src_rect, output, &dst_rect, NULL, 0, NULL);
633     if (err != VDP_STATUS_OK)
634     {
635         msg_Err(filter, "video %s %s failure: %s", "mixer", "rendering",
636                 vdp_get_error_string(sys->vdp, err));
637         goto error;
638     }
639
640     if (swap)
641     {
642         err = vdp_output_surface_render_output_surface(sys->vdp,
643             dst->p_sys->surface, NULL, output, NULL, NULL, NULL,
644             VDP_OUTPUT_SURFACE_RENDER_ROTATE_90);
645         vdp_output_surface_destroy(sys->vdp, output);
646         if (err != VDP_STATUS_OK)
647         {
648             msg_Err(filter, "output %s %s failure: %s", "surface", "render",
649                     vdp_get_error_string(sys->vdp, err));
650             goto error;
651         }
652     }
653
654 skip:
655     f = sys->history[0].field;
656     if (f != NULL)
657         f->destroy(f); /* Release oldest field */
658     memmove(sys->history, sys->history + 1, /* Advance history */
659             sizeof (sys->history[0]) * (MAX_PAST + MAX_FUTURE));
660
661     return dst;
662 error:
663     picture_Release(dst);
664     dst = NULL;
665     goto skip;
666 }
667
668 static picture_t *YCbCrRender(filter_t *filter, picture_t *src)
669 {
670     src = VideoImport(filter, src);
671     return (src != NULL) ? VideoRender(filter, src) : NULL;
672 }
673
674 static int OutputOpen(vlc_object_t *obj)
675 {
676     filter_t *filter = (filter_t *)obj;
677
678     if (filter->fmt_out.video.i_chroma != VLC_CODEC_VDPAU_OUTPUT)
679         return VLC_EGENERIC;
680
681     assert(filter->fmt_out.video.orientation == ORIENT_TOP_LEFT);
682
683     filter_sys_t *sys = malloc(sizeof (*sys));
684     if (unlikely(sys == NULL))
685         return VLC_ENOMEM;
686
687     filter->p_sys = sys;
688
689     picture_t *(*video_filter)(filter_t *, picture_t *) = VideoRender;
690
691     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_444)
692     {
693         sys->chroma = VDP_CHROMA_TYPE_444;
694         sys->format = VDP_YCBCR_FORMAT_NV12;
695     }
696     else
697     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_422)
698     {
699         sys->chroma = VDP_CHROMA_TYPE_422;
700         /* TODO: check if the drivery supports NV12 or UYVY */
701         sys->format = VDP_YCBCR_FORMAT_UYVY;
702     }
703     else
704     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_420)
705     {
706         sys->chroma = VDP_CHROMA_TYPE_420;
707         sys->format = VDP_YCBCR_FORMAT_NV12;
708     }
709     else
710     if (vlc_fourcc_to_vdp_ycc(filter->fmt_in.video.i_chroma,
711                               &sys->chroma, &sys->format))
712         video_filter = YCbCrRender;
713     else
714         goto error;
715
716     /* Get the context and allocate the mixer (through *ahem* picture) */
717     picture_t *pic = filter_NewPicture(filter);
718     if (pic == NULL)
719         goto error;
720
721     picture_sys_t *picsys = pic->p_sys;
722     assert(picsys != NULL && picsys->vdp != NULL);
723
724     sys->vdp = vdp_hold_x11(picsys->vdp, NULL);
725     sys->device = picsys->device;
726     picture_Release(pic);
727
728     sys->mixer = MixerCreate(filter);
729     if (sys->mixer == VDP_INVALID_HANDLE)
730     {
731         vdp_release_x11(sys->vdp);
732         goto error;
733     }
734
735     /* NOTE: The video mixer capabilities should be checked here, and the
736      * then video mixer set up. But:
737      * 1) The VDPAU back-end is accessible only once the first picture
738      *    gets filtered. Thus the video mixer is created later.
739      * 2) Bailing out due to insufficient capabilities would break the
740      *    video pipeline. Thus capabilities should be checked earlier. */
741
742     for (unsigned i = 0; i < MAX_PAST + MAX_FUTURE; i++)
743         sys->history[i].field = NULL;
744
745     sys->procamp.brightness = 0.f;
746     sys->procamp.contrast = 1.f;
747     sys->procamp.saturation = 1.f;
748     sys->procamp.hue = 0.f;
749
750     filter->pf_video_filter = video_filter;
751     filter->pf_video_flush = Flush;
752     return VLC_SUCCESS;
753 error:
754     free(sys);
755     return VLC_EGENERIC;
756 }
757
758 static void OutputClose(vlc_object_t *obj)
759 {
760     filter_t *filter = (filter_t *)obj;
761     filter_sys_t *sys = filter->p_sys;
762
763     Flush(filter);
764     vdp_video_mixer_destroy(sys->vdp, sys->mixer);
765     vdp_release_x11(sys->vdp);
766     free(sys);
767 }
768
769 static picture_t *VideoExport_Filter(filter_t *filter, picture_t *src)
770 {
771     if (unlikely(src->context == NULL))
772     {
773         msg_Err(filter, "corrupt VDPAU video surface %p", src);
774         picture_Release(src);
775         return NULL;
776     }
777
778     picture_t *dst = filter_NewPicture(filter);
779     if (dst == NULL)
780         return NULL;
781
782     return VideoExport(filter, src, dst);
783 }
784
785 static int YCbCrOpen(vlc_object_t *obj)
786 {
787     filter_t *filter = (filter_t *)obj;
788     if (filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_420
789      && filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_422
790      && filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_444)
791         return VLC_EGENERIC;
792
793     if (filter->fmt_in.video.i_visible_width
794                                        != filter->fmt_out.video.i_visible_width
795      || filter->fmt_in.video.i_visible_height
796                                       != filter->fmt_out.video.i_visible_height
797      || filter->fmt_in.video.i_x_offset != filter->fmt_out.video.i_x_offset
798      || filter->fmt_in.video.i_y_offset != filter->fmt_out.video.i_y_offset
799      || (filter->fmt_in.video.i_sar_num * filter->fmt_out.video.i_sar_den
800           != filter->fmt_in.video.i_sar_den * filter->fmt_out.video.i_sar_num))
801         return VLC_EGENERIC;
802
803     filter_sys_t *sys = malloc(sizeof (*sys));
804     if (unlikely(sys == NULL))
805         return VLC_ENOMEM;
806
807     if (!vlc_fourcc_to_vdp_ycc(filter->fmt_out.video.i_chroma,
808                                &sys->chroma, &sys->format))
809     {
810         free(sys);
811         return VLC_EGENERIC;
812     }
813
814     filter->pf_video_filter = VideoExport_Filter;
815     filter->p_sys = sys;
816     return VLC_SUCCESS;
817 }
818
819 static void YCbCrClose(vlc_object_t *obj)
820 {
821     filter_t *filter = (filter_t *)obj;
822     filter_sys_t *sys = filter->p_sys;
823
824     free(sys);
825 }
826
827 static const int algo_values[] = {
828     -1,
829     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
830     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
831 };
832
833 static const char *const algo_names[] = {
834     N_("Bob"), N_("Temporal"), N_("Temporal-spatial"),
835 };
836
837 vlc_module_begin()
838     set_shortname(N_("VDPAU"))
839     set_description(N_("VDPAU surface conversions"))
840     set_capability("video filter2", 10)
841     set_category(CAT_VIDEO)
842     set_subcategory(SUBCAT_VIDEO_VFILTER)
843     set_callbacks(OutputOpen, OutputClose)
844
845     add_integer("vdpau-deinterlace",
846                 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
847                 N_("Deinterlace"), N_("Deinterlacing algorithm"), true)
848         change_integer_list(algo_values, algo_names)
849     add_bool("vdpau-ivtc", false,
850              N_("Inverse telecine"), N_("Inverse telecine"), true)
851     add_bool("vdpau-chroma-skip", false,
852              N_("Deinterlace chroma skip"),
853              N_("Whether temporal deinterlacing applies to luma only"), true)
854     add_float_with_range("vdpau-noise-reduction", 0., 0., 1.,
855         N_("Noise reduction level"), N_("Noise reduction level"), true)
856     add_integer_with_range("vdpau-scaling", 0, 0, 9,
857        N_("Scaling quality"), N_("High quality scaling level"), true)
858
859     add_submodule()
860     set_callbacks(YCbCrOpen, YCbCrClose)
861 vlc_module_end()