From 160497b8ff316f5ddd4deb6198302e5abbe7a56b Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Mon, 11 Apr 2016 14:24:48 -0400 Subject: encode_lavc: Migrate to codecpar API. --- video/out/vo_lavc.c | 66 ++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 31 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c index bd07d10208..d102fec869 100644 --- a/video/out/vo_lavc.c +++ b/video/out/vo_lavc.c @@ -37,6 +37,7 @@ struct priv { uint8_t *buffer; size_t buffer_size; AVStream *stream; + AVCodecContext *codec; int have_first_packet; int harddup; @@ -108,14 +109,14 @@ static int reconfig(struct vo *vo, struct mp_image_params *params) * warning here. We choose to ignore that; just because ffmpeg currently * uses a plain 'int' for these struct fields, it doesn't mean it always * will */ - if (width == vc->stream->codec->width && - height == vc->stream->codec->height) { - if (aspect.num != vc->stream->codec->sample_aspect_ratio.num || - aspect.den != vc->stream->codec->sample_aspect_ratio.den) { + if (width == vc->codec->width && + height == vc->codec->height) { + if (aspect.num != vc->codec->sample_aspect_ratio.num || + aspect.den != vc->codec->sample_aspect_ratio.den) { /* aspect-only changes are not critical */ MP_WARN(vo, "unsupported pixel aspect ratio change from %d:%d to %d:%d\n", - vc->stream->codec->sample_aspect_ratio.num, - vc->stream->codec->sample_aspect_ratio.den, + vc->codec->sample_aspect_ratio.num, + vc->codec->sample_aspect_ratio.den, aspect.num, aspect.den); } goto done; @@ -144,18 +145,20 @@ static int reconfig(struct vo *vo, struct mp_image_params *params) goto error; } - vc->stream = encode_lavc_alloc_stream(vo->encode_lavc_ctx, - AVMEDIA_TYPE_VIDEO); - vc->stream->sample_aspect_ratio = vc->stream->codec->sample_aspect_ratio = + if (encode_lavc_alloc_stream(vo->encode_lavc_ctx, + AVMEDIA_TYPE_VIDEO, + &vc->stream, &vc->codec) < 0) + goto error; + vc->stream->sample_aspect_ratio = vc->codec->sample_aspect_ratio = aspect; - vc->stream->codec->width = width; - vc->stream->codec->height = height; - vc->stream->codec->pix_fmt = pix_fmt; + vc->codec->width = width; + vc->codec->height = height; + vc->codec->pix_fmt = pix_fmt; - encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, params->colorspace); - encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, params->colorlevels); + encode_lavc_set_csp(vo->encode_lavc_ctx, vc->codec, params->colorspace); + encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->codec, params->colorlevels); - if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0) + if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->codec) < 0) goto error; vc->buffer_size = 6 * width * height + 200; @@ -204,7 +207,7 @@ static void write_packet(struct vo *vo, int size, AVPacket *packet) packet->stream_index = vc->stream->index; if (packet->pts != AV_NOPTS_VALUE) { packet->pts = av_rescale_q(packet->pts, - vc->stream->codec->time_base, + vc->codec->time_base, vc->stream->time_base); } else { MP_VERBOSE(vo, "codec did not provide pts\n"); @@ -213,12 +216,12 @@ static void write_packet(struct vo *vo, int size, AVPacket *packet) } if (packet->dts != AV_NOPTS_VALUE) { packet->dts = av_rescale_q(packet->dts, - vc->stream->codec->time_base, + vc->codec->time_base, vc->stream->time_base); } if (packet->duration > 0) { packet->duration = av_rescale_q(packet->duration, - vc->stream->codec->time_base, + vc->codec->time_base, vc->stream->time_base); } else { // HACK: libavformat calculates dts wrong if the initial packet @@ -226,15 +229,16 @@ static void write_packet(struct vo *vo, int size, AVPacket *packet) // have b-frames! if (!packet->duration) if (!vc->have_first_packet) - if (vc->stream->codec->has_b_frames - || vc->stream->codec->max_b_frames) + if (vc->codec->has_b_frames + || vc->codec->max_b_frames) if (vc->stream->time_base.num * 1000LL <= vc->stream->time_base.den) packet->duration = FFMAX(1, av_rescale_q(1, - vc->stream->codec->time_base, vc->stream->time_base)); + vc->codec->time_base, vc->stream->time_base)); } - if (encode_lavc_write_frame(vo->encode_lavc_ctx, packet) < 0) { + if (encode_lavc_write_frame(vo->encode_lavc_ctx, + vc->stream, packet) < 0) { MP_ERR(vo, "error writing\n"); return; } @@ -251,23 +255,23 @@ static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet) return 0; memcpy(vc->buffer, frame, sizeof(AVPicture)); MP_DBG(vo, "got pts %f\n", - frame->pts * (double) vc->stream->codec->time_base.num / - (double) vc->stream->codec->time_base.den); + frame->pts * (double) vc->codec->time_base.num / + (double) vc->codec->time_base.den); packet->size = sizeof(AVPicture); return packet->size; } else { int got_packet = 0; - int status = avcodec_encode_video2(vc->stream->codec, packet, + int status = avcodec_encode_video2(vc->codec, packet, frame, &got_packet); int size = (status < 0) ? status : got_packet ? packet->size : 0; if (frame) MP_DBG(vo, "got pts %f; out size: %d\n", - frame->pts * (double) vc->stream->codec->time_base.num / - (double) vc->stream->codec->time_base.den, size); + frame->pts * (double) vc->codec->time_base.num / + (double) vc->codec->time_base.den, size); if (got_packet) - encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream); + encode_lavc_write_stats(vo->encode_lavc_ctx, vc->codec); return size; } } @@ -295,7 +299,7 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) pts = vc->expected_next_pts; } - avc = vc->stream->codec; + avc = vc->codec; if (vc->worst_time_base.den == 0) { //if (avc->time_base.num / avc->time_base.den >= vc->stream->time_base.num / vc->stream->time_base.den) @@ -376,7 +380,7 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) } vc->lastpts = outpts; ectx->last_video_in_pts = pts; - frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream)) + frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->codec)) / timeunit + 0.5); // calculate expected pts of next video frame @@ -396,7 +400,7 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) MP_INFO(vo, "--oneverdrop increased pts by %d\n", (int) (vc->lastipts - frameipts + step)); frameipts = vc->lastipts + step; - vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream); + vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->codec); } } -- cgit v1.2.3 From f5ff2656e0d192a2e25fe5f65edf219972211a48 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 11 Apr 2016 20:46:05 +0200 Subject: vaapi: determine surface format in decoder, not in renderer Until now, we have made the assumption that a driver will use only 1 hardware surface format. the format is dictated by the driver (you don't create surfaces with a specific format - you just pass a rt_format and get a surface that will be in a specific driver-chosen format). In particular, the renderer created a dummy surface to probe the format, and hoped the decoder would produce the same format. Due to a driver bug this required a workaround to actually get the same format as the driver did. Change this so that the format is determined in the decoder. The format is then passed down as hw_subfmt, which allows the renderer to configure itself with the correct format. If the hardware surface changes its format midstream, the renderer can be reconfigured using the normal mechanisms. This calls va_surface_init_subformat() each time after the decoder returns a surface. Since libavcodec/AVFrame has no concept of sub- formats, this is unavoidable. It creates and destroys a derived VAImage, but this shouldn't have any bad performance effects (at least I didn't notice any measurable effects). Note that vaDeriveImage() failures are silently ignored as some drivers (the vdpau wrapper) support neither vaDeriveImage, nor EGL interop. In addition, we still probe whether we can map an image in the EGL interop code. This is important as it's the only way to determine whether EGL interop is supported at all. With respect to the driver bug mentioned above, it doesn't matter which format the test surface has. In vf_vavpp, also remove the rt_format guessing business. I think the existing logic was a bit meaningless anyway. It's not even a given that vavpp produces the same rt_format for output. --- video/out/opengl/hwdec_vaegl.c | 50 +++++++++++------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c index 7b34d6bb5c..d62a20a219 100644 --- a/video/out/opengl/hwdec_vaegl.c +++ b/video/out/opengl/hwdec_vaegl.c @@ -172,30 +172,6 @@ static void destroy(struct gl_hwdec *hw) va_destroy(p->ctx); } -// Create an empty dummy VPP. This works around a weird bug that affects the -// VA surface format, as it is reported by vaDeriveImage(). Before a VPP -// context or a decoder context is created, the surface format will be reported -// as YV12. Surfaces created after context creation will report NV12 (even -// though surface creation does not take a context as argument!). Existing -// surfaces will change their format from YV12 to NV12 as soon as the decoder -// renders to them! Because we want know the surface format in advance (to -// simplify our renderer configuration logic), we hope that this hack gives -// us reasonable behavior. -// See: https://bugs.freedesktop.org/show_bug.cgi?id=79848 -static void insane_hack(struct gl_hwdec *hw) -{ - struct priv *p = hw->priv; - VAConfigID config; - if (vaCreateConfig(p->display, VAProfileNone, VAEntrypointVideoProc, - NULL, 0, &config) == VA_STATUS_SUCCESS) - { - // We want to keep this until the VADisplay is destroyed. It will - // implicitly free the context. - VAContextID context; - vaCreateContext(p->display, config, 0, 0, 0, NULL, 0, &context); - } -} - static int create(struct gl_hwdec *hw) { GL *gl = hw->gl; @@ -248,7 +224,6 @@ static int create(struct gl_hwdec *hw) MP_VERBOSE(p, "using VAAPI EGL interop\n"); - insane_hack(hw); if (!test_format(hw)) { destroy(hw); return -1; @@ -278,6 +253,18 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) } gl->BindTexture(GL_TEXTURE_2D, 0); + hw->converted_imgfmt = va_fourcc_to_imgfmt(params->hw_subfmt); + if (hw->converted_imgfmt != IMGFMT_NV12 && + hw->converted_imgfmt != IMGFMT_420P) + { + MP_FATAL(p, "unsupported VA image format %s\n", + mp_tag_str(params->hw_subfmt)); + return -1; + } + + MP_VERBOSE(p, "format: %s %s\n", mp_tag_str(params->hw_subfmt), + mp_imgfmt_to_name(hw->converted_imgfmt)); + return 0; } @@ -308,18 +295,6 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, goto err; int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc); - if (mpfmt != IMGFMT_NV12 && mpfmt != IMGFMT_420P) { - MP_FATAL(p, "unsupported VA image format %s\n", - mp_tag_str(va_image->format.fourcc)); - goto err; - } - - if (!hw->converted_imgfmt) { - MP_VERBOSE(p, "format: %s %s\n", mp_tag_str(va_image->format.fourcc), - mp_imgfmt_to_name(mpfmt)); - hw->converted_imgfmt = mpfmt; - } - if (hw->converted_imgfmt != mpfmt) { MP_FATAL(p, "mid-stream hwdec format change (%s -> %s) not supported\n", mp_imgfmt_to_name(hw->converted_imgfmt), mp_imgfmt_to_name(mpfmt)); @@ -387,6 +362,7 @@ static bool test_format(struct gl_hwdec *hw) va_pool_set_allocator(alloc, p->ctx, VA_RT_FORMAT_YUV420); struct mp_image *surface = mp_image_pool_get(alloc, IMGFMT_VAAPI, 64, 64); if (surface) { + va_surface_init_subformat(surface); struct mp_image_params params = surface->params; if (reinit(hw, ¶ms) >= 0) { GLuint textures[4]; -- cgit v1.2.3 From e3e03d0f34de86085d18f92a3dceab337c3220b6 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Tue, 12 Apr 2016 15:55:48 +0200 Subject: vo_opengl: simplify and improve up scale=oversample Since what we're doing is a linear blend of the four colors, we can just do it for free by using GPU sampling. This requires significantly fewer texture fetches and calculations to compute the final color, making it much more efficient. The code is also much shorter and simpler. --- video/out/opengl/video_shaders.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c index bea1bbf325..cf022b96dd 100644 --- a/video/out/opengl/video_shaders.c +++ b/video/out/opengl/video_shaders.c @@ -208,31 +208,15 @@ void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler, GLSLF("{\n"); GLSL(vec2 pos = pos + vec2(0.5) * pt;) // round to nearest GLSL(vec2 fcoord = fract(pos * size - vec2(0.5));) - // We only need to sample from the four corner pixels since we're using - // nearest neighbour and can compute the exact transition point - GLSL(vec2 baseNW = pos - fcoord * pt;) - GLSL(vec2 baseNE = baseNW + vec2(pt.x, 0.0);) - GLSL(vec2 baseSW = baseNW + vec2(0.0, pt.y);) - GLSL(vec2 baseSE = baseNW + pt;) // Determine the mixing coefficient vector gl_sc_uniform_vec2(sc, "output_size", (float[2]){w, h}); - GLSL(vec2 coeff = vec2((baseSE - pos) * output_size);) - GLSL(coeff = clamp(coeff, 0.0, 1.0);) + GLSL(vec2 coeff = fcoord * output_size/size;) float threshold = scaler->conf.kernel.params[0]; - if (threshold > 0) { // also rules out NAN - GLSLF("coeff = mix(coeff, vec2(0.0), " - "lessThanEqual(coeff, vec2(%f)));\n", threshold); - GLSLF("coeff = mix(coeff, vec2(1.0), " - "greaterThanEqual(coeff, vec2(%f)));\n", 1.0 - threshold); - } + threshold = isnan(threshold) ? 0.0 : threshold; + GLSLF("coeff = (coeff - %f) / %f;\n", threshold, 1.0 - 2 * threshold); + GLSL(coeff = clamp(coeff, 0.0, 1.0);) // Compute the right blend of colors - GLSL(vec4 left = mix(texture(tex, baseSW), - texture(tex, baseNW), - coeff.y);) - GLSL(vec4 right = mix(texture(tex, baseSE), - texture(tex, baseNE), - coeff.y);) - GLSL(color = mix(right, left, coeff.x);) + GLSL(color = texture(tex, pos + pt * (coeff - fcoord));) GLSLF("}\n"); } -- cgit v1.2.3 From 8c02c92ab962107ee43c71854bd9712cc492046e Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 15 Apr 2016 09:45:15 +0200 Subject: vo_opengl: rpi: don't include x11 header file Copy & paste bug. --- video/out/opengl/context_rpi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'video/out') diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c index c01c173b8f..c0ca73335a 100644 --- a/video/out/opengl/context_rpi.c +++ b/video/out/opengl/context_rpi.c @@ -19,7 +19,6 @@ #include #include "common/common.h" -#include "video/out/x11_common.h" #include "context.h" #include "context_rpi.h" -- cgit v1.2.3 From b735c0e2077cb37df6764350472cbc68a9142d91 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 15 Apr 2016 13:58:41 +0200 Subject: lcms: include math.h Fixes #3053. --- video/out/opengl/lcms.c | 1 + 1 file changed, 1 insertion(+) (limited to 'video/out') diff --git a/video/out/opengl/lcms.c b/video/out/opengl/lcms.c index 7db8da6f70..a2030d3792 100644 --- a/video/out/opengl/lcms.c +++ b/video/out/opengl/lcms.c @@ -16,6 +16,7 @@ */ #include +#include #include "mpv_talloc.h" -- cgit v1.2.3 From 798188cb33adebf2a3c4ea44918f8afef1c8f17b Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 15 Apr 2016 15:43:46 +0200 Subject: vo_lavc: send refcounted AVFrame to encoder This potentially makes it more efficient, and actually makes it simpler. Yes, AV_PICTURE_TYPE_NONE is the default for pict_type. --- video/out/vo_lavc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c index d102fec869..6573e1d8a8 100644 --- a/video/out/vo_lavc.c +++ b/video/out/vo_lavc.c @@ -421,15 +421,15 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) skipframes = 0; if (thisduration > skipframes) { - AVFrame *frame = av_frame_alloc(); + AVFrame *frame = mp_image_to_av_frame(vc->lastimg); + if (!frame) + abort(); // this is a nop, unless the worst time base is the STREAM time base frame->pts = av_rescale_q(vc->lastipts + skipframes, vc->worst_time_base, avc->time_base); - enum AVPictureType savetype = frame->pict_type; - mp_image_copy_fields_to_av_frame(frame, vc->lastimg); - frame->pict_type = savetype; + frame->pict_type = AV_PICTURE_TYPE_NONE; // keep this at avcodec_get_frame_defaults default frame->quality = avc->global_quality; -- cgit v1.2.3 From a77cbc504ac3ff359d4c029e3c1b34581e7c7caa Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 15 Apr 2016 15:45:34 +0200 Subject: vo_lavc: unsupport deprecated AVFMT_RAWPICTURE As of ffmpeg git master, only the libavdevice decklink wrapper supports this. Everything else has dropped support. You're now supposed to use AV_CODEC_ID_WRAPPED_AVFRAME, which works a bit differently. Normal AVFrames should still work for these encoders. --- video/out/vo_lavc.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c index 6573e1d8a8..4f70c2b2ed 100644 --- a/video/out/vo_lavc.c +++ b/video/out/vo_lavc.c @@ -250,30 +250,19 @@ static void write_packet(struct vo *vo, int size, AVPacket *packet) static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet) { struct priv *vc = vo->priv; - if (encode_lavc_oformat_flags(vo->encode_lavc_ctx) & AVFMT_RAWPICTURE) { - if (!frame) - return 0; - memcpy(vc->buffer, frame, sizeof(AVPicture)); - MP_DBG(vo, "got pts %f\n", + int got_packet = 0; + int status = avcodec_encode_video2(vc->codec, packet, + frame, &got_packet); + int size = (status < 0) ? status : got_packet ? packet->size : 0; + + if (frame) + MP_DBG(vo, "got pts %f; out size: %d\n", frame->pts * (double) vc->codec->time_base.num / - (double) vc->codec->time_base.den); - packet->size = sizeof(AVPicture); - return packet->size; - } else { - int got_packet = 0; - int status = avcodec_encode_video2(vc->codec, packet, - frame, &got_packet); - int size = (status < 0) ? status : got_packet ? packet->size : 0; - - if (frame) - MP_DBG(vo, "got pts %f; out size: %d\n", - frame->pts * (double) vc->codec->time_base.num / - (double) vc->codec->time_base.den, size); - - if (got_packet) - encode_lavc_write_stats(vo->encode_lavc_ctx, vc->codec); - return size; - } + (double) vc->codec->time_base.den, size); + + if (got_packet) + encode_lavc_write_stats(vo->encode_lavc_ctx, vc->codec); + return size; } static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) -- cgit v1.2.3 From 9db1b7a001786ad5d4ca35decadd29189fefa8dd Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 15 Apr 2016 19:35:29 +0200 Subject: vo_lavc: fix build on Libav OF COURSE Libav doesn't have AV_PICTURE_TYPE_NONE. Why the fuck would it? --- video/out/vo_lavc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c index 4f70c2b2ed..188a575f45 100644 --- a/video/out/vo_lavc.c +++ b/video/out/vo_lavc.c @@ -418,8 +418,7 @@ static void draw_image_unlocked(struct vo *vo, mp_image_t *mpi) frame->pts = av_rescale_q(vc->lastipts + skipframes, vc->worst_time_base, avc->time_base); - frame->pict_type = AV_PICTURE_TYPE_NONE; - // keep this at avcodec_get_frame_defaults default + frame->pict_type = 0; // keep this at unknown/undefined frame->quality = avc->global_quality; -- cgit v1.2.3 From 44644e69f04b40142c7ab0930c9ec3856c006a56 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 16 Apr 2016 16:16:50 +0200 Subject: vo_opengl: fix an outdated comment This wasn't updated over multiple iterations. --- video/out/opengl/common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c index 46cbc2fc8c..30cce911e4 100644 --- a/video/out/opengl/common.c +++ b/video/out/opengl/common.c @@ -348,11 +348,9 @@ static const struct gl_functions gl_functions[] = { // Fill the GL struct with function pointers and extensions from the current // GL context. Called by the backend. -// getProcAddress: function to resolve function names, may be NULL +// get_fn: function to resolve function names // ext2: an extra extension string // log: used to output messages -// Note: if you create a CONTEXT_FORWARD_COMPATIBLE_BIT_ARB with OpenGL 3.0, -// you must append "GL_ARB_compatibility" to ext2. void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n), void *fn_ctx, const char *ext2, struct mp_log *log) { -- cgit v1.2.3 From 244eff92018487b08929a8a765706fa9fe8072de Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 22 Apr 2016 12:08:21 +0200 Subject: vo_opengl: always reset some GL state when leaving renderer The active texture and some pixelstore parameters are now always reset to defaults when entering and leaving the renderer. Could be important for libmpv. --- video/out/opengl/video.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 8807b65005..4307dcde47 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -2808,6 +2808,12 @@ void gl_video_uninit(struct gl_video *p) } void gl_video_set_gl_state(struct gl_video *p) +{ + // This resets certain important state to defaults. + gl_video_unset_gl_state(p); +} + +void gl_video_unset_gl_state(struct gl_video *p) { GL *gl = p->gl; @@ -2817,11 +2823,6 @@ void gl_video_set_gl_state(struct gl_video *p) gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); } -void gl_video_unset_gl_state(struct gl_video *p) -{ - /* nop */ -} - void gl_video_reset(struct gl_video *p) { gl_video_reset_surfaces(p); -- cgit v1.2.3 From 021cb2c3870b9d0b2f2e03416702fc561f160c2b Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 25 Apr 2016 11:28:49 +0200 Subject: mp_image: allow passing NULL to mp_image_new_custom_ref() A minor simplification. Most callers don't need this, and there's no good reason why the caller should provide an "initializer" like this. (This function calls mp_image_new_dummy_ref(), which has no reason for an initializer either.) --- video/out/vo_rpi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c index 9d782fc9c5..7ba3e8ba68 100644 --- a/video/out/vo_rpi.c +++ b/video/out/vo_rpi.c @@ -435,8 +435,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) } mmal_buffer_header_reset(buffer); - struct mp_image *new_ref = mp_image_new_custom_ref(&(struct mp_image){0}, - buffer, + struct mp_image *new_ref = mp_image_new_custom_ref(NULL, buffer, free_mmal_buffer); if (!new_ref) { mmal_buffer_header_release(buffer); -- cgit v1.2.3 From dfa88271a2c76bfbd4dbc3fc87ee81bd0a74ec8e Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 26 Apr 2016 13:59:06 +0200 Subject: vp_rpi: fix indentation This also moves the p->background check into the top if (the code effectively didn't do anything when this false). --- video/out/vo_rpi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c index 7ba3e8ba68..b192476e52 100644 --- a/video/out/vo_rpi.c +++ b/video/out/vo_rpi.c @@ -255,16 +255,15 @@ static int create_overlays(struct vo *vo) struct priv *p = vo->priv; destroy_overlays(vo); - if (vo->opts->fullscreen) { - // Use the whole screen. - VC_RECT_T dst = {.width = p->w, .height = p->h}; - VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16}; - VC_DISPMANX_ALPHA_T alpha = { - .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, - .opacity = 0xFF, - }; + if (vo->opts->fullscreen && p->background) { + // Use the whole screen. + VC_RECT_T dst = {.width = p->w, .height = p->h}; + VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16}; + VC_DISPMANX_ALPHA_T alpha = { + .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, + .opacity = 0xFF, + }; - if (p->background) { p->window = vc_dispmanx_element_add(p->update, p->display, p->background_layer, &dst, 0, &src, @@ -275,7 +274,6 @@ static int create_overlays(struct vo *vo) return -1; } } - } if (p->enable_osd) { VC_RECT_T dst = {.x = p->x, .y = p->y, -- cgit v1.2.3 From 74e3d11f214de023871e849e121b50e203a5a1c0 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 26 Apr 2016 16:03:42 +0200 Subject: vo_rpi: attempt to survive display mode changes Recreate all dispmanx objects after mode changes signalled by the TV callback. This is needed since dispmanx objects are marked as invalid and cease working. One important point is that the vsync callbacks will stop coming when this happens, so restoring the callback is important. Note that the MMAL renderer itself does not get trashed by the firmware on such events, but we completely reconfigure it anyway when it happens. --- video/out/vo_rpi.c | 128 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 38 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c index b192476e52..e5ca1d79e3 100644 --- a/video/out/vo_rpi.c +++ b/video/out/vo_rpi.c @@ -30,8 +30,6 @@ #include -#include "osdep/atomics.h" - #include "common/common.h" #include "common/msg.h" #include "options/m_config.h" @@ -69,11 +67,10 @@ struct priv { // for RAM input MMAL_POOL_T *swpool; - atomic_bool update_display; - pthread_mutex_t vsync_mutex; pthread_cond_t vsync_cond; int64_t vsync_counter; + bool reload_display; int background_layer; int video_layer; @@ -89,6 +86,8 @@ struct priv { #define ALIGN_W 32 #define ALIGN_H 16 +static void recreate_renderer(struct vo *vo); + // Make mpi point to buffer, assuming MMAL_ENCODING_I420. // buffer can be NULL. // Return the required buffer space. @@ -255,6 +254,9 @@ static int create_overlays(struct vo *vo) struct priv *p = vo->priv; destroy_overlays(vo); + if (!p->display) + return -1; + if (vo->opts->fullscreen && p->background) { // Use the whole screen. VC_RECT_T dst = {.width = p->w, .height = p->h}; @@ -362,7 +364,7 @@ static void wait_next_vsync(struct vo *vo) struct priv *p = vo->priv; pthread_mutex_lock(&p->vsync_mutex); int64_t old = p->vsync_counter; - while (old == p->vsync_counter) + while (old == p->vsync_counter && !p->reload_display) pthread_cond_wait(&p->vsync_cond, &p->vsync_mutex); pthread_mutex_unlock(&p->vsync_mutex); } @@ -370,6 +372,10 @@ static void wait_next_vsync(struct vo *vo) static void flip_page(struct vo *vo) { struct priv *p = vo->priv; + + if (!p->renderer_enabled) + return; + struct mp_image *mpi = p->next_image; p->next_image = NULL; @@ -405,6 +411,9 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) { struct priv *p = vo->priv; + if (!p->renderer_enabled) + return; + mp_image_t *mpi = NULL; if (!frame->redraw && !frame->repeat) mpi = mp_image_new_ref(frame->current); @@ -506,6 +515,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *params) MMAL_PORT_T *input = p->renderer->input[0]; bool opaque = params->imgfmt == IMGFMT_MMAL; + if (!p->display) + return -1; + disable_renderer(vo); input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420; @@ -560,6 +572,9 @@ static struct mp_image *take_screenshot(struct vo *vo) { struct priv *p = vo->priv; + if (!p->display) + return NULL; + struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h); if (!img) return NULL; @@ -612,14 +627,15 @@ static int control(struct vo *vo, uint32_t request, void *data) case VOCTRL_SCREENSHOT_WIN: *(struct mp_image **)data = take_screenshot(vo); return VO_TRUE; - case VOCTRL_CHECK_EVENTS: - if (atomic_load(&p->update_display)) { - atomic_store(&p->update_display, false); - update_display_size(vo); - if (p->renderer_enabled) - set_geometry(vo); - } + case VOCTRL_CHECK_EVENTS: { + pthread_mutex_lock(&p->vsync_mutex); + bool reload_required = p->reload_display; + p->reload_display = false; + pthread_mutex_unlock(&p->vsync_mutex); + if (reload_required) + recreate_renderer(vo); return VO_TRUE; + } case VOCTRL_GET_DISPLAY_FPS: *(double *)data = p->display_fps; return VO_TRUE; @@ -633,7 +649,10 @@ static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1, { struct vo *vo = callback_data; struct priv *p = vo->priv; - atomic_store(&p->update_display, true); + pthread_mutex_lock(&p->vsync_mutex); + p->reload_display = true; + pthread_cond_signal(&p->vsync_cond); + pthread_mutex_unlock(&p->vsync_mutex); vo_wakeup(vo); } @@ -647,6 +666,55 @@ static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg) pthread_mutex_unlock(&p->vsync_mutex); } +static void destroy_dispmanx(struct vo *vo) +{ + struct priv *p = vo->priv; + + disable_renderer(vo); + destroy_overlays(vo); + + if (p->display) { + vc_dispmanx_vsync_callback(p->display, NULL, NULL); + vc_dispmanx_display_close(p->display); + } + p->display = 0; +} + +static int recreate_dispmanx(struct vo *vo) +{ + struct priv *p = vo->priv; + + p->display = vc_dispmanx_display_open(p->display_nr); + p->update = vc_dispmanx_update_start(0); + if (!p->display || !p->update) { + MP_FATAL(vo, "Could not get DISPMANX objects.\n"); + if (p->display) + vc_dispmanx_display_close(p->display); + p->display = 0; + p->update = 0; + return -1; + } + + update_display_size(vo); + + vc_dispmanx_vsync_callback(p->display, vsync_callback, vo); + + return 0; +} + +static void recreate_renderer(struct vo *vo) +{ + MP_WARN(vo, "Recreating renderer after display change.\n"); + + destroy_dispmanx(vo); + recreate_dispmanx(vo); + + if (vo->params) { + if (reconfig(vo, vo->params) < 0) + MP_FATAL(vo, "Recreation failed.\n"); + } +} + static void uninit(struct vo *vo) { struct priv *p = vo->priv; @@ -655,20 +723,10 @@ static void uninit(struct vo *vo) talloc_free(p->next_image); - destroy_overlays(vo); - - if (p->update) - vc_dispmanx_update_submit_sync(p->update); + destroy_dispmanx(vo); - if (p->renderer) { - disable_renderer(vo); + if (p->renderer) mmal_component_release(p->renderer); - } - - if (p->display) { - vc_dispmanx_vsync_callback(p->display, NULL, NULL); - vc_dispmanx_display_close(p->display); - } mmal_vc_deinit(); @@ -693,12 +751,14 @@ static int preinit(struct vo *vo) return -1; } - p->display = vc_dispmanx_display_open(p->display_nr); - p->update = vc_dispmanx_update_start(0); - if (!p->display || !p->update) { - MP_FATAL(vo, "Could not get DISPMANX objects.\n"); + pthread_mutex_init(&p->vsync_mutex, NULL); + pthread_cond_init(&p->vsync_cond, NULL); + + if (recreate_dispmanx(vo) < 0) + goto fail; + + if (update_display_size(vo) < 0) goto fail; - } if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer)) { @@ -706,16 +766,8 @@ static int preinit(struct vo *vo) goto fail; } - if (update_display_size(vo) < 0) - goto fail; - vc_tv_register_callback(tv_callback, vo); - pthread_mutex_init(&p->vsync_mutex, NULL); - pthread_cond_init(&p->vsync_cond, NULL); - - vc_dispmanx_vsync_callback(p->display, vsync_callback, vo); - return 0; fail: -- cgit v1.2.3 From 67985252514acd57d00a0a7a73e8a29c5e815882 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 26 Apr 2016 17:01:37 +0200 Subject: vo_rpi: wait for vsync with a timeout Sucks, but better than freezing forever given the (to me) unpredictable RPI behavior. This will be good enough to drop out of vsync timing mode, or to abort playback. --- video/out/vo_rpi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c index e5ca1d79e3..06327e9fd7 100644 --- a/video/out/vo_rpi.c +++ b/video/out/vo_rpi.c @@ -33,6 +33,7 @@ #include "common/common.h" #include "common/msg.h" #include "options/m_config.h" +#include "osdep/timer.h" #include "vo.h" #include "win_state.h" #include "video/mp_image.h" @@ -363,9 +364,12 @@ static void wait_next_vsync(struct vo *vo) { struct priv *p = vo->priv; pthread_mutex_lock(&p->vsync_mutex); + struct timespec end = mp_rel_time_to_timespec(0.050); int64_t old = p->vsync_counter; - while (old == p->vsync_counter && !p->reload_display) - pthread_cond_wait(&p->vsync_cond, &p->vsync_mutex); + while (old == p->vsync_counter && !p->reload_display) { + if (pthread_cond_timedwait(&p->vsync_cond, &p->vsync_mutex, &end)) + break; + } pthread_mutex_unlock(&p->vsync_mutex); } -- cgit v1.2.3 From dfe40f770df7cc86c574fa290360f5d065fee7b5 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 26 Apr 2016 17:12:01 +0200 Subject: vo_rpi: rename mutex/cond variables They're now used for the TV callback too, not just vsync. --- video/out/vo_rpi.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'video/out') diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c index 06327e9fd7..bbbaae912f 100644 --- a/video/out/vo_rpi.c +++ b/video/out/vo_rpi.c @@ -68,8 +68,8 @@ struct priv { // for RAM input MMAL_POOL_T *swpool; - pthread_mutex_t vsync_mutex; - pthread_cond_t vsync_cond; + pthread_mutex_t display_mutex; + pthread_cond_t display_cond; int64_t vsync_counter; bool reload_display; @@ -363,14 +363,14 @@ static int set_geometry(struct vo *vo) static void wait_next_vsync(struct vo *vo) { struct priv *p = vo->priv; - pthread_mutex_lock(&p->vsync_mutex); + pthread_mutex_lock(&p->display_mutex); struct timespec end = mp_rel_time_to_timespec(0.050); int64_t old = p->vsync_counter; while (old == p->vsync_counter && !p->reload_display) { - if (pthread_cond_timedwait(&p->vsync_cond, &p->vsync_mutex, &end)) + if (pthread_cond_timedwait(&p->display_cond, &p->display_mutex, &end)) break; } - pthread_mutex_unlock(&p->vsync_mutex); + pthread_mutex_unlock(&p->display_mutex); } static void flip_page(struct vo *vo) @@ -632,10 +632,10 @@ static int control(struct vo *vo, uint32_t request, void *data) *(struct mp_image **)data = take_screenshot(vo); return VO_TRUE; case VOCTRL_CHECK_EVENTS: { - pthread_mutex_lock(&p->vsync_mutex); + pthread_mutex_lock(&p->display_mutex); bool reload_required = p->reload_display; p->reload_display = false; - pthread_mutex_unlock(&p->vsync_mutex); + pthread_mutex_unlock(&p->display_mutex); if (reload_required) recreate_renderer(vo); return VO_TRUE; @@ -653,10 +653,10 @@ static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1, { struct vo *vo = callback_data; struct priv *p = vo->priv; - pthread_mutex_lock(&p->vsync_mutex); + pthread_mutex_lock(&p->display_mutex); p->reload_display = true; - pthread_cond_signal(&p->vsync_cond); - pthread_mutex_unlock(&p->vsync_mutex); + pthread_cond_signal(&p->display_cond); + pthread_mutex_unlock(&p->display_mutex); vo_wakeup(vo); } @@ -664,10 +664,10 @@ static void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *arg) { struct vo *vo = arg; struct priv *p = vo->priv; - pthread_mutex_lock(&p->vsync_mutex); + pthread_mutex_lock(&p->display_mutex); p->vsync_counter += 1; - pthread_cond_signal(&p->vsync_cond); - pthread_mutex_unlock(&p->vsync_mutex); + pthread_cond_signal(&p->display_cond); + pthread_mutex_unlock(&p->display_mutex); } static void destroy_dispmanx(struct vo *vo) @@ -734,8 +734,8 @@ static void uninit(struct vo *vo) mmal_vc_deinit(); - pthread_cond_destroy(&p->vsync_cond); - pthread_mutex_destroy(&p->vsync_mutex); + pthread_cond_destroy(&p->display_cond); + pthread_mutex_destroy(&p->display_mutex); } static int preinit(struct vo *vo) @@ -755,8 +755,8 @@ static int preinit(struct vo *vo) return -1; } - pthread_mutex_init(&p->vsync_mutex, NULL); - pthread_cond_init(&p->vsync_cond, NULL); + pthread_mutex_init(&p->display_mutex, NULL); + pthread_cond_init(&p->display_cond, NULL); if (recreate_dispmanx(vo) < 0) goto fail; -- cgit v1.2.3 From d3a26272cddd295eb863d4a06a899239cab78df4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 13:32:20 +0200 Subject: vo_opengl: print error if opengl hwdec interop fails --- video/out/opengl/video.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'video/out') diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 4307dcde47..de5c49da34 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -2542,6 +2542,8 @@ static void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) bool ok = p->hwdec->driver->map_image(p->hwdec, vimg->mpi, imgtex) >= 0; for (int n = 0; n < p->plane_count; n++) vimg->planes[n].gl_texture = ok ? imgtex[n] : -1; + if (!ok) + MP_FATAL(p, "Mapping hardware decoded surface failed.\n"); return; } -- cgit v1.2.3 From 3706918311ef4cc57b1241e87dcc43d699e960f9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 13:49:47 +0200 Subject: vo_opengl: D3D11VA + ANGLE interop This uses ID3D11VideoProcessor to convert the video to a RGBA surface, which is then bound to ANGLE. Currently ANGLE does not provide any way to bind nv12 surfaces directly, so this will have to do. ID3D11VideoContext1 would give us slightly more control about the colorspace conversion, though it's still not good, and not available in MinGW headers yet. The video processor is created lazily, because we need to have the coded frame size, of which AVFrame and mp_image have no concept of. Doing the creation lazily is less of a pain than somehow hacking the coded frame size into mp_image. I'm not really sure how ID3D11VideoProcessorInputView is supposed to work. We recreate it on every frame, which is simple and hopefully doesn't affect performance. --- video/out/opengl/hwdec.c | 2 + video/out/opengl/hwdec_d3d11egl.c | 413 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 415 insertions(+) create mode 100644 video/out/opengl/hwdec_d3d11egl.c (limited to 'video/out') diff --git a/video/out/opengl/hwdec.c b/video/out/opengl/hwdec.c index b58af9bae9..02aa0c2cd2 100644 --- a/video/out/opengl/hwdec.c +++ b/video/out/opengl/hwdec.c @@ -29,6 +29,7 @@ extern const struct gl_hwdec_driver gl_hwdec_vaglx; extern const struct gl_hwdec_driver gl_hwdec_videotoolbox; extern const struct gl_hwdec_driver gl_hwdec_vdpau; extern const struct gl_hwdec_driver gl_hwdec_dxva2egl; +extern const struct gl_hwdec_driver gl_hwdec_d3d11egl; extern const struct gl_hwdec_driver gl_hwdec_dxva2gldx; extern const struct gl_hwdec_driver gl_hwdec_dxva2; @@ -47,6 +48,7 @@ static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = { #endif #if HAVE_DXVA2_HWACCEL #if HAVE_EGL_ANGLE + &gl_hwdec_d3d11egl, &gl_hwdec_dxva2egl, #endif #if HAVE_GL_DXINTEROP diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c new file mode 100644 index 0000000000..44bab2c245 --- /dev/null +++ b/video/out/opengl/hwdec_d3d11egl.c @@ -0,0 +1,413 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see . + */ + +#include +#include +#include +#include + +#include "common/common.h" +#include "osdep/timer.h" +#include "osdep/windows_utils.h" +#include "hwdec.h" +#include "video/d3d11va.h" +#include "video/d3d.h" +#include "video/hwdec.h" + +struct priv { + struct mp_d3d_ctx ctx; + + ID3D11Device *d3d11_device; + ID3D11VideoDevice *video_dev; + ID3D11VideoContext *video_ctx; + + EGLDisplay egl_display; + EGLConfig egl_config; + EGLSurface egl_surface; + + ID3D11Texture2D *texture; + ID3D11VideoProcessor *video_proc; + ID3D11VideoProcessorEnumerator *vp_enum; + ID3D11VideoProcessorOutputView *out_view; + int c_w, c_h; + + GLuint gl_texture; +}; + +static void destroy_video_proc(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + + if (p->out_view) + ID3D11VideoProcessorOutputView_Release(p->out_view); + p->out_view = NULL; + + if (p->video_proc) + ID3D11VideoProcessor_Release(p->video_proc); + p->video_proc = NULL; + + if (p->vp_enum) + ID3D11VideoProcessorEnumerator_Release(p->vp_enum); + p->vp_enum = NULL; +} + +static void destroy_objects(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + + gl->DeleteTextures(1, &p->gl_texture); + p->gl_texture = 0; + + if (p->egl_display && p->egl_surface) { + eglReleaseTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER); + eglDestroySurface(p->egl_display, p->egl_surface); + } + p->egl_surface = NULL; + + if (p->texture) + ID3D11Texture2D_Release(p->texture); + p->texture = NULL; + + destroy_video_proc(hw); +} + +static void destroy(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + + destroy_objects(hw); + + if (p->video_ctx) + ID3D11VideoContext_Release(p->video_ctx); + p->video_ctx = NULL; + + if (p->video_dev) + ID3D11VideoDevice_Release(p->video_dev); + p->video_dev = NULL; + + if (p->d3d11_device) + ID3D11Device_Release(p->d3d11_device); + p->d3d11_device = NULL; +} + +static int create(struct gl_hwdec *hw) +{ + if (hw->hwctx) + return -1; + + EGLDisplay egl_display = eglGetCurrentDisplay(); + if (!egl_display) + return -1; + + const char *exts = eglQueryString(egl_display, EGL_EXTENSIONS); + if (!exts || !strstr(exts, "EGL_ANGLE_d3d_share_handle_client_buffer") || + !strstr(exts, "EGL_EXT_device_query")) + return -1; + + PFNEGLQUERYDISPLAYATTRIBEXTPROC p_eglQueryDisplayAttribEXT = + (void *)eglGetProcAddress("eglQueryDisplayAttribEXT"); + PFNEGLQUERYDEVICEATTRIBEXTPROC p_eglQueryDeviceAttribEXT = + (void *)eglGetProcAddress("eglQueryDeviceAttribEXT"); + if (!p_eglQueryDisplayAttribEXT || !p_eglQueryDeviceAttribEXT) + return -1; + + HRESULT hr; + struct priv *p = talloc_zero(hw, struct priv); + hw->priv = p; + + p->egl_display = egl_display; + + EGLAttrib device = 0; + if (!p_eglQueryDisplayAttribEXT(egl_display, EGL_DEVICE_EXT, &device)) + goto fail; + EGLAttrib d3d_device = 0; + if (!p_eglQueryDeviceAttribEXT((EGLDeviceEXT)device, EGL_D3D11_DEVICE_ANGLE, + &d3d_device)) + { + MP_ERR(hw, "Could not get EGL_D3D11_DEVICE_ANGLE from ANGLE.\n"); + goto fail; + } + + p->d3d11_device = (ID3D11Device *)d3d_device; + if (!p->d3d11_device) + goto fail; + ID3D11Device_AddRef(p->d3d11_device); + + ID3D10Multithread *multithread; + hr = ID3D11Device_QueryInterface(p->d3d11_device, &IID_ID3D10Multithread, + (void **)&multithread); + if (FAILED(hr)) { + ID3D10Multithread_Release(multithread); + MP_ERR(hw, "Failed to get Multithread interface: %s\n", + mp_HRESULT_to_str(hr)); + goto fail; + } + ID3D10Multithread_SetMultithreadProtected(multithread, TRUE); + ID3D10Multithread_Release(multithread); + + hr = ID3D11Device_QueryInterface(p->d3d11_device, &IID_ID3D11VideoDevice, + (void **)&p->video_dev); + if (FAILED(hr)) + goto fail; + + ID3D11DeviceContext *device_ctx; + ID3D11Device_GetImmediateContext(p->d3d11_device, &device_ctx); + if (!device_ctx) + goto fail; + hr = ID3D11DeviceContext_QueryInterface(device_ctx, &IID_ID3D11VideoContext, + (void **)&p->video_ctx); + ID3D11DeviceContext_Release(device_ctx); + if (FAILED(hr)) + goto fail; + + EGLint attrs[] = { + EGL_BUFFER_SIZE, 32, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_ALPHA_SIZE, 8, + EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, + EGL_NONE + }; + EGLint count; + if (!eglChooseConfig(p->egl_display, attrs, &p->egl_config, 1, &count) || + !count) { + MP_ERR(hw, "Failed to get EGL surface configuration\n"); + goto fail; + } + + hw->converted_imgfmt = IMGFMT_RGB0; + + p->ctx.d3d11_device = p->d3d11_device; + p->ctx.hwctx.type = HWDEC_D3D11VA; + p->ctx.hwctx.d3d_ctx = &p->ctx; + + hw->hwctx = &p->ctx.hwctx; + return 0; +fail: + destroy(hw); + return -1; +} + +static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + HRESULT hr; + + destroy_objects(hw); + + assert(params->imgfmt == hw->driver->imgfmt); + + D3D11_TEXTURE2D_DESC texdesc = { + .Width = params->w, + .Height = params->h, + .Format = DXGI_FORMAT_B8G8R8A8_UNORM, + .MipLevels = 1, + .ArraySize = 1, + .SampleDesc = { .Count = 1 }, + .Usage = D3D11_USAGE_DEFAULT, + .BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, + .MiscFlags = D3D11_RESOURCE_MISC_SHARED, + }; + hr = ID3D11Device_CreateTexture2D(p->d3d11_device, &texdesc, NULL, &p->texture); + if (FAILED(hr)) { + MP_ERR(hw, "Failed to create texture: %s\n", mp_HRESULT_to_str(hr)); + goto fail; + } + + HANDLE share_handle = NULL; + IDXGIResource *res; + + hr = IUnknown_QueryInterface(p->texture, &IID_IDXGIResource, (void **)&res); + if (FAILED(hr)) + goto fail; + + hr = IDXGIResource_GetSharedHandle(res, &share_handle); + if (FAILED(hr)) + share_handle = NULL; + + IDXGIResource_Release(res); + + if (!share_handle) + goto fail; + + EGLint attrib_list[] = { + EGL_WIDTH, params->w, + EGL_HEIGHT, params->h, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, + EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + EGL_NONE + }; + p->egl_surface = eglCreatePbufferFromClientBuffer( + p->egl_display, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, + share_handle, p->egl_config, attrib_list); + if (p->egl_surface == EGL_NO_SURFACE) { + MP_ERR(hw, "Failed to create EGL surface\n"); + goto fail; + } + + gl->GenTextures(1, &p->gl_texture); + gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl->BindTexture(GL_TEXTURE_2D, 0); + + return 0; +fail: + destroy_objects(hw); + return -1; +} + +static int create_video_proc(struct gl_hwdec *hw, struct mp_image_params *params) +{ + struct priv *p = hw->priv; + HRESULT hr; + + destroy_video_proc(hw); + + // Note: we skip any deinterlacing considerations for now. + D3D11_VIDEO_PROCESSOR_CONTENT_DESC vpdesc = { + .InputWidth = p->c_w, + .InputHeight = p->c_h, + .OutputWidth = params->w, + .OutputHeight = params->h, + }; + hr = ID3D11VideoDevice_CreateVideoProcessorEnumerator(p->video_dev, &vpdesc, + &p->vp_enum); + if (FAILED(hr)) + goto fail; + + // Assume RateConversionIndex==0 always works fine for us. + hr = ID3D11VideoDevice_CreateVideoProcessor(p->video_dev, p->vp_enum, 0, + &p->video_proc); + if (FAILED(hr)) { + MP_ERR(hw, "Failed to create D3D11 video processor.\n"); + goto fail; + } + + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outdesc = { + .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D, + }; + hr = ID3D11VideoDevice_CreateVideoProcessorOutputView(p->video_dev, + (ID3D11Resource *)p->texture, + p->vp_enum, &outdesc, + &p->out_view); + if (FAILED(hr)) + goto fail; + + // Note: libavcodec does not support cropping left/top with hwaccel. + RECT src_rc = { + .right = params->w, + .bottom = params->h, + }; + ID3D11VideoContext_VideoProcessorSetStreamSourceRect(p->video_ctx, + p->video_proc, + 0, TRUE, &src_rc); + + // This is supposed to stop drivers from fucking up the video quality. + ID3D11VideoContext_VideoProcessorSetStreamAutoProcessingMode(p->video_ctx, + p->video_proc, + 0, FALSE); + + D3D11_VIDEO_PROCESSOR_COLOR_SPACE csp = { + .YCbCr_Matrix = params->colorspace != MP_CSP_BT_601, + }; + ID3D11VideoContext_VideoProcessorSetStreamColorSpace(p->video_ctx, + p->video_proc, + 0, &csp); + + return 0; +fail: + destroy_video_proc(hw); + return -1; +} + +static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, + GLuint *out_textures) +{ + struct priv *p = hw->priv; + GL *gl = hw->gl; + HRESULT hr; + ID3D11VideoProcessorInputView *in_view = NULL; + + if (!p->gl_texture) + return -1; + + ID3D11Texture2D *d3d_tex = d3d11_texture_in_mp_image(hw_image); + int d3d_subindex = d3d11_subindex_in_mp_image(hw_image); + if (!d3d_tex) + return -1; + + D3D11_TEXTURE2D_DESC texdesc; + ID3D11Texture2D_GetDesc(d3d_tex, &texdesc); + if (!p->video_proc || p->c_w != texdesc.Width || p->c_h != texdesc.Height) { + p->c_w = texdesc.Width; + p->c_h = texdesc.Height; + if (create_video_proc(hw, &hw_image->params) < 0) + return -1; + } + + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC indesc = { + .FourCC = 0, // huh? + .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D, + .Texture2D = { + .ArraySlice = d3d_subindex, + }, + }; + hr = ID3D11VideoDevice_CreateVideoProcessorInputView(p->video_dev, + (ID3D11Resource *)d3d_tex, + p->vp_enum, &indesc, + &in_view); + if (FAILED(hr)) { + MP_ERR(hw, "Could not create ID3D11VideoProcessorInputView\n"); + return -1; + } + + D3D11_VIDEO_PROCESSOR_STREAM stream = { + .Enable = TRUE, + .pInputSurface = in_view, + }; + hr = ID3D11VideoContext_VideoProcessorBlt(p->video_ctx, p->video_proc, + p->out_view, 0, 1, &stream); + ID3D11VideoProcessorInputView_Release(in_view); + if (FAILED(hr)) { + MP_ERR(hw, "VideoProcessorBlt failed.\n"); + return -1; + } + + gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); + eglBindTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER); + gl->BindTexture(GL_TEXTURE_2D, 0); + + out_textures[0] = p->gl_texture; + return 0; +} + +const struct gl_hwdec_driver gl_hwdec_d3d11egl = { + .name = "d3d11-egl", + .api = HWDEC_D3D11VA, + .imgfmt = IMGFMT_D3D11VA, + .create = create, + .reinit = reinit, + .map_image = map_image, + .destroy = destroy, +}; -- cgit v1.2.3 From dff33893f2ea91f425bceeec6596556d569e0370 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 14:03:30 +0200 Subject: d3d11va: store texture/subindex in IMGFMT_D3D11VA plane pointers Basically this gets rid of the need for the accessors in d3d11va.h, and the code can be cleaned up a little bit. Note that libavcodec only defines a ID3D11VideoDecoderOutputView pointer in the last plane pointers, but it tolerates/passes through the other plane pointers we set. --- video/out/opengl/hwdec_d3d11egl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c index 44bab2c245..abb5831670 100644 --- a/video/out/opengl/hwdec_d3d11egl.c +++ b/video/out/opengl/hwdec_d3d11egl.c @@ -17,6 +17,8 @@ #include #include +#include + #include #include @@ -24,7 +26,6 @@ #include "osdep/timer.h" #include "osdep/windows_utils.h" #include "hwdec.h" -#include "video/d3d11va.h" #include "video/d3d.h" #include "video/hwdec.h" @@ -352,8 +353,8 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, if (!p->gl_texture) return -1; - ID3D11Texture2D *d3d_tex = d3d11_texture_in_mp_image(hw_image); - int d3d_subindex = d3d11_subindex_in_mp_image(hw_image); + ID3D11Texture2D *d3d_tex = (void *)hw_image->planes[1]; + int d3d_subindex = (intptr_t)hw_image->planes[2]; if (!d3d_tex) return -1; -- cgit v1.2.3 From 0b1ba577b14387ca9547d8700203900e5b213dd3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 14:23:03 +0200 Subject: vo_opengl: d3d11egl: print warning on unsupported colorspaces Not much we can do about. If there are many complaints, a mechanism to automatically disable interop in such cases could be added. --- video/out/opengl/hwdec_d3d11egl.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'video/out') diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c index abb5831670..a63707b887 100644 --- a/video/out/opengl/hwdec_d3d11egl.c +++ b/video/out/opengl/hwdec_d3d11egl.c @@ -329,6 +329,17 @@ static int create_video_proc(struct gl_hwdec *hw, struct mp_image_params *params p->video_proc, 0, FALSE); + if ((params->colorspace != MP_CSP_BT_601 && + params->colorspace != MP_CSP_BT_709) || + params->colorlevels != MP_CSP_LEVELS_TV) + { + MP_WARN(hw, "Unsupported video colorspace (%s/%s). Consider disabling " + "hardware decoding, or using --hwdec=d3d11va-copy to get " + "correct output.\n", + m_opt_choice_str(mp_csp_names, params->colorspace), + m_opt_choice_str(mp_csp_levels_names, params->colorlevels)); + } + D3D11_VIDEO_PROCESSOR_COLOR_SPACE csp = { .YCbCr_Matrix = params->colorspace != MP_CSP_BT_601, }; -- cgit v1.2.3 From 9cb036f297d7a3f40aad5ccade182f5449ae7bf8 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 14:35:24 +0200 Subject: vo_opengl: d3d11egl: minor simplification This should be ok. eglBindTexImage() just associates the texture, and does not make a copy (not even a conceptual one). --- video/out/opengl/hwdec_d3d11egl.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c index a63707b887..66df9983d1 100644 --- a/video/out/opengl/hwdec_d3d11egl.c +++ b/video/out/opengl/hwdec_d3d11egl.c @@ -270,6 +270,7 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params) gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + eglBindTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER); gl->BindTexture(GL_TEXTURE_2D, 0); return 0; @@ -357,7 +358,6 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, GLuint *out_textures) { struct priv *p = hw->priv; - GL *gl = hw->gl; HRESULT hr; ID3D11VideoProcessorInputView *in_view = NULL; @@ -406,10 +406,6 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, return -1; } - gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); - eglBindTexImage(p->egl_display, p->egl_surface, EGL_BACK_BUFFER); - gl->BindTexture(GL_TEXTURE_2D, 0); - out_textures[0] = p->gl_texture; return 0; } -- cgit v1.2.3 From 757c8baf8c5bd4598cd5455908e4fa543821837a Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 19:02:04 +0200 Subject: vo_opengl: always use sized internal formats This shouldn't make much of a difference, but should make the following commit simpler. --- video/out/opengl/video.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'video/out') diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index de5c49da34..b517e9fb2a 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -261,10 +261,10 @@ static const struct fmt_entry mp_to_gl_formats[] = { }; static const struct fmt_entry gl_byte_formats[] = { - {0, GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 - {0, GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 - {0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 - {0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 + {0, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8 + {0, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8 + {0, GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 + {0, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 {0, GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // 1 x 16 {0, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // 2 x 16 {0, GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT}, // 3 x 16 @@ -307,10 +307,10 @@ static const struct fmt_entry gl_byte_formats_gles2[] = { }; static const struct fmt_entry gl_byte_formats_legacy[] = { - {0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, // 1 x 8 - {0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, // 2 x 8 - {0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 - {0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 + {0, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE}, // 1 x 8 + {0, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, // 2 x 8 + {0, GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8 + {0, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8 {0, GL_LUMINANCE16, GL_LUMINANCE, GL_UNSIGNED_SHORT},// 1 x 16 {0, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT},// 2 x 16 {0, GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT},// 3 x 16 -- cgit v1.2.3 From 9d16837c99c91e786e517b2520afa79bcdc433b3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 27 Apr 2016 19:14:10 +0200 Subject: vo_opengl: support GL_EXT_texture_norm16 on GLES This gives us 16 bit fixed-point integer texture formats, including ability to sample from them with linear filtering, and using them as FBO attachments. The integer texture format path is still there for the sake of ANGLE, which does not support GL_EXT_texture_norm16 yet. The change to pass_dither() is needed, because the code path using GL_R16 for the dither texture relies on glTexImage2D being able to convert from GL_FLOAT to GL_R16. GLES does not allow this. This could be trivially fixed by doing the conversion ourselves, but I'm too la