vdpau/chroma: set up device and mixer from the init function
[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 /** 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             assert(0);
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: assert(0);
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         sys->history[MAX_PAST + MAX_FUTURE].field = NULL;
466
467     vlc_vdp_video_field_t *f = sys->history[MAX_PAST].field;
468     if (f == NULL)
469         goto skip;
470
471     /* Get a VLC picture for a VDPAU output surface */
472     dst = filter_NewPicture(filter);
473     if (dst == NULL)
474         goto skip;
475
476     assert(dst->p_sys != NULL && dst->p_sys->vdp ==sys->vdp);
477     dst->date = sys->history[MAX_PAST].date;
478     dst->b_force = sys->history[MAX_PAST].force;
479
480     /* Enable/Disable features */
481     const VdpVideoMixerFeature features[] = {
482         VDP_VIDEO_MIXER_FEATURE_SHARPNESS,
483     };
484     const VdpBool enables[] = {
485         f->sharpen != 0.f,
486     };
487
488     err = vdp_video_mixer_set_feature_enables(sys->vdp, sys->mixer,
489                   sizeof (features) / sizeof (features[0]), features, enables);
490     if (err != VDP_STATUS_OK)
491         msg_Err(filter, "video %s %s failure: %s", "mixer", "features",
492                 vdp_get_error_string(sys->vdp, err));
493
494     /* Configure mixer depending on upstream video filters */
495     VdpVideoMixerAttribute attrs[2] = {
496         VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL,
497     };
498     const void *values[2] = {
499         &f->sharpen,
500     };
501     unsigned count = 1;
502     VdpCSCMatrix csc;
503
504     if ((sys->procamp.brightness != f->procamp.brightness
505       || sys->procamp.contrast != f->procamp.contrast
506       || sys->procamp.saturation != f->procamp.saturation
507       || sys->procamp.hue != f->procamp.hue)
508      && (MixerSetupColors(filter, &f->procamp, &csc) == VDP_STATUS_OK))
509     {
510         attrs[count] = VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX;
511         values[count] = &csc;
512         count++;
513     }
514
515     err = vdp_video_mixer_set_attribute_values(sys->vdp, sys->mixer,
516                                                count, attrs, values);
517     if (err != VDP_STATUS_OK)
518         msg_Err(filter, "video %s %s failure: %s", "mixer", "attributes",
519                 vdp_get_error_string(sys->vdp, err));
520
521     /* Check video orientation, allocate intermediate surface if needed */
522     bool swap = ORIENT_IS_SWAP(filter->fmt_in.video.orientation);
523     bool hflip = false, vflip = false;
524
525     switch (filter->fmt_in.video.orientation)
526     {
527         case ORIENT_TOP_LEFT:
528         case ORIENT_RIGHT_TOP:
529             break;
530         case ORIENT_TOP_RIGHT:
531         case ORIENT_RIGHT_BOTTOM:
532             hflip = true;
533             break;
534         case ORIENT_BOTTOM_LEFT:
535         case ORIENT_LEFT_TOP:
536             vflip = true;
537             break;
538         case ORIENT_BOTTOM_RIGHT:
539         case ORIENT_LEFT_BOTTOM:
540             vflip = hflip = true;
541             break;
542     }
543
544     VdpOutputSurface output = dst->p_sys->surface;
545
546     if (swap)
547     {
548         VdpRGBAFormat fmt;
549         uint32_t width, height;
550
551         err = vdp_output_surface_get_parameters(sys->vdp, output,
552                                                 &fmt, &width, &height);
553         if (err != VDP_STATUS_OK)
554         {
555             msg_Err(filter, "output %s %s failure: %s", "surface", "query",
556                     vdp_get_error_string(sys->vdp, err));
557             goto error;
558         }
559
560         err = vdp_output_surface_create(sys->vdp, sys->device,
561                                         fmt, height, width, &output);
562         if (err != VDP_STATUS_OK)
563         {
564             msg_Err(filter, "output %s %s failure: %s", "surface", "creation",
565                     vdp_get_error_string(sys->vdp, err));
566             goto error;
567         }
568     }
569
570     /* Render video into output */
571     VdpVideoMixerPictureStructure structure = f->structure;
572     VdpVideoSurface past[MAX_PAST];
573     VdpVideoSurface surface = f->frame->surface;
574     VdpVideoSurface future[MAX_FUTURE];
575     VdpRect src_rect = {
576         filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
577         filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset
578     };
579
580     if (hflip)
581         src_rect.x0 += filter->fmt_in.video.i_visible_width;
582     else
583         src_rect.x1 += filter->fmt_in.video.i_visible_width;
584     if (vflip)
585         src_rect.y0 += filter->fmt_in.video.i_visible_height;
586     else
587         src_rect.y1 += filter->fmt_in.video.i_visible_height;
588
589     VdpRect dst_rect = {
590         0, 0,
591         swap ? filter->fmt_out.video.i_visible_height
592              : filter->fmt_out.video.i_visible_width,
593         swap ? filter->fmt_out.video.i_visible_width
594              : filter->fmt_out.video.i_visible_height,
595     };
596
597     for (unsigned i = 0; i < MAX_PAST; i++)
598     {
599         f = sys->history[(MAX_PAST - 1) - i].field;
600         past[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
601     }
602     for (unsigned i = 0; i < MAX_FUTURE; i++)
603     {
604         f = sys->history[(MAX_PAST + 1) + i].field;
605         future[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
606     }
607
608     err = vdp_video_mixer_render(sys->vdp, sys->mixer, VDP_INVALID_HANDLE,
609                                  NULL, structure,
610                                  MAX_PAST, past, surface, MAX_FUTURE, future,
611                                  &src_rect, output, &dst_rect, NULL, 0, NULL);
612     if (err != VDP_STATUS_OK)
613     {
614         msg_Err(filter, "video %s %s failure: %s", "mixer", "rendering",
615                 vdp_get_error_string(sys->vdp, err));
616         goto error;
617     }
618
619     if (swap)
620     {
621         err = vdp_output_surface_render_output_surface(sys->vdp,
622             dst->p_sys->surface, NULL, output, NULL, NULL, NULL,
623             VDP_OUTPUT_SURFACE_RENDER_ROTATE_90);
624         vdp_output_surface_destroy(sys->vdp, output);
625         if (err != VDP_STATUS_OK)
626         {
627             msg_Err(filter, "output %s %s failure: %s", "surface", "render",
628                     vdp_get_error_string(sys->vdp, err));
629             goto error;
630         }
631     }
632
633 skip:
634     f = sys->history[0].field;
635     if (f != NULL)
636         f->destroy(f); /* Release oldest field */
637     memmove(sys->history, sys->history + 1, /* Advance history */
638             sizeof (sys->history[0]) * (MAX_PAST + MAX_FUTURE));
639
640     return dst;
641 error:
642     picture_Release(dst);
643     dst = NULL;
644     goto skip;
645 }
646
647 static picture_t *YCbCrRender(filter_t *filter, picture_t *src)
648 {
649     src = VideoImport(filter, src);
650     return (src != NULL) ? VideoRender(filter, src) : NULL;
651 }
652
653 static int OutputOpen(vlc_object_t *obj)
654 {
655     filter_t *filter = (filter_t *)obj;
656
657     if (filter->fmt_out.video.i_chroma != VLC_CODEC_VDPAU_OUTPUT)
658         return VLC_EGENERIC;
659
660     assert(filter->fmt_out.video.orientation == ORIENT_TOP_LEFT);
661
662     filter_sys_t *sys = malloc(sizeof (*sys));
663     if (unlikely(sys == NULL))
664         return VLC_ENOMEM;
665
666     filter->p_sys = sys;
667
668     picture_t *(*video_filter)(filter_t *, picture_t *) = VideoRender;
669
670     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_444)
671     {
672         sys->chroma = VDP_CHROMA_TYPE_444;
673         sys->format = VDP_YCBCR_FORMAT_NV12;
674     }
675     else
676     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_422)
677     {
678         sys->chroma = VDP_CHROMA_TYPE_422;
679         /* TODO: check if the drivery supports NV12 or UYVY */
680         sys->format = VDP_YCBCR_FORMAT_UYVY;
681     }
682     else
683     if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_420)
684     {
685         sys->chroma = VDP_CHROMA_TYPE_420;
686         sys->format = VDP_YCBCR_FORMAT_NV12;
687     }
688     else
689     if (vlc_fourcc_to_vdp_ycc(filter->fmt_in.video.i_chroma,
690                               &sys->chroma, &sys->format))
691         video_filter = YCbCrRender;
692     else
693         goto error;
694
695     /* Get the context and allocate the mixer (through *ahem* picture) */
696     picture_t *pic = filter_NewPicture(filter);
697     if (pic == NULL)
698         goto error;
699
700     picture_sys_t *picsys = pic->p_sys;
701     assert(picsys != NULL && picsys->vdp != NULL);
702
703     sys->vdp = vdp_hold_x11(picsys->vdp, NULL);
704     sys->device = picsys->device;
705     picture_Release(pic);
706
707     sys->mixer = MixerCreate(filter);
708     if (sys->mixer == VDP_INVALID_HANDLE)
709     {
710         vdp_release_x11(sys->vdp);
711         goto error;
712     }
713
714     /* NOTE: The video mixer capabilities should be checked here, and the
715      * then video mixer set up. But:
716      * 1) The VDPAU back-end is accessible only once the first picture
717      *    gets filtered. Thus the video mixer is created later.
718      * 2) Bailing out due to insufficient capabilities would break the
719      *    video pipeline. Thus capabilities should be checked earlier. */
720
721     for (unsigned i = 0; i < MAX_PAST + MAX_FUTURE; i++)
722         sys->history[i].field = NULL;
723
724     sys->procamp.brightness = 0.f;
725     sys->procamp.contrast = 1.f;
726     sys->procamp.saturation = 1.f;
727     sys->procamp.hue = 0.f;
728
729     filter->pf_video_filter = video_filter;
730     filter->pf_video_flush = Flush;
731     return VLC_SUCCESS;
732 error:
733     free(sys);
734     return VLC_EGENERIC;
735 }
736
737 static void OutputClose(vlc_object_t *obj)
738 {
739     filter_t *filter = (filter_t *)obj;
740     filter_sys_t *sys = filter->p_sys;
741
742     Flush(filter);
743     vdp_video_mixer_destroy(sys->vdp, sys->mixer);
744     vdp_release_x11(sys->vdp);
745     free(sys);
746 }
747
748 static picture_t *VideoExport_Filter(filter_t *filter, picture_t *src)
749 {
750     if (unlikely(src->context == NULL))
751     {
752         msg_Err(filter, "corrupt VDPAU video surface %p", src);
753         picture_Release(src);
754         return NULL;
755     }
756
757     picture_t *dst = filter_NewPicture(filter);
758     if (dst == NULL)
759         return NULL;
760
761     return VideoExport(filter, src, dst);
762 }
763
764 static int YCbCrOpen(vlc_object_t *obj)
765 {
766     filter_t *filter = (filter_t *)obj;
767     if (filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_420
768      && filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_422
769      && filter->fmt_in.video.i_chroma != VLC_CODEC_VDPAU_VIDEO_444)
770         return VLC_EGENERIC;
771
772     if (filter->fmt_in.video.i_visible_width
773                                        != filter->fmt_out.video.i_visible_width
774      || filter->fmt_in.video.i_visible_height
775                                       != filter->fmt_out.video.i_visible_height
776      || filter->fmt_in.video.i_x_offset != filter->fmt_out.video.i_x_offset
777      || filter->fmt_in.video.i_y_offset != filter->fmt_out.video.i_y_offset
778      || (filter->fmt_in.video.i_sar_num * filter->fmt_out.video.i_sar_den
779           != filter->fmt_in.video.i_sar_den * filter->fmt_out.video.i_sar_num))
780         return VLC_EGENERIC;
781
782     filter_sys_t *sys = malloc(sizeof (*sys));
783     if (unlikely(sys == NULL))
784         return VLC_ENOMEM;
785
786     if (!vlc_fourcc_to_vdp_ycc(filter->fmt_out.video.i_chroma,
787                                &sys->chroma, &sys->format))
788     {
789         free(sys);
790         return VLC_EGENERIC;
791     }
792
793     filter->pf_video_filter = VideoExport_Filter;
794     filter->p_sys = sys;
795     return VLC_SUCCESS;
796 }
797
798 static void YCbCrClose(vlc_object_t *obj)
799 {
800     filter_t *filter = (filter_t *)obj;
801     filter_sys_t *sys = filter->p_sys;
802
803     free(sys);
804 }
805
806 static const int algo_values[] = {
807     -1,
808     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
809     VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
810 };
811
812 static const char *const algo_names[] = {
813     N_("Bob"), N_("Temporal"), N_("Temporal-spatial"),
814 };
815
816 vlc_module_begin()
817     set_shortname(N_("VDPAU"))
818     set_description(N_("VDPAU surface conversions"))
819     set_capability("video filter2", 10)
820     set_category(CAT_VIDEO)
821     set_subcategory(SUBCAT_VIDEO_VFILTER)
822     set_callbacks(OutputOpen, OutputClose)
823
824     add_integer("vdpau-deinterlace",
825                 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
826                 N_("Deinterlace"), N_("Deinterlacing algorithm"), true)
827         change_integer_list(algo_values, algo_names)
828     add_bool("vdpau-ivtc", false,
829              N_("Inverse telecine"), N_("Inverse telecine"), true)
830     add_bool("vdpau-chroma-skip", false,
831              N_("Deinterlace chroma skip"),
832              N_("Whether temporal deinterlacing applies to luma only"), true)
833     add_float_with_range("vdpau-noise-reduction", 0., 0., 1.,
834         N_("Noise reduction level"), N_("Noise reduction level"), true)
835     add_integer_with_range("vdpau-scaling", 0, 0, 9,
836        N_("Scaling quality"), N_("High quality scaling level"), true)
837
838     add_submodule()
839     set_callbacks(YCbCrOpen, YCbCrClose)
840 vlc_module_end()