bool DXVA2Decoder::CreateDecoder(void) { if (!m_service || !m_context.surface) return false; HRESULT hr = IDirectXVideoDecoderService_CreateVideoDecoder( m_service, m_input, &m_format, const_cast<DXVA2_ConfigPictureDecode*>(m_context.cfg), m_context.surface, m_context.surface_count, &m_context.decoder); if (FAILED(hr)) return false; LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created decoder: %1->%2x%3 (%4 surfaces)") .arg(toString(m_codec_id)).arg(m_width).arg(m_height) .arg(m_context.surface_count)); return true; }
/** * It creates a DXVA2 decoder using the given video format */ static int DxCreateVideoDecoder(vlc_va_t *va, int codec_id, const video_format_t *fmt, bool b_threading) { VLC_UNUSED(b_threading); vlc_va_sys_t *p_sys = va->sys; directx_sys_t *sys = &va->sys->dx_sys; if (FAILED(IDirectXVideoDecoderService_CreateSurface((IDirectXVideoDecoderService*) sys->d3ddec, sys->surface_width, sys->surface_height, sys->surface_count - 1, p_sys->render, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, (LPDIRECT3DSURFACE9*) sys->hw_surface, NULL))) { msg_Err(va, "IDirectXVideoAccelerationService_CreateSurface failed"); sys->surface_count = 0; return VLC_EGENERIC; } msg_Dbg(va, "IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)", sys->surface_count, sys->surface_width, sys->surface_height); /* */ DXVA2_VideoDesc dsc; ZeroMemory(&dsc, sizeof(dsc)); dsc.SampleWidth = fmt->i_width; dsc.SampleHeight = fmt->i_height; dsc.Format = p_sys->render; if (fmt->i_frame_rate > 0 && fmt->i_frame_rate_base > 0) { dsc.InputSampleFreq.Numerator = fmt->i_frame_rate; dsc.InputSampleFreq.Denominator = fmt->i_frame_rate_base; } else { dsc.InputSampleFreq.Numerator = 0; dsc.InputSampleFreq.Denominator = 0; } dsc.OutputFrameFreq = dsc.InputSampleFreq; dsc.UABProtectionLevel = FALSE; dsc.Reserved = 0; /* FIXME I am unsure we can let unknown everywhere */ DXVA2_ExtendedFormat *ext = &dsc.SampleFormat; ext->SampleFormat = 0;//DXVA2_SampleUnknown; ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown; ext->NominalRange = 0;//DXVA2_NominalRange_Unknown; ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown; ext->VideoLighting = 0;//DXVA2_VideoLighting_Unknown; ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown; ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown; /* List all configurations available for the decoder */ UINT cfg_count = 0; DXVA2_ConfigPictureDecode *cfg_list = NULL; if (FAILED(IDirectXVideoDecoderService_GetDecoderConfigurations((IDirectXVideoDecoderService*) sys->d3ddec, &sys->input, &dsc, NULL, &cfg_count, &cfg_list))) { msg_Err(va, "IDirectXVideoDecoderService_GetDecoderConfigurations failed"); return VLC_EGENERIC; } msg_Dbg(va, "we got %d decoder configurations", cfg_count); /* Select the best decoder configuration */ int cfg_score = 0; for (unsigned i = 0; i < cfg_count; i++) { const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; /* */ msg_Dbg(va, "configuration[%d] ConfigBitstreamRaw %d", i, cfg->ConfigBitstreamRaw); /* */ int score; if (cfg->ConfigBitstreamRaw == 1) score = 1; else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) score = 2; else continue; if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt)) score += 16; if (cfg_score < score) { p_sys->cfg = *cfg; cfg_score = score; } } CoTaskMemFree(cfg_list); if (cfg_score <= 0) { msg_Err(va, "Failed to find a supported decoder configuration"); return VLC_EGENERIC; } /* Create the decoder */ IDirectXVideoDecoder *decoder; if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder((IDirectXVideoDecoderService*) sys->d3ddec, &sys->input, &dsc, &p_sys->cfg, (LPDIRECT3DSURFACE9*) sys->hw_surface, sys->surface_count, &decoder))) { msg_Err(va, "IDirectXVideoDecoderService_CreateVideoDecoder failed"); return VLC_EGENERIC; } sys->decoder = (IUnknown*) decoder; msg_Dbg(va, "IDirectXVideoDecoderService_CreateVideoDecoder succeed"); return VLC_SUCCESS; }
/** * It creates a DXVA2 decoder using the given video format */ static int DxCreateVideoDecoder(vlc_va_dxva2_t *va, int codec_id, const video_format_t *fmt) { /* */ msg_Dbg(va->log, "DxCreateVideoDecoder id %d %dx%d", codec_id, fmt->i_width, fmt->i_height); va->width = fmt->i_width; va->height = fmt->i_height; /* Allocates all surfaces needed for the decoder */ va->surface_width = (fmt->i_width + 15) & ~15; va->surface_height = (fmt->i_height + 15) & ~15; int surface_count; switch (codec_id) { case AV_CODEC_ID_H264: surface_count = 16 + 1 + 2; break; default: surface_count = 2 + 1; break; } if (surface_count > VA_DXVA2_MAX_SURFACE_COUNT) return VLC_EGENERIC; va->surface_count = surface_count; if (FAILED(IDirectXVideoDecoderService_CreateSurface(va->vs, va->surface_width, va->surface_height, va->surface_count - 1, va->render, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, va->hw_surface, NULL))) { msg_Err(va->log, "IDirectXVideoAccelerationService_CreateSurface failed"); va->surface_count = 0; return VLC_EGENERIC; } for (unsigned i = 0; i < va->surface_count; i++) { vlc_va_surface_t *surface = &va->surface[i]; surface->d3d = va->hw_surface[i]; surface->refcount = 0; surface->order = 0; } msg_Dbg(va->log, "IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)", va->surface_count, fmt->i_width, fmt->i_height); /* */ DXVA2_VideoDesc dsc; ZeroMemory(&dsc, sizeof(dsc)); dsc.SampleWidth = fmt->i_width; dsc.SampleHeight = fmt->i_height; dsc.Format = va->render; if (fmt->i_frame_rate > 0 && fmt->i_frame_rate_base > 0) { dsc.InputSampleFreq.Numerator = fmt->i_frame_rate; dsc.InputSampleFreq.Denominator = fmt->i_frame_rate_base; } else { dsc.InputSampleFreq.Numerator = 0; dsc.InputSampleFreq.Denominator = 0; } dsc.OutputFrameFreq = dsc.InputSampleFreq; dsc.UABProtectionLevel = FALSE; dsc.Reserved = 0; /* FIXME I am unsure we can let unknown everywhere */ DXVA2_ExtendedFormat *ext = &dsc.SampleFormat; ext->SampleFormat = 0;//DXVA2_SampleUnknown; ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown; ext->NominalRange = 0;//DXVA2_NominalRange_Unknown; ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown; ext->VideoLighting = 0;//DXVA2_VideoLighting_Unknown; ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown; ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown; /* List all configurations available for the decoder */ UINT cfg_count = 0; DXVA2_ConfigPictureDecode *cfg_list = NULL; if (FAILED(IDirectXVideoDecoderService_GetDecoderConfigurations(va->vs, &va->input, &dsc, NULL, &cfg_count, &cfg_list))) { msg_Err(va->log, "IDirectXVideoDecoderService_GetDecoderConfigurations failed"); return VLC_EGENERIC; } msg_Dbg(va->log, "we got %d decoder configurations", cfg_count); /* Select the best decoder configuration */ int cfg_score = 0; for (unsigned i = 0; i < cfg_count; i++) { const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; /* */ msg_Dbg(va->log, "configuration[%d] ConfigBitstreamRaw %d", i, cfg->ConfigBitstreamRaw); /* */ int score; if (cfg->ConfigBitstreamRaw == 1) score = 1; else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) score = 2; else continue; if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt)) score += 16; if (cfg_score < score) { va->cfg = *cfg; cfg_score = score; } } CoTaskMemFree(cfg_list); if (cfg_score <= 0) { msg_Err(va->log, "Failed to find a supported decoder configuration"); return VLC_EGENERIC; } /* Create the decoder */ IDirectXVideoDecoder *decoder; if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder(va->vs, &va->input, &dsc, &va->cfg, va->hw_surface, va->surface_count, &decoder))) { msg_Err(va->log, "IDirectXVideoDecoderService_CreateVideoDecoder failed"); return VLC_EGENERIC; } va->decoder = decoder; msg_Dbg(va->log, "IDirectXVideoDecoderService_CreateVideoDecoder succeed"); return VLC_SUCCESS; }
static int dxva2_create_decoder(AVCodecContext *s) { HwAccelContext *hac = s->opaque; DXVA2Context *ctx = hac->hwaccel_ctx; struct dxva_context *dxva_ctx = s->hwaccel_context; 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; hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n"); goto fail; } for (i = 0; dxva2_modes[i].guid; i++) { D3DFORMAT *target_list = NULL; unsigned target_count = 0; const dxva2_mode *mode = &dxva2_modes[i]; if (mode->codec != s->codec_id) continue; for (j = 0; j < guid_count; j++) { if (IsEqualGUID(mode->guid, &guid_list[j])) break; } if (j == guid_count) continue; hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); if (FAILED(hr)) { continue; } for (j = 0; j < target_count; j++) { const D3DFORMAT format = target_list[j]; if (format == MKTAG('N','V','1','2')) { target_format = format; break; } } CoTaskMemFree(target_list); if (target_format) { device_guid = *mode->guid; break; } } CoTaskMemFree(guid_list); if (IsEqualGUID(&device_guid, &GUID_NULL)) { av_log(NULL, loglevel, "No decoder device for codec found\n"); goto fail; } desc.SampleWidth = s->coded_width; desc.SampleHeight = s->coded_height; desc.Format = target_format; ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config); if (ret < 0) { goto fail; } /* decoding MPEG-2 requires additional alignment on some Intel GPUs, but it causes issues for H.264 on certain AMD GPUs..... */ if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) surface_alignment = 32; /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure all coding features have enough room to work with */ else if (s->codec_id == AV_CODEC_ID_HEVC) surface_alignment = 128; else surface_alignment = 16; /* 4 base work surfaces */ ctx->num_surfaces = 4; /* add surfaces based on number of possible refs */ if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC) ctx->num_surfaces += 16; else ctx->num_surfaces += 2; /* add extra surfaces for frame threading */ if (s->active_thread_type & FF_THREAD_FRAME) ctx->num_surfaces += s->thread_count; ctx->surfaces = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces)); ctx->surface_infos = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos)); if (!ctx->surfaces || !ctx->surface_infos) { av_log(NULL, loglevel, "Unable to allocate surface arrays\n"); goto fail; } hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service, FFALIGN(s->coded_width, surface_alignment), FFALIGN(s->coded_height, surface_alignment), ctx->num_surfaces - 1, target_format, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, ctx->surfaces, NULL); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create %d video surfaces\n", ctx->num_surfaces); goto fail; } hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, &desc, &config, ctx->surfaces, ctx->num_surfaces, &ctx->decoder); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n"); goto fail; } ctx->decoder_guid = device_guid; ctx->decoder_config = config; dxva_ctx->cfg = &ctx->decoder_config; dxva_ctx->decoder = ctx->decoder; dxva_ctx->surface = ctx->surfaces; dxva_ctx->surface_count = ctx->num_surfaces; if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E)) dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; return 0; fail: dxva2_destroy_decoder(s); return AVERROR(EINVAL); }
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; 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; hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); if (FAILED(hr)) { MP_ERR(ctx, "Failed to retrieve decoder device GUIDs\n"); goto fail; } // dump all decoder info MP_VERBOSE(ctx, "%d decoder devices:\n", (int)guid_count); for (j = 0; j < guid_count; j++) { GUID *guid = &guid_list[j]; const char *name = "<unknown>"; for (i = 0; dxva2_modes[i].guid; i++) { if (IsEqualGUID(dxva2_modes[i].guid, guid)) name = dxva2_modes[i].name; } D3DFORMAT *target_list = NULL; unsigned target_count = 0; hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, guid, &target_count, &target_list); if (FAILED(hr)) continue; char fmts[256] = {0}; for (i = 0; i < target_count; i++) mp_snprintf_cat(fmts, sizeof(fmts), " %s", mp_tag_str(target_list[i])); CoTaskMemFree(target_list); MP_VERBOSE(ctx, "%s %s %s\n", mp_GUID_to_str(guid), name, fmts); } // find a suitable decoder for (i = 0; dxva2_modes[i].guid; i++) { D3DFORMAT *target_list = NULL; unsigned target_count = 0; const dxva2_mode *mode = &dxva2_modes[i]; if (mode->codec != codec_id) continue; for (j = 0; j < guid_count; j++) { if (IsEqualGUID(mode->guid, &guid_list[j])) break; } if (j == guid_count) continue; hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); if (FAILED(hr)) { continue; } for (j = 0; j < target_count; j++) { const D3DFORMAT format = target_list[j]; if (format == MKTAG('N','V','1','2')) { target_format = format; break; } } CoTaskMemFree(target_list); if (target_format) { device_guid = *mode->guid; break; } } CoTaskMemFree(guid_list); if (IsEqualGUID(&device_guid, &GUID_NULL)) { MP_ERR(ctx, "No decoder device for codec found\n"); goto fail; } desc.SampleWidth = w; desc.SampleHeight = h; desc.Format = target_format; ret = dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc, &config); if (ret < 0) { goto fail; } /* decoding MPEG-2 requires additional alignment on some Intel GPUs, but it causes issues for H.264 on certain AMD GPUs..... */ if (codec_id == AV_CODEC_ID_MPEG2VIDEO) surface_alignment = 32; /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure all coding features have enough room to work with */ else if (codec_id == AV_CODEC_ID_HEVC) surface_alignment = 128; else surface_alignment = 16; /* 4 base work surfaces */ ctx->num_surfaces = 4 + ADDTIONAL_SURFACES; /* add surfaces based on number of possible refs */ if (codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_HEVC) ctx->num_surfaces += 16; else ctx->num_surfaces += 2; ctx->surfaces = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces)); ctx->surface_infos = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos)); if (!ctx->surfaces || !ctx->surface_infos) { MP_ERR(ctx, "Unable to allocate surface arrays\n"); goto fail; } 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; } hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, &desc, &config, ctx->surfaces, ctx->num_surfaces, &ctx->decoder); if (FAILED(hr)) { MP_ERR(ctx, "Failed to create DXVA2 video decoder\n"); goto fail; } ctx->decoder_guid = device_guid; ctx->decoder_config = config; dxva_ctx->cfg = &ctx->decoder_config; dxva_ctx->decoder = ctx->decoder; dxva_ctx->surface = ctx->surfaces; dxva_ctx->surface_count = ctx->num_surfaces; if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E)) dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; return 0; fail: dxva2_destroy_decoder(s); return -1; }
static void test_decoder_resolution(IDirectXVideoDecoderService *service, REFGUID const guid, D3DFORMAT format, UINT width, UINT height, UINT surface_count) { HRESULT hr; DXVA2_VideoDesc desc; UINT count; DXVA2_ConfigPictureDecode *configs; DXVA2_ConfigPictureDecode config; IDirect3DSurface9 **surfaces; IDirectXVideoDecoder *decoder; UINT i; trace("Analysing buffer sizes for: %s (%u x %u)\n", convert_decoderguid_to_str(guid), width, height); memset(&desc, 0, sizeof(desc)); desc.SampleWidth = width; desc.SampleHeight = height; desc.Format = format; desc.InputSampleFreq.Numerator = 25; desc.InputSampleFreq.Denominator = 1; desc.OutputFrameFreq.Numerator = 25; desc.OutputFrameFreq.Denominator = 1; if (0) /* crashes on windows */ { hr = IDirectXVideoDecoderService_GetDecoderConfigurations(service, guid, &desc, NULL, NULL, NULL); ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", hr); hr = IDirectXVideoDecoderService_GetDecoderConfigurations(service, guid, &desc, NULL, &count, NULL); ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", hr); hr = IDirectXVideoDecoderService_GetDecoderConfigurations(service, guid, &desc, NULL, NULL, &configs); ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", hr); } hr = IDirectXVideoDecoderService_GetDecoderConfigurations(service, &GUID_NULL, &desc, NULL, &count, &configs); ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %x\n", hr); hr = IDirectXVideoDecoderService_GetDecoderConfigurations(service, guid, &desc, NULL, &count, &configs); ok(!hr, "Failed to get decoder congiruation for: %s\n", convert_decoderguid_to_str(guid)); if (hr) return; if (!count) { skip("Decoder for %s does not support a single decoder configuration? Skipping test\n", convert_decoderguid_to_str(guid)); return; } memcpy(&config, &configs[0], sizeof(config)); CoTaskMemFree(configs); surfaces = (void*)HeapAlloc(GetProcessHeap(), 0, surface_count * sizeof(IDirect3DSurface9)); if (!surfaces) { skip("Failed to allocate memory for surfaces\n"); return; } hr = IDirectXVideoDecoderService_CreateSurface(service, width, height, surface_count-1, format, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, surfaces, NULL); ok(!hr, "Failed to create surfaces: %x\n", hr); if (hr) { HeapFree(GetProcessHeap(), 0, surfaces); return; } hr = IDirectXVideoDecoderService_CreateVideoDecoder(service, guid, &desc, &configs[0], surfaces, surface_count, &decoder); ok(!hr, "Failed to create decoder for %s: %x\n", convert_decoderguid_to_str(guid), hr); if (!hr) { test_decoding(decoder, guid, surfaces); IDirectXVideoDecoder_Release(decoder); } for (i = 0; i < surface_count; i++) IDirect3DSurface9_Release(surfaces[i]); HeapFree(GetProcessHeap(), 0, surfaces); }
static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h) { HRESULT hr; int ret = -1; struct priv *p = s->hwdec_priv; TA_FREEP(&p->decoder_pool); int n_surfaces = hwdec_get_max_refs(s) + ADDITIONAL_SURFACES; IDirect3DSurface9 **surfaces = NULL; IDirectXVideoDecoder *decoder = NULL; void *tmp = talloc_new(NULL); UINT n_guids; GUID *device_guids; hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids( p->decoder_service, &n_guids, &device_guids); if (FAILED(hr)) { MP_ERR(p, "Failed to retrieve decoder device GUIDs: %s\n", mp_HRESULT_to_str(hr)); goto done; } dump_decoder_info(s, device_guids, n_guids); struct d3d_decoder_fmt fmt = d3d_select_decoder_mode(s, device_guids, n_guids, d3d9_formats, MP_ARRAY_SIZE(d3d9_formats), dxva2_format_supported); CoTaskMemFree(device_guids); if (!fmt.format) { MP_ERR(p, "Failed to find a suitable decoder\n"); goto done; } p->mpfmt_decoded = fmt.format->mpfmt; struct mp_image_pool *decoder_pool = talloc_steal(tmp, mp_image_pool_new(n_surfaces)); DXVA2_ConfigPictureDecode *decoder_config = talloc_zero(decoder_pool, DXVA2_ConfigPictureDecode); int w_align = w, h_align = h; d3d_surface_align(s, &w_align, &h_align); DXVA2_VideoDesc video_desc ={ .SampleWidth = w, .SampleHeight = h, .Format = fmt.format->dxfmt, }; UINT n_configs = 0; DXVA2_ConfigPictureDecode *configs = NULL; hr = IDirectXVideoDecoderService_GetDecoderConfigurations( p->decoder_service, fmt.guid, &video_desc, NULL, &n_configs, &configs); if (FAILED(hr)) { MP_ERR(p, "Unable to retrieve decoder configurations: %s\n", mp_HRESULT_to_str(hr)); goto done; } unsigned max_score = 0; for (UINT i = 0; i < n_configs; i++) { unsigned score = d3d_decoder_config_score( s, &configs[i].guidConfigBitstreamEncryption, configs[i].ConfigBitstreamRaw); if (score > max_score) { max_score = score; *decoder_config = configs[i]; } } CoTaskMemFree(configs); if (!max_score) { MP_ERR(p, "Failed to find a suitable decoder configuration\n"); goto done; } surfaces = talloc_zero_array(decoder_pool, IDirect3DSurface9*, n_surfaces); hr = IDirectXVideoDecoderService_CreateSurface( p->decoder_service, w_align, h_align, n_surfaces - 1, fmt.format->dxfmt, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, surfaces, NULL); if (FAILED(hr)) { MP_ERR(p, "Failed to create %d video surfaces: %s\n", n_surfaces, mp_HRESULT_to_str(hr)); goto done; } hr = IDirectXVideoDecoderService_CreateVideoDecoder( p->decoder_service, fmt.guid, &video_desc, decoder_config, surfaces, n_surfaces, &decoder); if (FAILED(hr)) { MP_ERR(p, "Failed to create DXVA2 video decoder: %s\n", mp_HRESULT_to_str(hr)); goto done; } for (int i = 0; i < n_surfaces; i++) { struct mp_image *img = dxva2_new_ref(decoder, surfaces[i], w, h); if (!img) { MP_ERR(p, "Failed to create DXVA2 image\n"); goto done; } mp_image_pool_add(decoder_pool, img); // transferred to pool } // Pass required information on to ffmpeg. struct dxva_context *dxva_ctx = s->avctx->hwaccel_context; dxva_ctx->cfg = decoder_config; dxva_ctx->decoder = decoder; dxva_ctx->surface_count = n_surfaces; dxva_ctx->surface = surfaces; dxva_ctx->workaround = is_clearvideo(fmt.guid) ? FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO : 0; p->decoder_pool = talloc_steal(NULL, decoder_pool); ret = 0; done: // On success, `p->decoder_pool` mp_images still hold refs to `surfaces` and // `decoder`, so the pointers in the ffmpeg `dxva_context` strcture remain // valid for the lifetime of the pool. if (surfaces) { for (int i = 0; i < n_surfaces; i++) IDirect3DSurface9_Release(surfaces[i]); } if (decoder) IDirectXVideoDecoder_Release(decoder); talloc_free(tmp); return ret; } static void destroy_device(struct lavc_ctx *s) { struct priv *p = s->hwdec_priv; if (p->device) IDirect3DDevice9_Release(p->device); if (p->d3d9) IDirect3D9_Release(p->d3d9); } static bool create_device(struct lavc_ctx *s) { struct priv *p = s->hwdec_priv; d3d_load_dlls(); if (!d3d9_dll) { MP_ERR(p, "Failed to load D3D9 library\n"); return false; } HRESULT (WINAPI *Direct3DCreate9Ex)(UINT, IDirect3D9Ex **) = (void *)GetProcAddress(d3d9_dll, "Direct3DCreate9Ex"); if (!Direct3DCreate9Ex) { MP_ERR(p, "Failed to locate Direct3DCreate9Ex\n"); return false; } IDirect3D9Ex *d3d9ex = NULL; HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9ex); if (FAILED(hr)) { MP_ERR(p, "Failed to create IDirect3D9Ex object\n"); return false; } UINT adapter = D3DADAPTER_DEFAULT; D3DDISPLAYMODEEX modeex = {0}; IDirect3D9Ex_GetAdapterDisplayModeEx(d3d9ex, adapter, &modeex, NULL); D3DPRESENT_PARAMETERS present_params = { .Windowed = TRUE, .BackBufferWidth = 640, .BackBufferHeight = 480, .BackBufferCount = 0, .BackBufferFormat = modeex.Format, .SwapEffect = D3DSWAPEFFECT_DISCARD, .Flags = D3DPRESENTFLAG_VIDEO, }; IDirect3DDevice9Ex *exdev = NULL; hr = IDirect3D9Ex_CreateDeviceEx(d3d9ex, adapter, D3DDEVTYPE_HAL, GetShellWindow(), D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, &present_params, NULL, &exdev); if (FAILED(hr)) { MP_ERR(p, "Failed to create Direct3D device: %s\n", mp_HRESULT_to_str(hr)); IDirect3D9_Release(d3d9ex); return false; } p->d3d9 = (IDirect3D9 *)d3d9ex; p->device = (IDirect3DDevice9 *)exdev; return true; } static void dxva2_uninit(struct lavc_ctx *s) { struct priv *p = s->hwdec_priv; if (!p) return; av_freep(&s->avctx->hwaccel_context); talloc_free(p->decoder_pool); if (p->decoder_service) IDirectXVideoDecoderService_Release(p->decoder_service); if (p->device_manager && p->device_handle != INVALID_HANDLE_VALUE) IDirect3DDeviceManager9_CloseDeviceHandle(p->device_manager, p->device_handle); if (p->device_manager) IDirect3DDeviceManager9_Release(p->device_manager); destroy_device(s); TA_FREEP(&s->hwdec_priv); } static int dxva2_init(struct lavc_ctx *s) { HRESULT hr; struct priv *p = talloc_zero(NULL, struct priv); if (!p) return -1; s->hwdec_priv = p; p->device_handle = INVALID_HANDLE_VALUE; p->log = mp_log_new(s, s->log, "dxva2"); if (s->hwdec->type == HWDEC_DXVA2_COPY) { mp_check_gpu_memcpy(p->log, NULL); p->sw_pool = talloc_steal(p, mp_image_pool_new(17)); } p->device = hwdec_devices_load(s->hwdec_devs, s->hwdec->type); if (p->device) { IDirect3D9_AddRef(p->device); MP_VERBOSE(p, "Using VO-supplied device %p.\n", p->device); } else if (s->hwdec->type == HWDEC_DXVA2) { MP_ERR(p, "No Direct3D device provided for native dxva2 decoding\n"); goto fail; } else { if (!create_device(s)) goto fail; } d3d_load_dlls(); if (!dxva2_dll) { MP_ERR(p, "Failed to load DXVA2 library\n"); goto fail; } HRESULT (WINAPI *CreateDeviceManager9)(UINT *, IDirect3DDeviceManager9 **) = (void *)GetProcAddress(dxva2_dll, "DXVA2CreateDirect3DDeviceManager9"); if (!CreateDeviceManager9) { MP_ERR(p, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n"); goto fail; } unsigned reset_token = 0; hr = CreateDeviceManager9(&reset_token, &p->device_manager); if (FAILED(hr)) { MP_ERR(p, "Failed to create Direct3D device manager: %s\n", mp_HRESULT_to_str(hr)); goto fail; } hr = IDirect3DDeviceManager9_ResetDevice(p->device_manager, p->device, reset_token); if (FAILED(hr)) { MP_ERR(p, "Failed to bind Direct3D device to device manager: %s\n", mp_HRESULT_to_str(hr)); goto fail; } hr = IDirect3DDeviceManager9_OpenDeviceHandle(p->device_manager, &p->device_handle); if (FAILED(hr)) { MP_ERR(p, "Failed to open device handle: %s\n", mp_HRESULT_to_str(hr)); goto fail; } hr = IDirect3DDeviceManager9_GetVideoService( p->device_manager, p->device_handle, &IID_IDirectXVideoDecoderService, (void **)&p->decoder_service); if (FAILED(hr)) { MP_ERR(p, "Failed to create IDirectXVideoDecoderService: %s\n", mp_HRESULT_to_str(hr)); goto fail; } s->avctx->hwaccel_context = av_mallocz(sizeof(struct dxva_context)); if (!s->avctx->hwaccel_context) goto fail; return 0; fail: dxva2_uninit(s); return -1; } static int dxva2_probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, const char *codec) { // dxva2-copy can do without external context; dxva2 requires it. if (hwdec->type == HWDEC_DXVA2) { if (!hwdec_devices_load(ctx->hwdec_devs, HWDEC_DXVA2)) return HWDEC_ERR_NO_CTX; } else { hwdec_devices_load(ctx->hwdec_devs, HWDEC_DXVA2_COPY); } return d3d_probe_codec(codec); } const struct vd_lavc_hwdec mp_vd_lavc_dxva2 = { .type = HWDEC_DXVA2, .image_format = IMGFMT_DXVA2, .probe = dxva2_probe, .init = dxva2_init, .uninit = dxva2_uninit, .init_decoder = dxva2_init_decoder, .allocate_image = dxva2_allocate_image, }; const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy = { .type = HWDEC_DXVA2_COPY, .copying = true, .image_format = IMGFMT_DXVA2, .probe = dxva2_probe, .init = dxva2_init, .uninit = dxva2_uninit, .init_decoder = dxva2_init_decoder, .allocate_image = dxva2_allocate_image, .process_image = dxva2_retrieve_image, .delay_queue = HWDEC_DELAY_QUEUE_COUNT, };
/** * It creates a DXVA2 decoder using the given video format */ static int hb_dx_create_video_decoder( hb_va_dxva2_t *dxva2, int codec_id, const hb_title_t* fmt ) { dxva2->width = fmt->width; dxva2->height = fmt->height; dxva2->surface_width = (fmt->width + 15) & ~15; dxva2->surface_height = (fmt->height + 15) & ~15; switch( codec_id ) { case AV_CODEC_ID_H264: dxva2->surface_count = 16 + 1; break; default: dxva2->surface_count = 2 + 1; break; } LPDIRECT3DSURFACE9 surface_list[VA_DXVA2_MAX_SURFACE_COUNT]; if( FAILED( IDirectXVideoDecoderService_CreateSurface( dxva2->vs, dxva2->surface_width, dxva2->surface_height, dxva2->surface_count - 1, dxva2->render, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, surface_list, NULL ))) { hb_log( "dxva2:IDirectXVideoAccelerationService_CreateSurface failed" ); dxva2->surface_count = 0; return HB_WORK_ERROR; } unsigned i; for( i = 0; i<dxva2->surface_count; i++ ) { hb_va_surface_t *surface = &dxva2->surface[i]; surface->d3d = surface_list[i]; surface->refcount = 0; surface->order = 0; } hb_log( "dxva2:CreateSurface succeed with %d, fmt (%dx%d) surfaces (%dx%d)", dxva2->surface_count, fmt->width, fmt->height, dxva2->surface_width, dxva2->surface_height ); DXVA2_VideoDesc dsc; memset( &dsc, 0, sizeof(dsc)); dsc.SampleWidth = fmt->width; dsc.SampleHeight = fmt->height; dsc.Format = dxva2->render; if( fmt->rate> 0 && fmt->rate_base> 0 ) { dsc.InputSampleFreq.Numerator = fmt->rate; dsc.InputSampleFreq.Denominator = fmt->rate_base; } else { dsc.InputSampleFreq.Numerator = 0; dsc.InputSampleFreq.Denominator = 0; } dsc.OutputFrameFreq = dsc.InputSampleFreq; dsc.UABProtectionLevel = FALSE; dsc.Reserved = 0; /* FIXME I am unsure we can let unknown everywhere */ DXVA2_ExtendedFormat *ext = &dsc.SampleFormat; ext->SampleFormat = 0; //DXVA2_SampleUnknown; ext->VideoChromaSubsampling = 0; //DXVA2_VideoChromaSubsampling_Unknown; ext->NominalRange = 0; //DXVA2_NominalRange_Unknown; ext->VideoTransferMatrix = 0; //DXVA2_VideoTransferMatrix_Unknown; ext->VideoLighting = 0; //DXVA2_VideoLighting_Unknown; ext->VideoPrimaries = 0; //DXVA2_VideoPrimaries_Unknown; ext->VideoTransferFunction = 0; //DXVA2_VideoTransFunc_Unknown; /* List all configurations available for the decoder */ UINT cfg_count = 0; DXVA2_ConfigPictureDecode *cfg_list = NULL; if( FAILED( IDirectXVideoDecoderService_GetDecoderConfigurations( dxva2->vs, &dxva2->input, &dsc, NULL, &cfg_count, &cfg_list ))) { hb_log( "dxva2:IDirectXVideoDecoderService_GetDecoderConfigurations failed" ); return HB_WORK_ERROR; } hb_log( "dxva2:we got %d decoder configurations", cfg_count ); /* Select the best decoder configuration */ int cfg_score = 0; for( i = 0; i < cfg_count; i++ ) { const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; hb_log( "dxva2:configuration[%d] ConfigBitstreamRaw %d", i, cfg->ConfigBitstreamRaw ); int score; if( cfg->ConfigBitstreamRaw == 1 ) score = 1; else if( codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2 ) score = 2; else continue; if( IsEqualGUID( &cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt )) score += 16; if( cfg_score < score ) { dxva2->cfg = *cfg; cfg_score = score; } } //my_release(cfg_list); if( cfg_score <= 0 ) { hb_log( "dxva2:Failed to find a supported decoder configuration" ); return HB_WORK_ERROR; } /* Create the decoder */ IDirectXVideoDecoder *decoder; if( FAILED( IDirectXVideoDecoderService_CreateVideoDecoder( dxva2->vs, &dxva2->input, &dsc, &dxva2->cfg, surface_list, dxva2->surface_count, &decoder ))) { hb_log( "dxva2:IDirectXVideoDecoderService_CreateVideoDecoder failed" ); return HB_WORK_ERROR; } dxva2->decoder = decoder; hb_log( "dxva2:IDirectXVideoDecoderService_CreateVideoDecoder succeed" ); return HB_WORK_OK; }
static int dxva2_create_decoder(AVCodecContext *s) { InputStream *ist = s->opaque; int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; DXVA2Context *ctx = ist->hwaccel_ctx; struct dxva_context *dxva_ctx = s->hwaccel_context; GUID *guid_list = NULL; unsigned guid_count = 0, i, j; GUID device_guid = GUID_NULL; const D3DFORMAT surface_format = s->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? MKTAG('P', '0', '1', '0') : MKTAG('N', 'V', '1', '2'); D3DFORMAT target_format = 0; DXVA2_VideoDesc desc = { 0 }; DXVA2_ConfigPictureDecode config; HRESULT hr; int surface_alignment, num_surfaces; int ret; AVDXVA2FramesContext *frames_hwctx; AVHWFramesContext *frames_ctx; hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n"); goto fail; } for (i = 0; dxva2_modes[i].guid; i++) { D3DFORMAT *target_list = NULL; unsigned target_count = 0; const dxva2_mode *mode = &dxva2_modes[i]; if (mode->codec != s->codec_id) continue; for (j = 0; j < guid_count; j++) { if (IsEqualGUID(mode->guid, &guid_list[j])) break; } if (j == guid_count) continue; hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); if (FAILED(hr)) { continue; } for (j = 0; j < target_count; j++) { const D3DFORMAT format = target_list[j]; if (format == surface_format) { target_format = format; break; } } CoTaskMemFree(target_list); if (target_format) { device_guid = *mode->guid; break; } } CoTaskMemFree(guid_list); if (IsEqualGUID(&device_guid, &GUID_NULL)) { av_log(NULL, loglevel, "No decoder device for codec found\n"); goto fail; } desc.SampleWidth = s->coded_width; desc.SampleHeight = s->coded_height; desc.Format = target_format; ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config); if (ret < 0) { goto fail; } /* decoding MPEG-2 requires additional alignment on some Intel GPUs, but it causes issues for H.264 on certain AMD GPUs..... */ if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) surface_alignment = 32; /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure all coding features have enough room to work with */ else if (s->codec_id == AV_CODEC_ID_HEVC) surface_alignment = 128; else surface_alignment = 16; /* 4 base work surfaces */ num_surfaces = 4; /* add surfaces based on number of possible refs */ if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC) num_surfaces += 16; else num_surfaces += 2; /* add extra surfaces for frame threading */ if (s->active_thread_type & FF_THREAD_FRAME) num_surfaces += s->thread_count; ctx->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->hw_device_ctx); if (!ctx->hw_frames_ctx) goto fail; frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; frames_hwctx = frames_ctx->hwctx; frames_ctx->format = AV_PIX_FMT_DXVA2_VLD; frames_ctx->sw_format = s->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12; frames_ctx->width = FFALIGN(s->coded_width, surface_alignment); frames_ctx->height = FFALIGN(s->coded_height, surface_alignment); frames_ctx->initial_pool_size = num_surfaces; frames_hwctx->surface_type = DXVA2_VideoDecoderRenderTarget; ret = av_hwframe_ctx_init(ctx->hw_frames_ctx); if (ret < 0) { av_log(NULL, loglevel, "Failed to initialize the HW frames context\n"); goto fail; } hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, &desc, &config, frames_hwctx->surfaces, frames_hwctx->nb_surfaces, &frames_hwctx->decoder_to_release); if (FAILED(hr)) { av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n"); goto fail; } ctx->decoder_guid = device_guid; ctx->decoder_config = config; dxva_ctx->cfg = &ctx->decoder_config; dxva_ctx->decoder = frames_hwctx->decoder_to_release; dxva_ctx->surface = frames_hwctx->surfaces; dxva_ctx->surface_count = frames_hwctx->nb_surfaces; if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E)) dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; return 0; fail: av_buffer_unref(&ctx->hw_frames_ctx); return AVERROR(EINVAL); }