diff options
-rw-r--r-- | video/decode/dxva2.c | 196 | ||||
-rw-r--r-- | video/dxva2.c | 122 | ||||
-rw-r--r-- | video/dxva2.h | 34 | ||||
-rw-r--r-- | wscript_build.py | 1 |
4 files changed, 222 insertions, 131 deletions
diff --git a/video/decode/dxva2.c b/video/decode/dxva2.c index 8cdda9f11b..8bfb75193d 100644 --- a/video/decode/dxva2.c +++ b/video/decode/dxva2.c @@ -25,8 +25,6 @@ #include <stdint.h> -#include <d3d9.h> -#include <dxva2api.h> #include <ks.h> #include <libavcodec/dxva2.h> @@ -36,6 +34,7 @@ #include "common/av_common.h" #include "osdep/windows_utils.h" #include "video/fmt-conversion.h" +#include "video/dxva2.h" #include "video/mp_image_pool.h" #include "video/hwdec.h" #include "video/d3d.h" @@ -97,10 +96,11 @@ static const dxva2_mode dxva2_modes[] = { #undef MODE -typedef struct surface_info { - int used; - uint64_t age; -} surface_info; +struct dxva2_decoder { + DXVA2_ConfigPictureDecode config; + IDirectXVideoDecoder *decoder; + struct mp_image_pool *pool; +}; typedef struct DXVA2Context { struct mp_log *log; @@ -114,54 +114,18 @@ typedef struct DXVA2Context { IDirect3DDevice9 *d3d9device; IDirect3DDeviceManager9 *d3d9devmgr; IDirectXVideoDecoderService *decoder_service; - IDirectXVideoDecoder *decoder; - - DXVA2_ConfigPictureDecode decoder_config; + struct dxva2_decoder *decoder; - LPDIRECT3DSURFACE9 *surfaces; - surface_info *surface_infos; - uint32_t num_surfaces; - uint64_t surface_age; - - struct mp_image_pool *sw_pool; + struct mp_image_pool *sw_pool; } DXVA2Context; -typedef struct DXVA2SurfaceWrapper { - DXVA2Context *ctx; - LPDIRECT3DSURFACE9 surface; - IDirectXVideoDecoder *decoder; -} DXVA2SurfaceWrapper; - -static void dxva2_destroy_decoder(struct lavc_ctx *s) -{ - DXVA2Context *ctx = s->hwdec_priv; - int i; - - if (ctx->surfaces) { - for (i = 0; i < ctx->num_surfaces; i++) { - if (ctx->surfaces[i]) - IDirect3DSurface9_Release(ctx->surfaces[i]); - } - } - av_freep(&ctx->surfaces); - av_freep(&ctx->surface_infos); - ctx->num_surfaces = 0; - ctx->surface_age = 0; - - if (ctx->decoder) { - IDirectXVideoDecoder_Release(ctx->decoder); - ctx->decoder = NULL; - } -} - static void dxva2_uninit(struct lavc_ctx *s) { DXVA2Context *ctx = s->hwdec_priv; if (!ctx) return; - if (ctx->decoder) - dxva2_destroy_decoder(s); + talloc_free(ctx->decoder); if (ctx->decoder_service) IDirectXVideoDecoderService_Release(ctx->decoder_service); @@ -189,59 +153,14 @@ static void dxva2_uninit(struct lavc_ctx *s) s->hwdec_priv = NULL; } -static void dxva2_release_img(void *ptr) -{ - DXVA2SurfaceWrapper *w = ptr; - DXVA2Context *ctx = w->ctx; - int i; - - for (i = 0; i < ctx->num_surfaces; i++) { - if (ctx->surfaces[i] == w->surface) { - ctx->surface_infos[i].used = 0; - break; - } - } - IDirect3DSurface9_Release(w->surface); - IDirectXVideoDecoder_Release(w->decoder); - av_free(w); -} - -static struct mp_image *dxva2_allocate_image(struct lavc_ctx *s, - int img_w, int img_h) +static struct mp_image *dxva2_allocate_image(struct lavc_ctx *s, int w, int h) { DXVA2Context *ctx = s->hwdec_priv; - int i, old_unused = -1; - for (i = 0; i < ctx->num_surfaces; i++) { - surface_info *info = &ctx->surface_infos[i]; - if (!info->used && (old_unused == -1 || info->age < ctx->surface_infos[old_unused].age)) - old_unused = i; - } - if (old_unused == -1) { - MP_ERR(ctx, "No free DXVA2 surface!\n"); - return NULL; - } - i = old_unused; - - DXVA2SurfaceWrapper *w = av_mallocz(sizeof(*w)); - if (!w) - return NULL; - - w->ctx = ctx; - w->surface = ctx->surfaces[i];; - IDirect3DSurface9_AddRef(w->surface); - w->decoder = ctx->decoder; - IDirectXVideoDecoder_AddRef(w->decoder); - - ctx->surface_infos[i].used = 1; - ctx->surface_infos[i].age = ctx->surface_age++; - - struct mp_image mpi = {0}; - mp_image_setfmt(&mpi, IMGFMT_DXVA2); - mp_image_set_size(&mpi, img_w, img_h); - mpi.planes[3] = (void *)w->surface; - - return mp_image_new_custom_ref(&mpi, w, dxva2_release_img); + struct mp_image *img = mp_image_pool_get(ctx->decoder->pool, IMGFMT_DXVA2, w, h); + if (!img) + MP_ERR(ctx, "Failed to allocate additional DXVA2 surface.\n"); + return img; } static void copy_nv12(struct mp_image *dest, uint8_t *src_bits, @@ -262,7 +181,7 @@ static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s, struct mp_image *img) { DXVA2Context *ctx = s->hwdec_priv; - LPDIRECT3DSURFACE9 surface = (LPDIRECT3DSURFACE9)img->planes[3]; + LPDIRECT3DSURFACE9 surface = d3d9_surface_in_mp_image(img); D3DSURFACE_DESC surfaceDesc; D3DLOCKED_RECT LockedRect; HRESULT hr; @@ -469,20 +388,30 @@ static int dxva2_get_decoder_configuration(struct lavc_ctx *s, return 0; } +static void dxva2_destroy_decoder(void *arg) +{ + struct dxva2_decoder *decoder = arg; + if (decoder->decoder) + IDirectXVideoDecoder_Release(decoder->decoder); +} + static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h, enum AVCodecID codec_id, int profile) { DXVA2Context *ctx = s->hwdec_priv; struct dxva_context *dxva_ctx = s->avctx->hwaccel_context; + void *tmp = talloc_new(NULL); GUID *guid_list = NULL; unsigned guid_count = 0, i, j; GUID device_guid = GUID_NULL; D3DFORMAT target_format = 0; DXVA2_VideoDesc desc = { 0 }; - DXVA2_ConfigPictureDecode config; HRESULT hr; - int surface_alignment; - int ret; + struct dxva2_decoder *decoder; + int surface_alignment, num_surfaces; + struct mp_image **imgs; + LPDIRECT3DSURFACE9 *surfaces; + int ret = -1; hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); if (FAILED(hr)) { @@ -556,8 +485,10 @@ static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h, desc.SampleHeight = h; desc.Format = target_format; - ret = dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc, &config); - if (ret < 0) { + decoder = talloc_zero(tmp, struct dxva2_decoder); + talloc_set_destructor(decoder, dxva2_destroy_decoder); + if (dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc, + &decoder->config) < 0) { goto fail; } @@ -572,50 +503,53 @@ static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h, else surface_alignment = 16; - ctx->num_surfaces = hwdec_get_max_refs(s) + ADDITIONAL_SURFACES; - - ctx->surfaces = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces)); - ctx->surface_infos = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos)); + num_surfaces = hwdec_get_max_refs(s) + ADDITIONAL_SURFACES; - if (!ctx->surfaces || !ctx->surface_infos) { - MP_ERR(ctx, "Unable to allocate surface arrays\n"); - goto fail; - } + decoder->pool = talloc_steal(decoder, mp_image_pool_new(num_surfaces)); + dxva2_pool_set_allocator(decoder->pool, ctx->decoder_service, + target_format, surface_alignment); - hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service, - FFALIGN(w, surface_alignment), - FFALIGN(h, surface_alignment), - ctx->num_surfaces - 1, - target_format, D3DPOOL_DEFAULT, 0, - DXVA2_VideoDecoderRenderTarget, - ctx->surfaces, NULL); - if (FAILED(hr)) { - MP_ERR(ctx, "Failed to create %d video surfaces\n", ctx->num_surfaces); - goto fail; + // Preallocate images from the pool so the surfaces can be used to create + // the decoder and passed to ffmpeg in the dxva_ctx. The mp_images + // themselves will be freed (returned to the pool) along with the temporary + // talloc context on exit from this function. + imgs = talloc_array(tmp, struct mp_image *, num_surfaces); + surfaces = talloc_array(decoder->pool, LPDIRECT3DSURFACE9, num_surfaces); + for (i = 0; i < num_surfaces; i++) { + imgs[i] = talloc_steal( + imgs, mp_image_pool_get(decoder->pool, IMGFMT_DXVA2, w, h)); + surfaces[i] = d3d9_surface_in_mp_image(imgs[i]); } hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, - &desc, &config, ctx->surfaces, - ctx->num_surfaces, &ctx->decoder); + &desc, &decoder->config, surfaces, + num_surfaces, &decoder->decoder); + if (FAILED(hr)) { MP_ERR(ctx, "Failed to create DXVA2 video decoder\n"); goto fail; } - ctx->decoder_config = config; + // According to ffmpeg_dxva2.c, the surfaces must not outlive the + // IDirectXVideoDecoder they were used to create. This adds a reference for + // each one of them, which is released on final mp_image destruction. + for (i = 0; i < num_surfaces; i++) + dxva2_img_ref_decoder(imgs[i], decoder->decoder); - dxva_ctx->cfg = &ctx->decoder_config; - dxva_ctx->decoder = ctx->decoder; - dxva_ctx->surface = ctx->surfaces; - dxva_ctx->surface_count = ctx->num_surfaces; + // Pass required information on to ffmpeg. + dxva_ctx->cfg = &decoder->config; + dxva_ctx->decoder = decoder->decoder; + dxva_ctx->surface = surfaces; + dxva_ctx->surface_count = num_surfaces; if (IsEqualGUID(&device_guid, &DXVADDI_Intel_ModeH264_E)) dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; - return 0; + ctx->decoder = talloc_steal(NULL, decoder); + ret = 0; fail: - dxva2_destroy_decoder(s); - return -1; + talloc_free(tmp); + return ret; } static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h) @@ -635,8 +569,8 @@ static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h) return -1; } - if (ctx->decoder) - dxva2_destroy_decoder(s); + talloc_free(ctx->decoder); + ctx->decoder = NULL; if (dxva2_create_decoder(s, w, h, codec, profile) < 0) { MP_ERR(ctx, "Error creating the DXVA2 decoder\n"); diff --git a/video/dxva2.c b/video/dxva2.c new file mode 100644 index 0000000000..cf0bf3d08c --- /dev/null +++ b/video/dxva2.c @@ -0,0 +1,122 @@ +/* + * This file is part of mpv. + * + * 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 "common/av_common.h" +#include "dxva2.h" +#include "mp_image.h" +#include "img_format.h" +#include "mp_image_pool.h" + +struct dxva2_surface { + HMODULE d3dlib; + HMODULE dxva2lib; + + IDirectXVideoDecoder *decoder; + LPDIRECT3DSURFACE9 surface; +}; + +LPDIRECT3DSURFACE9 d3d9_surface_in_mp_image(struct mp_image *mpi) +{ + return mpi && mpi->imgfmt == IMGFMT_DXVA2 ? + (LPDIRECT3DSURFACE9)mpi->planes[3] : NULL; +} + +void dxva2_img_ref_decoder(struct mp_image *mpi, IDirectXVideoDecoder *decoder) +{ + assert(mpi->imgfmt == IMGFMT_DXVA2); + struct dxva2_surface *surface = (struct dxva2_surface *)mpi->planes[0]; + if (surface->decoder) + IDirectXVideoDecoder_Release(surface->decoder); + surface->decoder = decoder; + IDirectXVideoDecoder_AddRef(surface->decoder); +} + +static void dxva2_pool_release_img(void *arg) +{ + struct dxva2_surface *surface = arg; + if (surface->surface) + IDirect3DSurface9_Release(surface->surface); + + if (surface->decoder) + IDirectXVideoDecoder_Release(surface->decoder); + + if (surface->dxva2lib) + FreeLibrary(surface->dxva2lib); + + if (surface->d3dlib) + FreeLibrary(surface->d3dlib); + + talloc_free(surface); +} + +struct pool_alloc_ctx { + IDirectXVideoDecoderService *decoder_service; + D3DFORMAT target_format; + int surface_alignment; +}; + +static struct mp_image *dxva2_pool_alloc_img(void *arg, int fmt, int w, int h) +{ + if (fmt != IMGFMT_DXVA2) + return NULL; + struct dxva2_surface *surface = talloc_zero(NULL, struct dxva2_surface); + + // Add additional references to the libraries which might otherwise be freed + // before the surface, which is observed to lead to bad behaviour + surface->d3dlib = LoadLibrary(L"d3d9.dll"); + surface->dxva2lib = LoadLibrary(L"dxva2.dll"); + if (!surface->d3dlib || !surface->dxva2lib) + goto fail; + + struct pool_alloc_ctx *alloc_ctx = arg; + HRESULT hr = IDirectXVideoDecoderService_CreateSurface( + alloc_ctx->decoder_service, + FFALIGN(w, alloc_ctx->surface_alignment), + FFALIGN(h, alloc_ctx->surface_alignment), + 0, alloc_ctx->target_format, D3DPOOL_DEFAULT, 0, + DXVA2_VideoDecoderRenderTarget, + &surface->surface, NULL); + if (FAILED(hr)) + goto fail; + + struct mp_image mpi = {0}; + mp_image_setfmt(&mpi, IMGFMT_DXVA2); + mp_image_set_size(&mpi, w, h); + mpi.planes[0] = (void *)surface; + mpi.planes[3] = (void *)surface->surface; + + return mp_image_new_custom_ref(&mpi, surface, dxva2_pool_release_img); +fail: + dxva2_pool_release_img(surface); + return NULL; +} + +void dxva2_pool_set_allocator(struct mp_image_pool *pool, + IDirectXVideoDecoderService *decoder_service, + D3DFORMAT target_format, int surface_alignment) +{ + struct pool_alloc_ctx *alloc_ctx = talloc_ptrtype(pool, alloc_ctx); + *alloc_ctx = (struct pool_alloc_ctx){ + decoder_service = decoder_service, + target_format = target_format, + surface_alignment = surface_alignment + }; + mp_image_pool_set_allocator(pool, dxva2_pool_alloc_img, alloc_ctx); + mp_image_pool_set_lru(pool); +} diff --git a/video/dxva2.h b/video/dxva2.h new file mode 100644 index 0000000000..4acb9c99d4 --- /dev/null +++ b/video/dxva2.h @@ -0,0 +1,34 @@ +/* + * This file is part of mpv. + * + * 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/>. + */ + +#ifndef MPV_DXVA2_H +#define MPV_DXVA2_H + +#include <d3d9.h> +#include <dxva2api.h> + +struct mp_image; +struct mp_image_pool; + +LPDIRECT3DSURFACE9 d3d9_surface_in_mp_image(struct mp_image *mpi); +void dxva2_img_ref_decoder(struct mp_image *mpi, IDirectXVideoDecoder *decoder); + +void dxva2_pool_set_allocator(struct mp_image_pool *pool, + IDirectXVideoDecoderService *decoder_service, + D3DFORMAT target_format, int surface_alignment); + +#endif diff --git a/wscript_build.py b/wscript_build.py index 090ac96181..75dc763f35 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -280,6 +280,7 @@ def build(ctx): ( "video/mp_image.c" ), ( "video/mp_image_pool.c" ), ( "video/sws_utils.c" ), + ( "video/dxva2.c", "dxva2-hwaccel" ), ( "video/vaapi.c", "vaapi" ), ( "video/vdpau.c", "vdpau" ), ( "video/vdpau_mixer.c", "vdpau" ), |