aboutsummaryrefslogtreecommitdiffhomepage
path: root/video/decode/dxva2.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/decode/dxva2.c')
-rw-r--r--video/decode/dxva2.c196
1 files changed, 65 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");