int check_dxva_codec_profile(AVCodecID codec, AVPixelFormat pix_fmt, int profile, int hwpixfmt) { // check mpeg2 pixfmt if (codec == AV_CODEC_ID_MPEG2VIDEO && pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUVJ420P && pix_fmt != hwpixfmt && pix_fmt != AV_PIX_FMT_NONE) return 1; // check h264 pixfmt if (codec == AV_CODEC_ID_H264 && pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUVJ420P && pix_fmt != hwpixfmt && pix_fmt != AV_PIX_FMT_NONE) return 1; // check h264 profile if (codec == AV_CODEC_ID_H264 && profile != FF_PROFILE_UNKNOWN && !H264_CHECK_PROFILE(profile)) return 1; // check wmv/vc1 profile if ((codec == AV_CODEC_ID_WMV3 || codec == AV_CODEC_ID_VC1) && profile == FF_PROFILE_VC1_COMPLEX) return 1; // check hevc profile/pixfmt if (codec == AV_CODEC_ID_HEVC && (!HEVC_CHECK_PROFILE(profile) || (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUVJ420P && pix_fmt != AV_PIX_FMT_YUV420P10 && pix_fmt != hwpixfmt && pix_fmt != AV_PIX_FMT_NONE))) return 1; // check vp9 profile/pixfmt if (codec == AV_CODEC_ID_VP9 && (!VP9_CHECK_PROFILE(profile) || (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUV420P10 && pix_fmt != AV_PIX_FMT_DXVA2_VLD && pix_fmt != AV_PIX_FMT_NONE))) return 1; return 0; }
STDMETHODIMP CDecDXVA2::InitDecoder(AVCodecID codec, const CMediaType *pmt) { HRESULT hr = S_OK; DbgLog((LOG_TRACE, 10, L"CDecDXVA2::InitDecoder(): Initializing DXVA2 decoder")); // Hack-ish check to avoid re-creating the full decoder when only the aspect ratio changes. // Re-creating the DXVA2 decoder can lead to issues like missing frames or a several second delay if (m_pDecoder) { CMediaType mediaTypeCheck = m_MediaType; if (mediaTypeCheck.formattype == FORMAT_VideoInfo2 && pmt->formattype == FORMAT_VideoInfo2) { VIDEOINFOHEADER2 *vih2Old = (VIDEOINFOHEADER2 *)mediaTypeCheck.Format(); VIDEOINFOHEADER2 *vih2New = (VIDEOINFOHEADER2 *)pmt->Format(); vih2Old->dwPictAspectRatioX = vih2New->dwPictAspectRatioX; vih2Old->dwPictAspectRatioY = vih2New->dwPictAspectRatioY; if (mediaTypeCheck == *pmt) { DbgLog((LOG_TRACE, 10, L"-> Skipping re-init because media type is unchanged.")); m_MediaType = *pmt; return S_OK; } } } DestroyDecoder(false); m_DisplayDelay = DXVA2_QUEUE_SURFACES; // Intel GPUs don't like the display and performance goes way down, so disable it. if (m_dwVendorId == VEND_ID_INTEL) m_DisplayDelay = 0; // Reduce display delay for DVD decoding for lower decode latency if (m_pCallback->GetDecodeFlags() & LAV_VIDEO_DEC_FLAG_DVD) m_DisplayDelay /= 2; // If we have a DXVA Decoder, check if its capable // If we don't have one yet, it may be handed to us later, and compat is checked at that point GUID input = GUID_NULL; if (m_pDXVADecoderService) { D3DFORMAT output; hr = FindVideoServiceConversion(codec, &input, &output); if (FAILED(hr)) { DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(codec))); return E_FAIL; } } m_bFailHWDecode = FALSE; DbgLog((LOG_TRACE, 10, L"-> Creation of DXVA2 decoder successfull, initializing ffmpeg")); hr = CDecAvcodec::InitDecoder(codec, pmt); if (FAILED(hr)) { return hr; } if (((codec == AV_CODEC_ID_H264 || codec == AV_CODEC_ID_MPEG2VIDEO || codec == AV_CODEC_ID_HEVC) && m_pAVCtx->pix_fmt != AV_PIX_FMT_YUV420P && m_pAVCtx->pix_fmt != AV_PIX_FMT_YUVJ420P && m_pAVCtx->pix_fmt != AV_PIX_FMT_DXVA2_VLD && m_pAVCtx->pix_fmt != AV_PIX_FMT_NONE) || (codec == AV_CODEC_ID_H264 && m_pAVCtx->profile != FF_PROFILE_UNKNOWN && !H264_CHECK_PROFILE(m_pAVCtx->profile)) || ((codec == AV_CODEC_ID_WMV3 || codec == AV_CODEC_ID_VC1) && m_pAVCtx->profile == FF_PROFILE_VC1_COMPLEX) || (codec == AV_CODEC_ID_HEVC && !HEVC_CHECK_PROFILE(m_pAVCtx->profile))) { DbgLog((LOG_TRACE, 10, L"-> Incompatible profile detected, falling back to software decoding")); return E_FAIL; } m_dwSurfaceWidth = GetAlignedDimension(m_pAVCtx->coded_width); m_dwSurfaceHeight = GetAlignedDimension(m_pAVCtx->coded_height); if (FAILED(CheckHWCompatConditions(input))) { return E_FAIL; } m_MediaType = *pmt; return S_OK; }
int CDecDXVA2::get_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic, int flags) { CDecDXVA2 *pDec = (CDecDXVA2 *)c->opaque; IMediaSample *pSample = nullptr; HRESULT hr = S_OK; if (pic->format != AV_PIX_FMT_DXVA2_VLD || (c->codec_id == AV_CODEC_ID_H264 && !H264_CHECK_PROFILE(c->profile)) || (c->codec_id == AV_CODEC_ID_HEVC && !HEVC_CHECK_PROFILE(c->profile))) { DbgLog((LOG_ERROR, 10, L"DXVA2 buffer request, but not dxva2 pixfmt or unsupported profile")); pDec->m_bFailHWDecode = TRUE; return -1; } hr = pDec->ReInitDXVA2Decoder(c); if (FAILED(hr)) { pDec->m_bFailHWDecode = TRUE; return -1; } if (FAILED(pDec->m_pD3DDevMngr->TestDevice(pDec->m_hDevice))) { DbgLog((LOG_ERROR, 10, L"Device Lost")); } int i; if (pDec->m_bNative) { if (!pDec->m_pDXVA2Allocator) return -1; hr = pDec->m_pDXVA2Allocator->GetBuffer(&pSample, nullptr, nullptr, 0); if (FAILED(hr)) { DbgLog((LOG_ERROR, 10, L"DXVA2Allocator returned error, hr: 0x%x", hr)); return -1; } ILAVDXVA2Sample *pLavDXVA2 = nullptr; hr = pSample->QueryInterface(&pLavDXVA2); if (FAILED(hr)) { DbgLog((LOG_ERROR, 10, L"Sample is no LAV DXVA2 sample?????")); SafeRelease(&pSample); return -1; } i = pLavDXVA2->GetDXSurfaceId(); SafeRelease(&pLavDXVA2); } else { int old, old_unused; for (i = 0, old = 0, old_unused = -1; i < pDec->m_NumSurfaces; i++) { d3d_surface_t *surface = &pDec->m_pSurfaces[i]; if (!surface->used && (old_unused == -1 || surface->age < pDec->m_pSurfaces[old_unused].age)) old_unused = i; if (surface->age < pDec->m_pSurfaces[old].age) old = i; } if (old_unused == -1) { DbgLog((LOG_TRACE, 10, L"No free surface, using oldest")); i = old; } else { i = old_unused; } } LPDIRECT3DSURFACE9 pSurface = pDec->m_pSurfaces[i].d3d; if (!pSurface) { DbgLog((LOG_ERROR, 10, L"There is a sample, but no D3D Surace? WTF?")); SafeRelease(&pSample); return -1; } pDec->m_pSurfaces[i].age = pDec->m_CurrentSurfaceAge++; pDec->m_pSurfaces[i].used = true; memset(pic->data, 0, sizeof(pic->data)); memset(pic->linesize, 0, sizeof(pic->linesize)); memset(pic->buf, 0, sizeof(pic->buf)); pic->data[0] = pic->data[3] = (uint8_t *)pSurface; pic->data[4] = (uint8_t *)pSample; SurfaceWrapper *surfaceWrapper = new SurfaceWrapper(); surfaceWrapper->pDec = pDec; surfaceWrapper->sample = pSample; surfaceWrapper->surface = pSurface; surfaceWrapper->surface->AddRef(); surfaceWrapper->pDXDecoder = pDec->m_pDecoder; surfaceWrapper->pDXDecoder->AddRef(); pic->buf[0] = av_buffer_create(nullptr, 0, free_dxva2_buffer, surfaceWrapper, 0); return 0; }