1 /*****************************************************************************
2 * chroma.c: VLC picture import into VDPAU
3 *****************************************************************************
4 * Copyright (C) 2013 RĂ©mi Denis-Courmont
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.
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.
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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_filter.h>
32 #include "vlc_vdpau.h"
34 /* Picture history as recommended by VDPAU documentation */
44 VdpYCbCrFormat format;
48 vlc_vdp_video_field_t *field;
51 } history[MAX_PAST + 1 + MAX_FUTURE];
62 /** Initialize the colour space conversion matrix */
63 static VdpStatus MixerSetupColors(filter_t *filter, const VdpProcamp *procamp,
64 VdpCSCMatrix *restrict csc)
66 filter_sys_t *sys = filter->p_sys;
68 VdpColorStandard std = (filter->fmt_in.video.i_height > 576)
69 ? VDP_COLOR_STANDARD_ITUR_BT_709
70 : VDP_COLOR_STANDARD_ITUR_BT_601;
72 err = vdp_generate_csc_matrix(sys->vdp, procamp, std, csc);
73 if (err != VDP_STATUS_OK)
75 msg_Err(filter, "video %s failure: %s", "color space matrix",
76 vdp_get_error_string(sys->vdp, err));
82 sys->procamp.brightness = procamp->brightness;
83 sys->procamp.contrast = procamp->contrast;
84 sys->procamp.saturation = procamp->saturation;
85 sys->procamp.hue = procamp->hue;
89 sys->procamp.brightness = 0.f;
90 sys->procamp.contrast = 1.f;
91 sys->procamp.saturation = 1.f;
92 sys->procamp.hue = 0.f;
97 /** Create VDPAU video mixer */
98 static VdpVideoMixer MixerCreate(filter_t *filter)
100 filter_sys_t *sys = filter->p_sys;
105 /* Check for potentially useful features */
106 VdpVideoMixerFeature featv[5];
109 int algo = var_InheritInteger(filter, "vdpau-deinterlace");
111 if (algo == VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL)
113 err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
115 if (err == VDP_STATUS_OK && ok == VDP_TRUE)
116 msg_Dbg(filter, "using video mixer %s feature",
117 "temporal-spatial deinterlace");
119 algo = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL; /* fallback */
121 if (algo == VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL)
123 err = vdp_video_mixer_query_feature_support(sys->vdp, sys->device,
125 if (err == VDP_STATUS_OK && ok == VDP_TRUE)
126 msg_Dbg(filter, "using video mixer %s feature",
127 "temporal deinterlace");
133 featv[featc++] = algo;
134 ivtc = var_InheritBool(filter, "vdpau-ivtc");
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",
142 featv[featc++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
146 const float noise = var_InheritFloat(filter, "vdpau-noise-reduction");
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)
153 msg_Dbg(filter, "using video mixer %s feature", "noise reduction");
154 featv[featc++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
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)
162 msg_Dbg(filter, "using video mixer %s feature", "sharpness");
163 featv[featc++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
166 const int offset = VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 - 1;
167 unsigned level = var_InheritInteger(filter, "vdpau-scaling");
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)
175 msg_Dbg(filter, "using video mixer high quality scaling L%u",
177 featv[featc++] = offset + level;
180 level--; /* fallback to lower quality */
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,
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, };
193 err = vdp_video_mixer_create(sys->vdp, sys->device, featc, featv,
194 3, parms, values, &mixer);
195 if (err != VDP_STATUS_OK)
197 msg_Err(filter, "video %s %s failure: %s", "mixer", "creation",
198 vdp_get_error_string(sys->vdp, err));
199 return VDP_INVALID_HANDLE;
202 msg_Dbg(filter, "using video mixer %"PRIu32, mixer);
204 /* Set initial features and attributes */
205 VdpVideoMixerAttribute attrv[3];
213 if (MixerSetupColors(filter, NULL, &csc) == VDP_STATUS_OK)
215 attrv[attrc] = VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX;
222 featv[featc++] = algo;
224 featv[featc++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
226 chroma_skip = var_InheritBool(filter, "vdpau-chroma-skip");
227 attrv[attrc] = VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE;
228 valv[attrc] = &chroma_skip;
234 featv[featc++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
236 attrv[attrc] = VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL;
237 valv[attrc] = &noise;
242 featv[featc++] = offset + level;
246 VdpBool enablev[featc];
248 for (unsigned i = 0; i < featc; i++)
249 enablev[i] = VDP_TRUE;
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));
260 err = vdp_video_mixer_set_attribute_values(sys->vdp, mixer,
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));
270 static void Flush(filter_t *filter)
272 filter_sys_t *sys = filter->p_sys;
274 for (unsigned i = 0; i < MAX_PAST + MAX_FUTURE; i++)
275 if (sys->history[i].field != NULL)
277 sys->history[i].field->destroy(sys->history[i].field);
278 sys->history[i].field = NULL;
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)
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;
289 VdpVideoSurface surface = psys->surface;
293 picture_CopyProperties(dst, src);
295 for (int i = 0; i < dst->i_planes; i++)
297 planes[i] = dst->p[i].p_pixels;
298 pitches[i] = dst->p[i].i_pitch;
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)
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;
309 err = vdp_video_surface_get_bits_y_cb_cr(psys->vdp, surface, sys->format,
311 if (err != VDP_STATUS_OK)
313 msg_Err(filter, "video %s %s failure: %s", "surface", "export",
314 vdp_get_error_string(psys->vdp, err));
315 picture_Release(dst);
318 picture_Release(src);
322 /** Import VLC picture into VDPAU video surface */
323 static picture_t *VideoImport(filter_t *filter, picture_t *src)
325 filter_sys_t *sys = filter->p_sys;
326 VdpVideoSurface surface;
329 if (sys->vdp == NULL)
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)
338 msg_Err(filter, "video %s %s failure: %s", "surface", "creation",
339 vdp_get_error_string(sys->vdp, err));
344 const void *planes[3];
346 for (int i = 0; i < src->i_planes; i++)
348 planes[i] = src->p[i].p_pixels;
349 pitches[i] = src->p[i].i_pitch;
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)
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;
360 err = vdp_video_surface_put_bits_y_cb_cr(sys->vdp, surface, sys->format,
362 if (err != VDP_STATUS_OK)
364 msg_Err(filter, "video %s %s failure: %s", "surface", "import",
365 vdp_get_error_string(sys->vdp, err));
369 /* Wrap surface into a picture */
370 video_format_t fmt = src->format;
374 case VDP_CHROMA_TYPE_420:
375 fmt.i_chroma = VLC_CODEC_VDPAU_VIDEO_420;
377 case VDP_CHROMA_TYPE_422:
378 fmt.i_chroma = VLC_CODEC_VDPAU_VIDEO_422;
380 case VDP_CHROMA_TYPE_444:
381 fmt.i_chroma = VLC_CODEC_VDPAU_VIDEO_444;
384 vlc_assert_unreachable();
387 picture_t *dst = picture_NewFromFormat(&fmt);
388 if (unlikely(dst == NULL))
390 picture_CopyProperties(dst, src);
391 picture_Release(src);
393 err = vlc_vdp_video_attach(sys->vdp, surface, dst);
394 if (unlikely(err != VDP_STATUS_OK))
396 picture_Release(dst);
401 vdp_video_surface_destroy(sys->vdp, surface);
403 picture_Release(src);
407 static inline VdpVideoSurface picture_GetVideoSurface(const picture_t *pic)
409 vlc_vdp_video_field_t *field = pic->context;
410 return field->frame->surface;
413 static picture_t *VideoRender(filter_t *filter, picture_t *src)
415 filter_sys_t *sys = filter->p_sys;
416 picture_t *dst = NULL;
419 if (unlikely(src->context == NULL))
421 msg_Err(filter, "corrupt VDPAU video surface %p", src);
422 picture_Release(src);
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)
430 video_format_t fmt = src->format;
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();
439 picture_t *pic = picture_NewFromFormat(&fmt);
440 if (likely(pic != NULL))
442 pic = VideoExport(filter, src, pic);
444 src = VideoImport(filter, pic);
450 picture_Release(src);
455 /* Update history and take "present" picture field */
456 if (likely(src != NULL))
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);
466 sys->history[MAX_PAST + MAX_FUTURE].field = NULL;
467 sys->history[MAX_PAST + MAX_FUTURE].force = false;
470 vlc_vdp_video_field_t *f = sys->history[MAX_PAST].field;
472 { /* There is no present field, probably just starting playback. */
473 if (!sys->history[MAX_PAST + MAX_FUTURE].force)
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)
481 f = sys->history[0].field;
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;
489 f = sys->history[MAX_PAST].field;
492 /* Get a VLC picture for a VDPAU output surface */
493 dst = filter_NewPicture(filter);
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;
501 /* Enable/Disable features */
502 const VdpVideoMixerFeature features[] = {
503 VDP_VIDEO_MIXER_FEATURE_SHARPNESS,
505 const VdpBool enables[] = {
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));
515 /* Configure mixer depending on upstream video filters */
516 VdpVideoMixerAttribute attrs[2] = {
517 VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL,
519 const void *values[2] = {
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))
531 attrs[count] = VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX;
532 values[count] = &csc;
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));
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;
546 switch (filter->fmt_in.video.orientation)
548 case ORIENT_TOP_LEFT:
549 case ORIENT_RIGHT_TOP:
551 case ORIENT_TOP_RIGHT:
552 case ORIENT_RIGHT_BOTTOM:
555 case ORIENT_BOTTOM_LEFT:
556 case ORIENT_LEFT_TOP:
559 case ORIENT_BOTTOM_RIGHT:
560 case ORIENT_LEFT_BOTTOM:
561 vflip = hflip = true;
565 VdpOutputSurface output = dst->p_sys->surface;
570 uint32_t width, height;
572 err = vdp_output_surface_get_parameters(sys->vdp, output,
573 &fmt, &width, &height);
574 if (err != VDP_STATUS_OK)
576 msg_Err(filter, "output %s %s failure: %s", "surface", "query",
577 vdp_get_error_string(sys->vdp, err));
581 err = vdp_output_surface_create(sys->vdp, sys->device,
582 fmt, height, width, &output);
583 if (err != VDP_STATUS_OK)
585 msg_Err(filter, "output %s %s failure: %s", "surface", "creation",
586 vdp_get_error_string(sys->vdp, err));
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];
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
602 src_rect.x0 += filter->fmt_in.video.i_visible_width;
604 src_rect.x1 += filter->fmt_in.video.i_visible_width;
606 src_rect.y0 += filter->fmt_in.video.i_visible_height;
608 src_rect.y1 += filter->fmt_in.video.i_visible_height;
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,
618 for (unsigned i = 0; i < MAX_PAST; i++)
620 f = sys->history[(MAX_PAST - 1) - i].field;
621 past[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
623 for (unsigned i = 0; i < MAX_FUTURE; i++)
625 f = sys->history[(MAX_PAST + 1) + i].field;
626 future[i] = (f != NULL) ? f->frame->surface : VDP_INVALID_HANDLE;
629 err = vdp_video_mixer_render(sys->vdp, sys->mixer, VDP_INVALID_HANDLE,
631 MAX_PAST, past, surface, MAX_FUTURE, future,
632 &src_rect, output, &dst_rect, NULL, 0, NULL);
633 if (err != VDP_STATUS_OK)
635 msg_Err(filter, "video %s %s failure: %s", "mixer", "rendering",
636 vdp_get_error_string(sys->vdp, err));
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)
648 msg_Err(filter, "output %s %s failure: %s", "surface", "render",
649 vdp_get_error_string(sys->vdp, err));
655 f = sys->history[0].field;
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));
663 picture_Release(dst);
668 static picture_t *YCbCrRender(filter_t *filter, picture_t *src)
670 src = VideoImport(filter, src);
671 return (src != NULL) ? VideoRender(filter, src) : NULL;
674 static int OutputOpen(vlc_object_t *obj)
676 filter_t *filter = (filter_t *)obj;
678 if (filter->fmt_out.video.i_chroma != VLC_CODEC_VDPAU_OUTPUT)
681 assert(filter->fmt_out.video.orientation == ORIENT_TOP_LEFT);
683 filter_sys_t *sys = malloc(sizeof (*sys));
684 if (unlikely(sys == NULL))
689 picture_t *(*video_filter)(filter_t *, picture_t *) = VideoRender;
691 if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_444)
693 sys->chroma = VDP_CHROMA_TYPE_444;
694 sys->format = VDP_YCBCR_FORMAT_NV12;
697 if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_422)
699 sys->chroma = VDP_CHROMA_TYPE_422;
700 /* TODO: check if the drivery supports NV12 or UYVY */
701 sys->format = VDP_YCBCR_FORMAT_UYVY;
704 if (filter->fmt_in.video.i_chroma == VLC_CODEC_VDPAU_VIDEO_420)
706 sys->chroma = VDP_CHROMA_TYPE_420;
707 sys->format = VDP_YCBCR_FORMAT_NV12;
710 if (vlc_fourcc_to_vdp_ycc(filter->fmt_in.video.i_chroma,
711 &sys->chroma, &sys->format))
712 video_filter = YCbCrRender;
716 /* Get the context and allocate the mixer (through *ahem* picture) */
717 picture_t *pic = filter_NewPicture(filter);
721 picture_sys_t *picsys = pic->p_sys;
722 assert(picsys != NULL && picsys->vdp != NULL);
724 sys->vdp = vdp_hold_x11(picsys->vdp, NULL);
725 sys->device = picsys->device;
726 picture_Release(pic);
728 sys->mixer = MixerCreate(filter);
729 if (sys->mixer == VDP_INVALID_HANDLE)
731 vdp_release_x11(sys->vdp);
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. */
742 for (unsigned i = 0; i < MAX_PAST + MAX_FUTURE; i++)
743 sys->history[i].field = NULL;
745 sys->procamp.brightness = 0.f;
746 sys->procamp.contrast = 1.f;
747 sys->procamp.saturation = 1.f;
748 sys->procamp.hue = 0.f;
750 filter->pf_video_filter = video_filter;
751 filter->pf_video_flush = Flush;
758 static void OutputClose(vlc_object_t *obj)
760 filter_t *filter = (filter_t *)obj;
761 filter_sys_t *sys = filter->p_sys;
764 vdp_video_mixer_destroy(sys->vdp, sys->mixer);
765 vdp_release_x11(sys->vdp);
769 static picture_t *VideoExport_Filter(filter_t *filter, picture_t *src)
771 if (unlikely(src->context == NULL))
773 msg_Err(filter, "corrupt VDPAU video surface %p", src);
774 picture_Release(src);
778 picture_t *dst = filter_NewPicture(filter);
782 return VideoExport(filter, src, dst);
785 static int YCbCrOpen(vlc_object_t *obj)
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)
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))
803 filter_sys_t *sys = malloc(sizeof (*sys));
804 if (unlikely(sys == NULL))
807 if (!vlc_fourcc_to_vdp_ycc(filter->fmt_out.video.i_chroma,
808 &sys->chroma, &sys->format))
814 filter->pf_video_filter = VideoExport_Filter;
819 static void YCbCrClose(vlc_object_t *obj)
821 filter_t *filter = (filter_t *)obj;
822 filter_sys_t *sys = filter->p_sys;
827 static const int algo_values[] = {
829 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
830 VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
833 static const char *const algo_names[] = {
834 N_("Bob"), N_("Temporal"), N_("Temporal-spatial"),
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)
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)
860 set_callbacks(YCbCrOpen, YCbCrClose)