summaryrefslogtreecommitdiffstats
path: root/video/vdpau_mixer.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/vdpau_mixer.c')
-rw-r--r--video/vdpau_mixer.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/video/vdpau_mixer.c b/video/vdpau_mixer.c
new file mode 100644
index 0000000..b1aed70
--- /dev/null
+++ b/video/vdpau_mixer.c
@@ -0,0 +1,306 @@
+/*
+ * This file is part of mpv.
+ *
+ * Parts of video mixer creation code:
+ * Copyright (C) 2008 NVIDIA (Rajib Mahapatra <rmahapatra@nvidia.com>)
+ * Copyright (C) 2009 Uoti Urpala
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+
+#include "vdpau_mixer.h"
+
+static void free_mixed_frame(void *arg)
+{
+ struct mp_vdpau_mixer_frame *frame = arg;
+ talloc_free(frame);
+}
+
+// This creates an image of format IMGFMT_VDPAU with a mp_vdpau_mixer_frame
+// struct. Use mp_vdpau_mixed_frame_get() to retrieve the struct and to
+// initialize it.
+// "base" is used only to set parameters, no image data is referenced.
+struct mp_image *mp_vdpau_mixed_frame_create(struct mp_image *base)
+{
+ assert(base->imgfmt == IMGFMT_VDPAU);
+
+ struct mp_vdpau_mixer_frame *frame =
+ talloc_zero(NULL, struct mp_vdpau_mixer_frame);
+ for (int n = 0; n < MP_VDP_HISTORY_FRAMES; n++)
+ frame->past[n] = frame->future[n] = VDP_INVALID_HANDLE;
+ frame->current = VDP_INVALID_HANDLE;
+ frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+
+ struct mp_image *mpi = mp_image_new_custom_ref(base, frame, free_mixed_frame);
+ if (mpi) {
+ mpi->planes[2] = (void *)frame;
+ mpi->planes[3] = (void *)(uintptr_t)VDP_INVALID_HANDLE;
+ }
+ return mpi;
+}
+
+struct mp_vdpau_mixer_frame *mp_vdpau_mixed_frame_get(struct mp_image *mpi)
+{
+ if (mpi->imgfmt != IMGFMT_VDPAU)
+ return NULL;
+ return (void *)mpi->planes[2];
+}
+
+struct mp_vdpau_mixer *mp_vdpau_mixer_create(struct mp_vdpau_ctx *vdp_ctx,
+ struct mp_log *log)
+{
+ struct mp_vdpau_mixer *mixer = talloc_ptrtype(NULL, mixer);
+ *mixer = (struct mp_vdpau_mixer){
+ .ctx = vdp_ctx,
+ .log = log,
+ .video_mixer = VDP_INVALID_HANDLE,
+ };
+ mp_vdpau_handle_preemption(mixer->ctx, &mixer->preemption_counter);
+ return mixer;
+}
+
+void mp_vdpau_mixer_destroy(struct mp_vdpau_mixer *mixer)
+{
+ struct vdp_functions *vdp = &mixer->ctx->vdp;
+ VdpStatus vdp_st;
+ if (mixer->video_mixer != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->video_mixer_destroy(mixer->video_mixer);
+ CHECK_VDP_WARNING(mixer, "Error when calling vdp_video_mixer_destroy");
+ }
+ talloc_free(mixer);
+}
+
+static bool opts_equal(const struct mp_vdpau_mixer_opts *a,
+ const struct mp_vdpau_mixer_opts *b)
+{
+ return a->deint == b->deint && a->chroma_deint == b->chroma_deint &&
+ a->pullup == b->pullup && a->hqscaling == b->hqscaling &&
+ a->sharpen == b->sharpen && a->denoise == b->denoise;
+}
+
+static int set_video_attribute(struct mp_vdpau_mixer *mixer,
+ VdpVideoMixerAttribute attr,
+ const void *value, char *attr_name)
+{
+ struct vdp_functions *vdp = &mixer->ctx->vdp;
+ VdpStatus vdp_st;
+
+ vdp_st = vdp->video_mixer_set_attribute_values(mixer->video_mixer, 1,
+ &attr, &value);
+ if (vdp_st != VDP_STATUS_OK) {
+ MP_ERR(mixer, "Error setting video mixer attribute %s: %s\n", attr_name,
+ vdp->get_error_string(vdp_st));
+ return -1;
+ }
+ return 0;
+}
+
+#define SET_VIDEO_ATTR(attr_name, attr_type, value) set_video_attribute(mixer, \
+ VDP_VIDEO_MIXER_ATTRIBUTE_ ## attr_name, &(attr_type){value},\
+ # attr_name)
+static int create_vdp_mixer(struct mp_vdpau_mixer *mixer,
+ VdpChromaType chroma_type, uint32_t w, uint32_t h)
+{
+ struct vdp_functions *vdp = &mixer->ctx->vdp;
+ VdpDevice vdp_device = mixer->ctx->vdp_device;
+ struct mp_vdpau_mixer_opts *opts = &mixer->opts;
+#define VDP_NUM_MIXER_PARAMETER 3
+#define MAX_NUM_FEATURES 6
+ int i;
+ VdpStatus vdp_st;
+
+ MP_VERBOSE(mixer, "Recreating vdpau video mixer.\n");
+
+ int feature_count = 0;
+ VdpVideoMixerFeature features[MAX_NUM_FEATURES];
+ VdpBool feature_enables[MAX_NUM_FEATURES];
+ static const VdpVideoMixerParameter parameters[VDP_NUM_MIXER_PARAMETER] = {
+ VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
+ VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
+ VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
+ };
+ const void *const parameter_values[VDP_NUM_MIXER_PARAMETER] = {
+ &(uint32_t){w},
+ &(uint32_t){h},
+ &(VdpChromaType){chroma_type},
+ };
+ if (opts->deint >= 3)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL;
+ if (opts->deint == 4)
+ features[feature_count++] =
+ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
+ if (opts->pullup)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
+ if (opts->denoise)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
+ if (opts->sharpen)
+ features[feature_count++] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
+ if (opts->hqscaling) {
+ VdpVideoMixerFeature hqscaling_feature =
+ VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + opts->hqscaling - 1;
+ VdpBool hqscaling_available;
+ vdp_st = vdp->video_mixer_query_feature_support(vdp_device,
+ hqscaling_feature,
+ &hqscaling_available);
+ CHECK_VDP_ERROR(mixer, "Error when calling video_mixer_query_feature_support");
+ if (hqscaling_available) {
+ features[feature_count++] = hqscaling_feature;
+ } else {
+ MP_ERR(mixer, "Your hardware or VDPAU library does not support "
+ "requested hqscaling.\n");
+ }
+ }
+
+ vdp_st = vdp->video_mixer_create(vdp_device, feature_count, features,
+ VDP_NUM_MIXER_PARAMETER,
+ parameters, parameter_values,
+ &mixer->video_mixer);
+ if (vdp_st != VDP_STATUS_OK)
+ mixer->video_mixer = VDP_INVALID_HANDLE;
+
+ CHECK_VDP_ERROR(mixer, "Error when calling vdp_video_mixer_create");
+
+ mixer->initialized = true;
+ mixer->current_chroma_type = chroma_type;
+ mixer->current_w = w;
+ mixer->current_h = h;
+
+ for (i = 0; i < feature_count; i++)
+ feature_enables[i] = VDP_TRUE;
+ if (feature_count) {
+ vdp_st = vdp->video_mixer_set_feature_enables(mixer->video_mixer,
+ feature_count, features,
+ feature_enables);
+ CHECK_VDP_WARNING(mixer, "Error calling vdp_video_mixer_set_feature_enables");
+ }
+ if (opts->denoise)
+ SET_VIDEO_ATTR(NOISE_REDUCTION_LEVEL, float, opts->denoise);
+ if (opts->sharpen)
+ SET_VIDEO_ATTR(SHARPNESS_LEVEL, float, opts->sharpen);
+ if (!opts->chroma_deint)
+ SET_VIDEO_ATTR(SKIP_CHROMA_DEINTERLACE, uint8_t, 1);
+
+ struct mp_cmat yuv2rgb;
+ VdpCSCMatrix matrix;
+
+ struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
+ mp_csp_set_image_params(&cparams, &mixer->image_params);
+ if (mixer->video_eq)
+ mp_csp_equalizer_state_get(mixer->video_eq, &cparams);
+ mp_get_csp_matrix(&cparams, &yuv2rgb);
+
+ for (int r = 0; r < 3; r++) {
+ for (int c = 0; c < 3; c++)
+ matrix[r][c] = yuv2rgb.m[r][c];
+ matrix[r][3] = yuv2rgb.c[r];
+ }
+
+ set_video_attribute(mixer, VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX,
+ &matrix, "CSC matrix");
+
+ return 0;
+}
+
+// If opts is NULL, use the opts as implied by the video image.
+int mp_vdpau_mixer_render(struct mp_vdpau_mixer *mixer,
+ struct mp_vdpau_mixer_opts *opts,
+ VdpOutputSurface output, VdpRect *output_rect,
+ struct mp_image *video, VdpRect *video_rect)
+{
+ struct vdp_functions *vdp = &mixer->ctx->vdp;
+ VdpStatus vdp_st;
+ VdpRect fallback_rect = {0, 0, video->w, video->h};
+
+ if (!video_rect)
+ video_rect = &fallback_rect;
+
+ int pe = mp_vdpau_handle_preemption(mixer->ctx, &mixer->preemption_counter);
+ if (pe < 1) {
+ mixer->video_mixer = VDP_INVALID_HANDLE;
+ if (pe < 0)
+ return -1;
+ }
+
+ if (video->imgfmt == IMGFMT_VDPAU_OUTPUT) {
+ VdpOutputSurface surface = (uintptr_t)video->planes[3];
+ int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
+ vdp_st = vdp->output_surface_render_output_surface(output,
+ output_rect,
+ surface,
+ video_rect,
+ NULL, NULL, flags);
+ CHECK_VDP_WARNING(mixer, "Error when calling "
+ "vdp_output_surface_render_output_surface");
+ return 0;
+ }
+
+ if (video->imgfmt != IMGFMT_VDPAU)
+ return -1;
+
+ struct mp_vdpau_mixer_frame *frame = mp_vdpau_mixed_frame_get(video);
+ struct mp_vdpau_mixer_frame fallback = {{0}};
+ if (!frame) {
+ frame = &fallback;
+ frame->current = (uintptr_t)video->planes[3];
+ for (int n = 0; n < MP_VDP_HISTORY_FRAMES; n++)
+ frame->past[n] = frame->future[n] = VDP_INVALID_HANDLE;
+ frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
+ }
+
+ if (!opts)
+ opts = &frame->opts;
+
+ if (mixer->video_mixer == VDP_INVALID_HANDLE)
+ mixer->initialized = false;
+
+ if (mixer->video_eq && mp_csp_equalizer_state_changed(mixer->video_eq))
+ mixer->initialized = false;
+
+ VdpChromaType s_chroma_type;
+ uint32_t s_w, s_h;
+
+ vdp_st = vdp->video_surface_get_parameters(frame->current, &s_chroma_type,
+ &s_w, &s_h);
+ CHECK_VDP_ERROR(mixer, "Error when calling vdp_video_surface_get_parameters");
+
+ if (!mixer->initialized || !opts_equal(opts, &mixer->opts) ||
+ !mp_image_params_equal(&video->params, &mixer->image_params) ||
+ mixer->current_w != s_w || mixer->current_h != s_h ||
+ mixer->current_chroma_type != s_chroma_type)
+ {
+ mixer->opts = *opts;
+ mixer->image_params = video->params;
+ if (mixer->video_mixer != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->video_mixer_destroy(mixer->video_mixer);
+ CHECK_VDP_WARNING(mixer, "Error when calling vdp_video_mixer_destroy");
+ }
+ mixer->video_mixer = VDP_INVALID_HANDLE;
+ mixer->initialized = false;
+ if (create_vdp_mixer(mixer, s_chroma_type, s_w, s_h) < 0)
+ return -1;
+ }
+
+ vdp_st = vdp->video_mixer_render(mixer->video_mixer, VDP_INVALID_HANDLE,
+ 0, frame->field,
+ MP_VDP_HISTORY_FRAMES, frame->past,
+ frame->current,
+ MP_VDP_HISTORY_FRAMES, frame->future,
+ video_rect,
+ output, NULL, output_rect,
+ 0, NULL);
+ CHECK_VDP_WARNING(mixer, "Error when calling vdp_video_mixer_render");
+ return 0;
+}