diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 07b2beeb76..849d4e2130 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -30,9 +30,11 @@ #ifdef MOZ_FMP4 #include "MP4Decoder.h" #endif -#include "WMFDecoderModule.h" +#if defined(XP_WIN) && defined(MOZ_WMF) #include "MediaPrefs.h" +#include "WMFDecoderModule.h" #include "mozilla/gfx/gfxVars.h" +#endif #include "CubebUtils.h" #include "nsIScrollableFrame.h" @@ -2379,17 +2381,26 @@ nsDOMWindowUtils::GetSupportsHardwareVP9Decoding(JS::MutableHandle aP } #if defined(XP_WIN) && defined(MOZ_WMF) + nsCOMPtr widget = GetWidget(); + LayerManager* layerManager = widget ? widget->GetLayerManager() : nullptr; if (!MediaPrefs::PDMWMFVP9DecoderEnabled()) { - promise->MaybeResolve(NS_LITERAL_STRING("No; media.wmf.vp9.enabled is false")); + promise->MaybeResolve(NS_LITERAL_STRING( + "No; media.wmf.vp9.enabled is false")); } else if (!gfx::gfxVars::CanUseHardwareVideoDecoding()) { - promise->MaybeResolve(NS_LITERAL_STRING("No; hardware video decoding disabled")); - } else if (WMFDecoderModule::HasVP9()) { - promise->MaybeResolve(NS_LITERAL_STRING("Yes")); + promise->MaybeResolve(NS_LITERAL_STRING( + "No; hardware video decoding disabled")); + } else if (WMFDecoderModule::HasVP9MFT()) { + promise->MaybeResolve(NS_LITERAL_STRING("Yes; using MFT")); + } else if (layerManager && WMFDecoderModule::HasVP9DXVA2( + layerManager->AsShadowForwarder())) { + promise->MaybeResolve(NS_LITERAL_STRING("Yes; using DXVA2")); } else { - promise->MaybeResolve(NS_LITERAL_STRING("No; MFT unavailable")); + promise->MaybeResolve(NS_LITERAL_STRING( + "No; no VP9 hardware decoder available")); } #else - promise->MaybeResolve(NS_LITERAL_STRING("No; not supported on this platform")); + promise->MaybeResolve(NS_LITERAL_STRING( + "No; not supported on this platform")); #endif aPromise.setObject(*promise->PromiseObj()); diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index baf886f347..e55f07a278 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -49,7 +49,7 @@ interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; interface nsIObserver; -[scriptable, uuid(58e97ce9-1d9e-4576-aabf-89480fdeb16d)] +[scriptable, uuid(16ed093c-84a7-4ca3-84c8-9ad71f8b5d82)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1419,11 +1419,10 @@ interface nsIDOMWindowUtils : nsISupports { * in hardware. */ readonly attribute jsval supportsHardwareH264Decoding; + /** - * Returns a Promise that will be resolved with a string once the capabilities - * of the vp9 decoder have been determined. - * Success does not mean that all vp9 video decoding will be done - * in hardware. + * Returns a Promise resolved with a string describing whether VP9 hardware + * decoding is available through the Windows MFT or direct DXVA2 path. */ readonly attribute jsval supportsHardwareVP9Decoding; diff --git a/dom/media/MediaPrefs.h b/dom/media/MediaPrefs.h index 8c0bee04c6..300db8a2cd 100644 --- a/dom/media/MediaPrefs.h +++ b/dom/media/MediaPrefs.h @@ -118,6 +118,9 @@ private: DECL_MEDIA_PREF("media.wmf.enabled", PDMWMFEnabled, bool, true); DECL_MEDIA_PREF("media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false); DECL_MEDIA_PREF("media.decoder-doctor.wmf-disabled-is-failure", DecoderDoctorWMFDisabledIsFailure, bool, false); + DECL_MEDIA_PREF("media.wmf.vp9.dxva2.enabled", PDMWMFVP9DXVA2Enabled, bool, true); + DECL_MEDIA_PREF("media.wmf.vp9.mft.enabled", PDMWMFVP9MFTEnabled, bool, true); + DECL_MEDIA_PREF("media.wmf.vp9.dxva2.preferred", PDMWMFVP9DXVA2Preferred, bool, false); DECL_MEDIA_PREF("media.wmf.vp9.enabled", PDMWMFVP9DecoderEnabled, bool, true); DECL_MEDIA_PREF("media.wmf.decoder.thread-count", PDMWMFThreadCount, int32_t, -1); #endif diff --git a/dom/media/platforms/wmf/DXVA2Manager.cpp b/dom/media/platforms/wmf/DXVA2Manager.cpp index 69de2be84f..da9e65381c 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.cpp +++ b/dom/media/platforms/wmf/DXVA2Manager.cpp @@ -89,7 +89,7 @@ public: virtual ~D3D9DXVA2Manager(); HRESULT Init(layers::KnowsCompositor* aKnowsCompositor, - nsACString& aFailureReason); + nsACString& aFailureReason, const GUID* aDecoderGUID); IUnknown* GetDXVADeviceManager() override; @@ -99,6 +99,8 @@ public: const nsIntRect& aRegion, Image** aOutImage) override; + HRESULT CopySurfaceToImage(IDirect3DSurface9* aSurface, + const nsIntRect& aRegion, Image** aOutImage) override; bool SupportsConfig(IMFMediaType* aType, float aFramerate) override; private: @@ -185,10 +187,6 @@ static const GUID DXVA2_Intel_ModeH264_E = { 0x604F8E68, 0x4951, 0x4c54, { 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6 } }; -static const GUID DXVA2_ModeVP9_VLD_Profile0 = { - 0x463707f8, 0xa1d0, 0x4585, { 0x87, 0x6d, 0x83, 0xaa, 0x6d, 0x60, 0xb8, 0x9e } -}; - // This tests if a DXVA video decoder can be created for the given media type/resolution. // It uses the same decoder device (DXVA2_ModeH264_E - DXVA2_ModeH264_VLD_NoFGT) as the H264 // decoder MFT provided by windows (CLSID_CMSH264DecoderMFT) uses, so we can use it to determine @@ -267,7 +265,7 @@ D3D9DXVA2Manager::GetDXVADeviceManager() HRESULT D3D9DXVA2Manager::Init(layers::KnowsCompositor* aKnowsCompositor, - nsACString& aFailureReason) + nsACString& aFailureReason, const GUID* aDecoderGUID) { MOZ_ASSERT(NS_IsMainThread()); @@ -388,11 +386,12 @@ D3D9DXVA2Manager::Init(layers::KnowsCompositor* aKnowsCompositor, bool found = false; for (UINT i = 0; i < deviceCount; i++) { - if (decoderDevices[i] == DXVA2_ModeH264_E || - decoderDevices[i] == DXVA2_Intel_ModeH264_E || - decoderDevices[i] == DXVA2_ModeVP9_VLD_Profile0) { + if (aDecoderGUID ? decoderDevices[i] == *aDecoderGUID : + (decoderDevices[i] == DXVA2_ModeH264_E || + decoderDevices[i] == DXVA2_Intel_ModeH264_E)) { mDecoderGUID = decoderDevices[i]; found = true; + break; } } CoTaskMemFree(decoderDevices); @@ -462,8 +461,15 @@ D3D9DXVA2Manager::CopyToImage(IMFSample* aSample, getter_AddRefs(surface)); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + return CopySurfaceToImage(surface, aRegion, aOutImage); +} + +HRESULT +D3D9DXVA2Manager::CopySurfaceToImage(IDirect3DSurface9* surface, + const nsIntRect& aRegion, Image** aOutImage) +{ RefPtr image = new D3D9SurfaceImage(); - hr = image->AllocateAndCopy(mTextureClientAllocator, surface, aRegion); + HRESULT hr = image->AllocateAndCopy(mTextureClientAllocator, surface, aRegion); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); RefPtr sourceSurf = image->GetD3D9Surface(); @@ -492,7 +498,7 @@ static uint32_t sDXVAVideosCount = 0; /* static */ DXVA2Manager* DXVA2Manager::CreateD3D9DXVA(layers::KnowsCompositor* aKnowsCompositor, - nsACString& aFailureReason) + nsACString& aFailureReason, const GUID* aDecoderGUID) { MOZ_ASSERT(NS_IsMainThread()); HRESULT hr; @@ -507,7 +513,7 @@ DXVA2Manager::CreateD3D9DXVA(layers::KnowsCompositor* aKnowsCompositor, } nsAutoPtr d3d9Manager(new D3D9DXVA2Manager()); - hr = d3d9Manager->Init(aKnowsCompositor, aFailureReason); + hr = d3d9Manager->Init(aKnowsCompositor, aFailureReason, aDecoderGUID); if (SUCCEEDED(hr)) { return d3d9Manager.forget(); } @@ -694,11 +700,10 @@ D3D11DXVA2Manager::Init(layers::KnowsCompositor* aKnowsCompositor, for (UINT i = 0; i < profileCount; i++) { GUID id; hr = videoDevice->GetVideoDecoderProfile(i, &id); - if (SUCCEEDED(hr)) { - if (id == DXVA2_ModeH264_E || id == DXVA2_Intel_ModeH264_E || id == DXVA2_ModeVP9_VLD_Profile0) { - mDecoderGUID = id; - found = true; - } + if (SUCCEEDED(hr) && (id == DXVA2_ModeH264_E || id == DXVA2_Intel_ModeH264_E)) { + mDecoderGUID = id; + found = true; + break; } } if (!found) { diff --git a/dom/media/platforms/wmf/DXVA2Manager.h b/dom/media/platforms/wmf/DXVA2Manager.h index c03a3a2d60..da35f2e0f2 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.h +++ b/dom/media/platforms/wmf/DXVA2Manager.h @@ -23,7 +23,7 @@ public: // Creates and initializes a DXVA2Manager. We can use DXVA2 via either // D3D9Ex or D3D11. - static DXVA2Manager* CreateD3D9DXVA(layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason); + static DXVA2Manager* CreateD3D9DXVA(layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason, const GUID* aDecoderGUID = nullptr); static DXVA2Manager* CreateD3D11DXVA(layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason); // Returns a pointer to the D3D device manager responsible for managing the @@ -37,6 +37,10 @@ public: const nsIntRect& aRegion, layers::Image** aOutImage) = 0; + virtual HRESULT CopySurfaceToImage(IDirect3DSurface9* aSurface, + const nsIntRect& aRegion, + layers::Image** aOutImage) { return E_NOTIMPL; } + virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) { return S_OK; } virtual bool IsD3D11() { return false; } diff --git a/dom/media/platforms/wmf/DXVAVP9Manager.cpp b/dom/media/platforms/wmf/DXVAVP9Manager.cpp new file mode 100644 index 0000000000..9061861f85 --- /dev/null +++ b/dom/media/platforms/wmf/DXVAVP9Manager.cpp @@ -0,0 +1,590 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DXVAVP9Manager.h" +#include "WMFUtils.h" +#include "nsPrintfCString.h" +#include "nsReadableUtils.h" +#include +#include + +// VP9 DXVA2 GUID: {463707F8-A1D0-4585-876D-83AA6D60B89E} +// its the VP9_VLD_Profile0. +static const GUID sVP9DecoderGUID = { + 0x463707f8, 0xa1d0, 0x4585, + { 0x87, 0x6d, 0x83, 0xaa, 0x6d, 0x60, 0xb8, 0x9e } +}; + +namespace mozilla { + +DXVAVP9Manager::DXVAVP9Manager() + : mNumSurfaces(kDefaultSurfaces) + , mCurrentSurface(kDefaultSurfaces - 1) + , mWidth(0) + , mHeight(0) + , mFrameCount(0) + , mLock("DXVAVP9Manager") +{ + memset(mRefFrameSurface, 0xFF, sizeof(mRefFrameSurface)); + memset(mRefFrameWidth, 0, sizeof(mRefFrameWidth)); + memset(mRefFrameHeight, 0, sizeof(mRefFrameHeight)); +} + +DXVAVP9Manager::~DXVAVP9Manager() +{ + Shutdown(); +} + +/* static */ const GUID& +DXVAVP9Manager::GetVP9DecoderGUID() +{ + return sVP9DecoderGUID; +} + +/* static */ bool +DXVAVP9Manager::SupportsVP9(IDirect3DDeviceManager9* aDeviceManager) +{ + if (!aDeviceManager) { + return false; + } + + HANDLE handle = nullptr; + HRESULT hr = aDeviceManager->OpenDeviceHandle(&handle); + if (FAILED(hr)) { + return false; + } + + RefPtr decoderService; + hr = aDeviceManager->GetVideoService( + handle, IID_PPV_ARGS(decoderService.StartAssignment())); + aDeviceManager->CloseDeviceHandle(handle); + if (FAILED(hr)) { + return false; + } + + UINT count = 0; + GUID* guids = nullptr; + hr = decoderService->GetDecoderDeviceGuids(&count, &guids); + if (FAILED(hr)) { + return false; + } + + bool found = false; + for (UINT i = 0; i < count; i++) { + if (guids[i] == sVP9DecoderGUID) { + found = true; + break; + } + } + CoTaskMemFree(guids); + return found; +} + +bool +DXVAVP9Manager::Init(IUnknown* aDeviceManager, uint32_t aWidth, + uint32_t aHeight, nsACString& aFailureReason) +{ + MutexAutoLock lock(mLock); + + if (!aDeviceManager) { aFailureReason.AssignLiteral("Missing D3D9 device manager"); return false; } + + HRESULT hr = aDeviceManager->QueryInterface( + __uuidof(IDirect3DDeviceManager9), + reinterpret_cast(mDeviceManager.StartAssignment())); + if (FAILED(hr)) { + aFailureReason.AssignLiteral( + "DXVAVP9: QueryInterface to IDirect3DDeviceManager9 failed — " + "D3D11 path is not supported"); + return false; + } + + HANDLE handle = nullptr; + hr = mDeviceManager->OpenDeviceHandle(&handle); + if (FAILED(hr)) { + aFailureReason = nsPrintfCString( + "DXVAVP9: OpenDeviceHandle failed %X", hr); + return false; + } + + hr = mDeviceManager->GetVideoService( + handle, IID_PPV_ARGS(mDecoderService.StartAssignment())); + mDeviceManager->CloseDeviceHandle(handle); + if (FAILED(hr)) { + aFailureReason = nsPrintfCString( + "DXVAVP9: GetVideoService failed %X", hr); + return false; + } + + if (!SupportsVP9(mDeviceManager)) { + aFailureReason.AssignLiteral( + "DXVAVP9: VP9 DXVA2 GUID not exposed by driver — " + "GPU/driver does not support hardware VP9 decode"); + return false; + } + + mWidth = aWidth ? aWidth : 1280; + mHeight = aHeight ? aHeight : 720; + + hr = InitDecoder(mWidth, mHeight); + if (FAILED(hr)) { + aFailureReason = nsPrintfCString( + "DXVAVP9: InitDecoder(%ux%u) failed %X", mWidth, mHeight, hr); + return false; + } + + return true; +} + +void +DXVAVP9Manager::Flush() +{ + MutexAutoLock lock(mLock); + mParser.Reset(); + memset(mRefFrameSurface, 0xff, sizeof(mRefFrameSurface)); + memset(mRefFrameWidth, 0, sizeof(mRefFrameWidth)); + memset(mRefFrameHeight, 0, sizeof(mRefFrameHeight)); +} + +void +DXVAVP9Manager::Shutdown() +{ + MutexAutoLock lock(mLock); + FreeSurfaces(); + mParser.Reset(); + mDecoderService = nullptr; + mDeviceManager = nullptr; +} + +HRESULT +DXVAVP9Manager::AllocateSurfaces(uint32_t aWidth, uint32_t aHeight, + uint32_t aCount) +{ + FreeSurfaces(); + + if (aCount < kMinSurfaces || aCount > kMaxSurfaces) { return E_INVALIDARG; } + + const uint32_t alignedWidth = (aWidth + 15) & ~15u; + const uint32_t alignedHeight = (aHeight + 15) & ~15u; + + IDirect3DSurface9* surfaces[kMaxSurfaces] = {}; + HRESULT hr = mDecoderService->CreateSurface( + alignedWidth, + alignedHeight, + aCount - 1, + (D3DFORMAT)MAKEFOURCC('N','V','1','2'), + D3DPOOL_DEFAULT, + 0, + DXVA2_VideoDecoderRenderTarget, + surfaces, + nullptr); + if (FAILED(hr)) { + return hr; + } + + for (uint32_t i = 0; i < aCount; i++) { + mSurfaces[i] = dont_AddRef(surfaces[i]); + } + mNumSurfaces = aCount; + mCurrentSurface = aCount - 1; + return S_OK; +} + +void +DXVAVP9Manager::FreeSurfaces() +{ + mDecoder = nullptr; + for (uint32_t i = 0; i < kMaxSurfaces; i++) { + mSurfaces[i] = nullptr; + } + memset(mRefFrameSurface, 0xFF, sizeof(mRefFrameSurface)); + memset(mRefFrameWidth, 0, sizeof(mRefFrameWidth)); + memset(mRefFrameHeight, 0, sizeof(mRefFrameHeight)); + mNumSurfaces = kDefaultSurfaces; + mCurrentSurface = kDefaultSurfaces - 1; +} + +HRESULT +DXVAVP9Manager::InitDecoder(uint32_t aWidth, uint32_t aHeight) +{ + if (!aWidth || !aHeight || aWidth > 8192 || aHeight > 8192) { return E_INVALIDARG; } + UINT formatCount = 0; + D3DFORMAT* formats = nullptr; + HRESULT hr = mDecoderService->GetDecoderRenderTargets(sVP9DecoderGUID, &formatCount, &formats); + if (FAILED(hr)) { return hr; } + bool nv12 = false; + for (UINT i = 0; i < formatCount; ++i) { + nv12 |= formats[i] == (D3DFORMAT)MAKEFOURCC('N','V','1','2'); + } + CoTaskMemFree(formats); + if (!nv12) { return E_FAIL; } + + DXVA2_VideoDesc desc = {}; + desc.SampleWidth = aWidth; + desc.SampleHeight = aHeight; + desc.Format = (D3DFORMAT)MAKEFOURCC('N','V','1','2'); + desc.InputSampleFreq.Numerator = 30; + desc.InputSampleFreq.Denominator = 1; + desc.OutputFrameFreq = desc.InputSampleFreq; + + UINT configCount = 0; + DXVA2_ConfigPictureDecode* configs = nullptr; + hr = mDecoderService->GetDecoderConfigurations( + sVP9DecoderGUID, &desc, nullptr, &configCount, &configs); + if (FAILED(hr)) { + return hr; + } + + if (configCount == 0) { + CoTaskMemFree(configs); + return E_FAIL; + } + + static const GUID noEncrypt = {0x1b81bed0, 0xa0c7, 0x11d3, + {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5}}; + DXVA2_ConfigPictureDecode chosenConfig = {}; + bool found = false; + UINT rejectedForBuffCount = 0; + UINT rejectedForRaw = 0; + UINT rejectedForEncrypt = 0; + for (UINT i = 0; i < configCount; ++i) { + if (configs[i].ConfigBitstreamRaw != 1 && + configs[i].ConfigBitstreamRaw != 2) { + rejectedForRaw = configs[i].ConfigBitstreamRaw; + continue; + } + if (configs[i].guidConfigBitstreamEncryption != noEncrypt && + configs[i].guidConfigBitstreamEncryption != GUID_NULL) { + rejectedForEncrypt++; + continue; + } + if (configs[i].ConfigMinRenderTargetBuffCount > kMaxSurfaces) { + rejectedForBuffCount = configs[i].ConfigMinRenderTargetBuffCount; + continue; + } + chosenConfig = configs[i]; found = true; break; + } + CoTaskMemFree(configs); + if (!found) { + return E_FAIL; + } + + uint32_t surfaceCount = kDefaultSurfaces; + if (chosenConfig.ConfigMinRenderTargetBuffCount > surfaceCount) { + surfaceCount = chosenConfig.ConfigMinRenderTargetBuffCount; + } + if (surfaceCount > kMaxSurfaces) { surfaceCount = kMaxSurfaces; } + + hr = AllocateSurfaces(aWidth, aHeight, surfaceCount); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + IDirect3DSurface9* surfacePtrs[kMaxSurfaces]; + for (uint32_t i = 0; i < mNumSurfaces; i++) { + surfacePtrs[i] = mSurfaces[i]; + } + + hr = mDecoderService->CreateVideoDecoder( + sVP9DecoderGUID, + &desc, + &chosenConfig, + surfacePtrs, + mNumSurfaces, + mDecoder.StartAssignment()); + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +HRESULT +DXVAVP9Manager::ConfigureForSize(uint32_t aWidth, uint32_t aHeight) +{ + MutexAutoLock lock(mLock); + if (aWidth == mWidth && aHeight == mHeight) { + return S_OK; + } + mWidth = aWidth; + mHeight = aHeight; + return InitDecoder(aWidth, aHeight); +} + +uint32_t +DXVAVP9Manager::PickNextSurface() const +{ + for (uint32_t tries = 0; tries < mNumSurfaces; tries++) { + uint32_t idx = (mCurrentSurface + 1 + tries) % mNumSurfaces; + bool inUse = false; + for (int slot = 0; slot < 8; slot++) { + if (mRefFrameSurface[slot] == idx) { + inUse = true; + break; + } + } + if (!inUse) { + return idx; + } + } + return (mCurrentSurface + 1) % mNumSurfaces; +} + +void +DXVAVP9Manager::FillPicParams(const VP9FrameHeader& h, + UXP_DXVA_PicParams_VP9& p) +{ + memset(&p, 0, sizeof(p)); + + p.CurrPic.Index7Bits = (UCHAR)mCurrentSurface; + p.CurrPic.AssociatedFlag = 0; + p.profile = 0; + + p.frame_type = h.frameType; + p.show_frame = h.showFrame; + p.error_resilient_mode = h.errorResilientMode; + p.subsampling_x = h.subsamplingX; + p.subsampling_y = h.subsamplingY; + p.refresh_frame_context = h.refreshFrameContext; + p.frame_parallel_decoding_mode = h.frameParallelDecodingMode; + p.intra_only = (h.frameType != 0 && h.isIntra) ? 1 : 0; + p.frame_context_idx = h.frameContextIdx; + p.reset_frame_context = h.resetFrameContext; + p.allow_high_precision_mv = (h.frameType != 0) ? h.allowHighPrecisionMv : 0; + + p.BitDepthMinus8Luma = 0; + p.BitDepthMinus8Chroma = 0; + + p.interp_filter = h.interpFilter; + + p.width = h.frameWidth; + p.height = h.frameHeight; + + for (int i = 0; i < 8; ++i) { + p.ref_frame_map[i].bPicEntry = mRefFrameSurface[i]; + p.ref_frame_coded_width[i] = mRefFrameWidth[i]; + p.ref_frame_coded_height[i] = mRefFrameHeight[i]; + } + + for (int i = 0; i < 3; i++) { + p.frame_refs[i].bPicEntry = 0xFF; + } + if (!h.isIntra) { + for (int i = 0; i < 3; i++) { + uint8_t slot = h.refFrameIdx[i] & 7; + uint8_t surfIdx = mRefFrameSurface[slot]; + p.frame_refs[i].bPicEntry = surfIdx; + p.ref_frame_sign_bias[i + 1] = h.refFrameSignBias[i + 1]; + } + } + + p.filter_level = (CHAR)h.filterLevel; + p.sharpness_level = (CHAR)h.sharpnessLevel; + p.mode_ref_delta_enabled = h.modeRefLfEnabled; + p.mode_ref_delta_update = h.modeRefDeltaUpdate; + p.use_prev_in_find_mv_refs = h.usePrevFrameMvs; + for (int i = 0; i < 4; i++) { p.ref_deltas[i] = h.refDeltas[i]; } + for (int i = 0; i < 2; i++) { p.mode_deltas[i] = h.modeDeltas[i]; } + + p.base_qindex = (SHORT)h.baseQIndex; + p.y_dc_delta_q = h.deltaQYDc; + p.uv_dc_delta_q = h.deltaQUvDc; + p.uv_ac_delta_q = h.deltaQUvAc; + + p.stVP9Segments.enabled = h.segmentationEnabled; + p.stVP9Segments.update_map = h.segmentationUpdateMap; + p.stVP9Segments.temporal_update = h.segmentationTemporalUpdate; + p.stVP9Segments.abs_delta = h.segmentationAbsOrDelta; + for (int i = 0; i < 7; i++) { + p.stVP9Segments.tree_probs[i] = h.segmentationTreeProbs[i]; + } + for (int i = 0; i < 3; i++) { + p.stVP9Segments.pred_probs[i] = h.segmentationPredProbs[i]; + } + for (int i = 0; i < 8; i++) { + UCHAR mask = 0; + for (int j = 0; j < 4; j++) { + if (h.segFeatureEnabled[i][j]) { + mask |= (1 << j); + } + p.stVP9Segments.feature_data[i][j] = (SHORT)h.segFeatureData[i][j]; + } + p.stVP9Segments.feature_mask[i] = mask; + } + + p.log2_tile_cols = (UCHAR)h.log2TileCols; + p.log2_tile_rows = (UCHAR)h.log2TileRows; + + p.first_partition_size = (USHORT)h.compressedHeaderSize; + p.uncompressed_header_size_byte_aligned = (USHORT)h.uncompressedHeaderSizeBytes; + + p.StatusReportFeedbackNumber = ++mFrameCount; + if (p.StatusReportFeedbackNumber == 0) { + p.StatusReportFeedbackNumber = ++mFrameCount; + } +} + +HRESULT +DXVAVP9Manager::DecodeFrame(const uint8_t* aData, uint32_t aSize, + IDirect3DSurface9** aOutSurface, + bool* aOutShouldDisplay, + VP9FrameHeader* aOutHeader) +{ + NS_ENSURE_TRUE(mDecoder, E_NOT_VALID_STATE); + NS_ENSURE_TRUE(aData && aSize > 0, E_INVALIDARG); + NS_ENSURE_TRUE(aOutSurface, E_POINTER); + NS_ENSURE_TRUE(aOutShouldDisplay, E_POINTER); + + MutexAutoLock lock(mLock); + + NS_ENSURE_TRUE(aOutHeader, E_POINTER); + *aOutSurface = nullptr; + *aOutShouldDisplay = false; + + VP9FrameHeader header; + if (!mParser.Parse(aData, aSize, header)) { + if (header.profile != 0) { + return MF_E_INVALIDMEDIATYPE; + } + return E_FAIL; + } + + *aOutHeader = header; + + if (header.showExistingFrame) { + uint8_t slot = header.frameToShowMapIdx; + uint8_t surfIdx = mRefFrameSurface[slot]; + if (surfIdx == 0xFF || !mSurfaces[surfIdx]) { + return E_FAIL; + } + *aOutSurface = mSurfaces[surfIdx]; + (*aOutSurface)->AddRef(); + *aOutShouldDisplay = true; + return S_OK; + } + + if (header.frameWidth != mWidth || header.frameHeight != mHeight) { + if (header.frameType != 0) { return MF_E_INVALIDMEDIATYPE; } + HRESULT hr = InitDecoder(header.frameWidth, header.frameHeight); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + mWidth = header.frameWidth; + mHeight = header.frameHeight; + } + + mCurrentSurface = PickNextSurface(); + IDirect3DSurface9* currentSurf = mSurfaces[mCurrentSurface]; + + static const uint32_t kBeginFrameRetries = 50; + HRESULT hr = E_PENDING; + for (uint32_t attempt = 0; attempt < kBeginFrameRetries; ++attempt) { + hr = mDecoder->BeginFrame(currentSurf, nullptr); + if (hr != E_PENDING) { + break; + } + ::Sleep(2); + } + if (FAILED(hr)) { + return hr; + } + + auto failWith = [this](HRESULT aHR) -> HRESULT { + mDecoder->EndFrame(nullptr); + return aHR; + }; + + if (aSize > UINT32_MAX - 127) { return failWith(E_INVALIDARG); } + UINT bitstreamCopied = (aSize + 127) & ~127u; + { + void* buf = nullptr; UINT size = 0; + hr = mDecoder->GetBuffer(DXVA2_BitStreamDateBufferType, &buf, &size); + if (FAILED(hr)) { return failWith(hr); } + if (!buf || size < bitstreamCopied) { + mDecoder->ReleaseBuffer(DXVA2_BitStreamDateBufferType); + return failWith(E_FAIL); + } + memcpy(buf, aData, aSize); + memset(static_cast(buf) + aSize, 0, bitstreamCopied - aSize); + hr = mDecoder->ReleaseBuffer(DXVA2_BitStreamDateBufferType); + if (FAILED(hr)) { return failWith(hr); } + } + + UXP_DXVA_PicParams_VP9 picParams; + FillPicParams(header, picParams); + + UXP_DXVA_Slice_VPx_Short sliceInfo = {}; + sliceInfo.BSNALunitDataLocation = 0; + sliceInfo.SliceBytesInBuffer = aSize; + sliceInfo.wBadSliceChopping = 0; + + { + void* buf = nullptr; + UINT size = 0; + hr = mDecoder->GetBuffer(DXVA2_PictureParametersBufferType, &buf, &size); + if (FAILED(hr)) { return failWith(hr); } + if (size < sizeof(picParams)) { + mDecoder->ReleaseBuffer(DXVA2_PictureParametersBufferType); + return failWith(E_FAIL); + } + memcpy(buf, &picParams, sizeof(picParams)); + hr = mDecoder->ReleaseBuffer(DXVA2_PictureParametersBufferType); + if (FAILED(hr)) { return failWith(hr); } + } + + { + void* buf = nullptr; + UINT size = 0; + hr = mDecoder->GetBuffer(DXVA2_SliceControlBufferType, &buf, &size); + if (FAILED(hr)) { return failWith(hr); } + if (size < sizeof(sliceInfo)) { + mDecoder->ReleaseBuffer(DXVA2_SliceControlBufferType); + return failWith(E_FAIL); + } + memcpy(buf, &sliceInfo, sizeof(sliceInfo)); + hr = mDecoder->ReleaseBuffer(DXVA2_SliceControlBufferType); + if (FAILED(hr)) { return failWith(hr); } + } + + DXVA2_DecodeBufferDesc bufDescs[3] = {}; + bufDescs[0].CompressedBufferType = DXVA2_PictureParametersBufferType; + bufDescs[0].DataSize = (UINT)sizeof(picParams); + bufDescs[0].DataOffset = 0; + + bufDescs[1].CompressedBufferType = DXVA2_SliceControlBufferType; + bufDescs[1].DataSize = (UINT)sizeof(sliceInfo); + bufDescs[1].DataOffset = 0; + + bufDescs[2].CompressedBufferType = DXVA2_BitStreamDateBufferType; + bufDescs[2].DataSize = bitstreamCopied; + bufDescs[2].DataOffset = 0; + + DXVA2_DecodeExecuteParams execParams = {}; + execParams.NumCompBuffers = 3; + execParams.pCompressedBuffers = bufDescs; + + hr = mDecoder->Execute(&execParams); + if (FAILED(hr)) { + return failWith(hr); + } + + hr = mDecoder->EndFrame(nullptr); + if (FAILED(hr)) { + return hr; + } + + for (int i = 0; i < 8; i++) { + if (header.refreshFrameFlags & (1 << i)) { + mRefFrameSurface[i] = (uint8_t)mCurrentSurface; + mRefFrameWidth[i] = header.frameWidth; + mRefFrameHeight[i] = header.frameHeight; + } + } + + mParser.Commit(header); + + *aOutSurface = currentSurf; + (*aOutSurface)->AddRef(); + *aOutShouldDisplay = (header.showFrame != 0); + + return S_OK; +} + +} // namespace mozilla diff --git a/dom/media/platforms/wmf/DXVAVP9Manager.h b/dom/media/platforms/wmf/DXVAVP9Manager.h new file mode 100644 index 0000000000..76ccd6ed56 --- /dev/null +++ b/dom/media/platforms/wmf/DXVAVP9Manager.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DXVAVP9Manager_h_ +#define DXVAVP9Manager_h_ + +#include "WMF.h" +#include "VP9DXVA.h" +#include "VP9HeaderParser.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Mutex.h" +#include "MediaInfo.h" +#include "ImageContainer.h" +#include "nsTArray.h" +#include "nsPrintfCString.h" + +namespace mozilla { + +namespace layers { +class KnowsCompositor; +class Image; +class D3D9RecycleAllocator; +} + +class DXVAVP9Manager { +public: + DXVAVP9Manager(); + ~DXVAVP9Manager(); + + bool Init(IUnknown* aDeviceManager, uint32_t aWidth, uint32_t aHeight, + nsACString& aFailureReason); + HRESULT DecodeFrame(const uint8_t* aData, uint32_t aSize, + IDirect3DSurface9** aOutSurface, + bool* aOutShouldDisplay, + VP9FrameHeader* aOutHeader); + + HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight); + + void Flush(); + void Shutdown(); + + bool IsValid() const { return mDecoder != nullptr; } + + static const GUID& GetVP9DecoderGUID(); + + static bool SupportsVP9(IDirect3DDeviceManager9* aDeviceManager); + +private: + HRESULT InitDecoder(uint32_t aWidth, uint32_t aHeight); + HRESULT AllocateSurfaces(uint32_t aWidth, uint32_t aHeight, uint32_t aCount); + void FreeSurfaces(); + + uint32_t PickNextSurface() const; + + void FillPicParams(const VP9FrameHeader& aHeader, + UXP_DXVA_PicParams_VP9& aPicParams); + + RefPtr mDeviceManager; + RefPtr mDecoderService; + RefPtr mDecoder; + + static const uint32_t kMinSurfaces = 9; + static const uint32_t kDefaultSurfaces = 10; + static const uint32_t kMaxSurfaces = 24; + RefPtr mSurfaces[kMaxSurfaces]; + uint32_t mNumSurfaces; + uint32_t mCurrentSurface; + + uint32_t mWidth; + uint32_t mHeight; + uint8_t mRefFrameSurface[8]; + uint32_t mRefFrameWidth[8]; + uint32_t mRefFrameHeight[8]; + uint32_t mFrameCount; + + VP9HeaderParser mParser; + Mutex mLock; +}; +} // namespace mozilla +#endif diff --git a/dom/media/platforms/wmf/VP9DXVA.h b/dom/media/platforms/wmf/VP9DXVA.h new file mode 100644 index 0000000000..58854f7d4e --- /dev/null +++ b/dom/media/platforms/wmf/VP9DXVA.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VP9DXVA_h_ +#define VP9DXVA_h_ +#include +#pragma pack(push, 1) + +typedef struct _UXP_DXVA_PicEntry_VPx { + union { + struct { + UCHAR Index7Bits : 7; + UCHAR AssociatedFlag : 1; + }; + UCHAR bPicEntry; + }; +} UXP_DXVA_PicEntry_VPx; + +typedef struct _UXP_DXVA_segmentation_VP9 { + union { + struct { + UCHAR enabled : 1; + UCHAR update_map : 1; + UCHAR temporal_update : 1; + UCHAR abs_delta : 1; + UCHAR ReservedSegmentFlags4Bits : 4; + }; + UCHAR wSegmentInfoFlags; + }; + UCHAR tree_probs[7]; + UCHAR pred_probs[3]; + SHORT feature_data[8][4]; + UCHAR feature_mask[8]; +} UXP_DXVA_segmentation_VP9; + +typedef struct _UXP_DXVA_PicParams_VP9 { + UXP_DXVA_PicEntry_VPx CurrPic; + UCHAR profile; + union { + struct { + USHORT frame_type : 1; + USHORT show_frame : 1; + USHORT error_resilient_mode : 1; + USHORT subsampling_x : 1; + USHORT subsampling_y : 1; + USHORT extra_plane : 1; + USHORT refresh_frame_context : 1; + USHORT frame_parallel_decoding_mode : 1; + USHORT intra_only : 1; + USHORT frame_context_idx : 2; + USHORT reset_frame_context : 2; + USHORT allow_high_precision_mv : 1; + USHORT ReservedFormatInfo2Bits : 2; + }; + USHORT wFormatAndPictureInfoFlags; + }; + UINT width; + UINT height; + UCHAR BitDepthMinus8Luma; + UCHAR BitDepthMinus8Chroma; + UCHAR interp_filter; + UCHAR Reserved8Bits; + UXP_DXVA_PicEntry_VPx ref_frame_map[8]; + UINT ref_frame_coded_width[8]; + UINT ref_frame_coded_height[8]; + UXP_DXVA_PicEntry_VPx frame_refs[3]; + CHAR ref_frame_sign_bias[4]; + CHAR filter_level; + CHAR sharpness_level; + union { + struct { + UCHAR mode_ref_delta_enabled : 1; + UCHAR mode_ref_delta_update : 1; + UCHAR use_prev_in_find_mv_refs : 1; + UCHAR ReservedControlInfo5Bits : 5; + }; + UCHAR wControlInfoFlags; + }; + CHAR ref_deltas[4]; + CHAR mode_deltas[2]; + SHORT base_qindex; + CHAR y_dc_delta_q; + CHAR uv_dc_delta_q; + CHAR uv_ac_delta_q; + UXP_DXVA_segmentation_VP9 stVP9Segments; + UCHAR log2_tile_cols; + UCHAR log2_tile_rows; + USHORT uncompressed_header_size_byte_aligned; + USHORT first_partition_size; + USHORT Reserved16Bits; + UINT Reserved32Bits; + UINT StatusReportFeedbackNumber; +} UXP_DXVA_PicParams_VP9; + +typedef struct _UXP_DXVA_Slice_VPx_Short { + UINT BSNALunitDataLocation; + UINT SliceBytesInBuffer; + USHORT wBadSliceChopping; +} UXP_DXVA_Slice_VPx_Short; + +#pragma pack(pop) + + +static_assert(sizeof(UXP_DXVA_PicParams_VP9) == 208, "VP9 picture ABI"); +static_assert(sizeof(UXP_DXVA_segmentation_VP9) == 83, "VP9 segmentation ABI"); +static_assert(sizeof(UXP_DXVA_Slice_VPx_Short) == 10, "VP9 slice ABI"); +static_assert(offsetof(UXP_DXVA_PicParams_VP9, stVP9Segments) == 109, "VP9 segment offset"); +static_assert(offsetof(UXP_DXVA_PicParams_VP9, StatusReportFeedbackNumber) == 204, "VP9 status offset"); +#endif diff --git a/dom/media/platforms/wmf/VP9HeaderParser.cpp b/dom/media/platforms/wmf/VP9HeaderParser.cpp new file mode 100644 index 0000000000..e22f7ce87b --- /dev/null +++ b/dom/media/platforms/wmf/VP9HeaderParser.cpp @@ -0,0 +1,464 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VP9HeaderParser.h" +#include +#include +namespace mozilla { +uint32_t +VP9BitReader::ReadBits(int n) +{ + if (n < 0 || n > 32) { mError = true; return 0; } + uint32_t result = 0; + while (n > 0) { + if (mBitsLeft == 0) { + if (mBytesRead >= mSize) { + mError = true; + return 0; + } + mCurrent = mData[mBytesRead++]; + mBitsLeft = 8; + } + int take = std::min(n, mBitsLeft); + result <<= take; + result |= (mCurrent >> (mBitsLeft - take)) & ((1 << take) - 1); + mBitsLeft -= take; + n -= take; + } + return result; +} + +int32_t +VP9BitReader::ReadSignedBits(int n) +{ + int32_t v = (int32_t)ReadBits(n); + int32_t sign = (int32_t)ReadBit(); + return sign ? -v : v; +} + +static void +ReadColorConfig(VP9BitReader& br, uint8_t profile, VP9FrameHeader& h) +{ + if (profile >= 2) { + br.ReadBit(); + } + h.colorSpace = (uint8_t)br.ReadBits(3); + if (h.colorSpace != 7) { + h.colorRange = (uint8_t)br.ReadBit(); + if (profile == 1 || profile == 3) { + h.subsamplingX = (uint8_t)br.ReadBit(); + h.subsamplingY = (uint8_t)br.ReadBit(); + br.ReadBit(); + } else { + h.subsamplingX = 1; + h.subsamplingY = 1; + } + } else { + h.colorRange = 1; + if (profile == 1 || profile == 3) { + h.subsamplingX = 0; + h.subsamplingY = 0; + br.ReadBit(); + } + } +} + +static uint32_t +ReadFrameSize(VP9BitReader& br) +{ + return br.ReadBits(16) + 1; +} + +void +VP9HeaderParser::ParseLoopFilter(VP9BitReader& br, VP9FrameHeader& h) +{ + h.filterLevel = (uint8_t)br.ReadBits(6); + h.sharpnessLevel = (uint8_t)br.ReadBits(3); + h.modeRefLfEnabled = 0; + + uint8_t modeRefDeltaEnabled = (uint8_t)br.ReadBit(); + h.modeRefDeltaUpdate = 0; + if (modeRefDeltaEnabled) { + h.modeRefLfEnabled = 1; + uint8_t modeRefDeltaUpdate = (uint8_t)br.ReadBit(); + h.modeRefDeltaUpdate = modeRefDeltaUpdate; + if (modeRefDeltaUpdate) { + for (int i = 0; i < 4; i++) { + if (br.ReadBit()) { + mRefDeltas[i] = (int8_t)br.ReadSignedBits(6); + } + } + for (int i = 0; i < 2; i++) { + if (br.ReadBit()) { + mModeDeltas[i] = (int8_t)br.ReadSignedBits(6); + } + } + } + } + for (int i = 0; i < 4; i++) { h.refDeltas[i] = mRefDeltas[i]; } + for (int i = 0; i < 2; i++) { h.modeDeltas[i] = mModeDeltas[i]; } +} + +void +VP9HeaderParser::ParseQuantization(VP9BitReader& br, VP9FrameHeader& h) +{ + h.baseQIndex = (uint8_t)br.ReadBits(8); + h.deltaQYDc = br.ReadBit() ? (int8_t)br.ReadSignedBits(4) : 0; + h.deltaQUvDc = br.ReadBit() ? (int8_t)br.ReadSignedBits(4) : 0; + h.deltaQUvAc = br.ReadBit() ? (int8_t)br.ReadSignedBits(4) : 0; + h.lossless = (h.baseQIndex == 0 && + h.deltaQYDc == 0 && + h.deltaQUvDc == 0 && + h.deltaQUvAc == 0); +} + +void +VP9HeaderParser::ParseSegmentation(VP9BitReader& br, VP9FrameHeader& h) +{ + static const int kSegLvlMax = 4; + static const int kMaxSegments = 8; + static const int kSegFeatureBits[4] = { 8, 6, 2, 0 }; + + + h.segmentationEnabled = (uint8_t)br.ReadBit(); + if (!h.segmentationEnabled) { + return; + } + + h.segmentationUpdateMap = (uint8_t)br.ReadBit(); + if (h.segmentationUpdateMap) { + for (int i = 0; i < 7; i++) { + h.segmentationTreeProbs[i] = br.ReadBit() + ? (uint8_t)br.ReadBits(8) + : 255; + } + h.segmentationTemporalUpdate = (uint8_t)br.ReadBit(); + memset(h.segmentationPredProbs, 255, sizeof(h.segmentationPredProbs)); + if (h.segmentationTemporalUpdate) { + for (int i = 0; i < 3; i++) { + h.segmentationPredProbs[i] = br.ReadBit() + ? (uint8_t)br.ReadBits(8) + : 255; + } + } + } + + uint8_t segUpdate = (uint8_t)br.ReadBit(); + if (!segUpdate) { + return; + } + + memset(h.segFeatureEnabled, 0, sizeof(h.segFeatureEnabled)); + memset(h.segFeatureData, 0, sizeof(h.segFeatureData)); + h.segmentationAbsOrDelta = (uint8_t)br.ReadBit(); + for (int i = 0; i < kMaxSegments; i++) { + for (int j = 0; j < kSegLvlMax; j++) { + if (br.ReadBit()) { + h.segFeatureEnabled[i][j] = 1; + int bits = kSegFeatureBits[j]; + if (bits > 0) { + int16_t v = (int16_t)br.ReadBits(bits); + if (j < 2 && br.ReadBit()) { + v = -v; + } + h.segFeatureData[i][j] = v; + } + } + } + } +} + +void +VP9HeaderParser::ParseTileInfo(VP9BitReader& br, VP9FrameHeader& h) +{ + static const uint32_t kMaxTileWidthB64 = 64; + static const uint32_t kMinTileWidthB64 = 4; + + uint32_t sbCols = (h.frameWidth + 63) >> 6; + + int minLog2TileCols = 0; + while ((kMaxTileWidthB64 << minLog2TileCols) < sbCols) { + minLog2TileCols++; + } + + int maxLog2TileCols = 1; + while ((sbCols >> maxLog2TileCols) >= kMinTileWidthB64) { + maxLog2TileCols++; + } + maxLog2TileCols--; + if (maxLog2TileCols < minLog2TileCols) { + maxLog2TileCols = minLog2TileCols; + } + + h.log2TileCols = (uint8_t)minLog2TileCols; + while (h.log2TileCols < (uint8_t)maxLog2TileCols) { + if (br.ReadBit()) { + h.log2TileCols++; + } else { + break; + } + } + + h.log2TileRows = 0; + if (br.ReadBit()) { + h.log2TileRows = 1; + if (br.ReadBit()) { + h.log2TileRows = 2; + } + } +} + +bool +VP9HeaderParser::ParseInternal(const uint8_t* aData, uint32_t aSize, + VP9FrameHeader& aHeader) +{ + memset(&aHeader, 0, sizeof(aHeader)); + + if (!aData || !aSize) { + return false; + } + + aHeader.subsamplingX = aHeader.subsamplingY = 1; + aHeader.colorSpace = mPrevious.colorSpace; + aHeader.colorRange = mPrevious.colorRange; + aHeader.segmentationAbsOrDelta = mPrevious.segmentationAbsOrDelta; + memcpy(aHeader.segFeatureEnabled, mPrevious.segFeatureEnabled, sizeof(aHeader.segFeatureEnabled)); + memcpy(aHeader.segFeatureData, mPrevious.segFeatureData, sizeof(aHeader.segFeatureData)); + memcpy(aHeader.segmentationTreeProbs, mPrevious.segmentationTreeProbs, sizeof(aHeader.segmentationTreeProbs)); + memcpy(aHeader.segmentationPredProbs, mPrevious.segmentationPredProbs, sizeof(aHeader.segmentationPredProbs)); + VP9BitReader br(aData, aSize); + + uint32_t marker = br.ReadBits(2); + if (marker != 0x2) { + return false; + } + + uint8_t profileLowBit = (uint8_t)br.ReadBit(); + uint8_t profileHighBit = (uint8_t)br.ReadBit(); + uint8_t profile = (profileHighBit << 1) | profileLowBit; + if (profile == 3) { + br.ReadBit(); + } + aHeader.profile = profile; + + if (profile != 0) { return false; } + + uint8_t showExistingFrame = (uint8_t)br.ReadBit(); + if (showExistingFrame) { + uint8_t mapIdx = (uint8_t)br.ReadBits(3); + aHeader.showExistingFrame = 1; + aHeader.frameToShowMapIdx = mapIdx; + aHeader.frameType = 1; + aHeader.showFrame = 1; + aHeader.frameWidth = mRefFrameWidth[mapIdx]; + aHeader.frameHeight = mRefFrameHeight[mapIdx]; + aHeader.isIntra = false; + aHeader.uncompressedHeaderSizeBytes = (uint32_t)br.BytesConsumed(); + aHeader.compressedHeaderSize = 0; + return !br.Failed() && aHeader.frameWidth && aHeader.frameHeight; + } + + aHeader.frameType = (uint8_t)br.ReadBit(); + aHeader.showFrame = (uint8_t)br.ReadBit(); + aHeader.errorResilientMode = (uint8_t)br.ReadBit(); + + if (aHeader.frameType == 0) { + uint8_t s0 = (uint8_t)br.ReadBits(8); + uint8_t s1 = (uint8_t)br.ReadBits(8); + uint8_t s2 = (uint8_t)br.ReadBits(8); + if (s0 != 0x49 || s1 != 0x83 || s2 != 0x42) { + return false; + } + + ReadColorConfig(br, profile, aHeader); + if (aHeader.colorSpace == 7 || aHeader.colorSpace == 6) { return false; } + + aHeader.frameWidth = ReadFrameSize(br); + aHeader.frameHeight = ReadFrameSize(br); + if (br.ReadBit()) { + aHeader.renderWidth = ReadFrameSize(br); + aHeader.renderHeight = ReadFrameSize(br); + } else { + aHeader.renderWidth = aHeader.frameWidth; + aHeader.renderHeight = aHeader.frameHeight; + } + + aHeader.refreshFrameFlags = 0xFF; + aHeader.isIntra = true; + + } else { + aHeader.isIntra = false; + + uint8_t intraOnly = 0; + if (!aHeader.showFrame) { + intraOnly = (uint8_t)br.ReadBit(); + } + aHeader.isIntra = (intraOnly != 0); + + if (!aHeader.errorResilientMode) { + aHeader.resetFrameContext = (uint8_t)br.ReadBits(2); + } else { + aHeader.resetFrameContext = 0; + } + + if (intraOnly) { + uint8_t s0 = (uint8_t)br.ReadBits(8); + uint8_t s1 = (uint8_t)br.ReadBits(8); + uint8_t s2 = (uint8_t)br.ReadBits(8); + if (s0 != 0x49 || s1 != 0x83 || s2 != 0x42) { + return false; + } + if (profile > 0) { + ReadColorConfig(br, profile, aHeader); + } else { + aHeader.colorSpace = 1; + aHeader.colorRange = 0; + aHeader.subsamplingX = 1; + aHeader.subsamplingY = 1; + } + aHeader.refreshFrameFlags = (uint8_t)br.ReadBits(8); + aHeader.frameWidth = ReadFrameSize(br); + aHeader.frameHeight = ReadFrameSize(br); + if (br.ReadBit()) { + aHeader.renderWidth = ReadFrameSize(br); + aHeader.renderHeight = ReadFrameSize(br); + } else { + aHeader.renderWidth = aHeader.frameWidth; + aHeader.renderHeight = aHeader.frameHeight; + } + + } else { + aHeader.refreshFrameFlags = (uint8_t)br.ReadBits(8); + + for (int i = 0; i < 3; i++) { + aHeader.refFrameIdx[i] = (uint8_t)br.ReadBits(3); + aHeader.refFrameSignBias[i+1] = (uint8_t)br.ReadBit(); + } + + for (int i = 0; i < 3; ++i) { + if (!mRefFrameWidth[aHeader.refFrameIdx[i]]) { return false; } + } + bool foundRef = false; + for (int i = 0; i < 3; i++) { + if (br.ReadBit()) { + aHeader.frameWidth = mRefFrameWidth[aHeader.refFrameIdx[i]]; + aHeader.frameHeight = mRefFrameHeight[aHeader.refFrameIdx[i]]; + foundRef = true; + break; + } + } + if (!foundRef) { + aHeader.frameWidth = ReadFrameSize(br); + aHeader.frameHeight = ReadFrameSize(br); + } + + if (br.ReadBit()) { + aHeader.renderWidth = ReadFrameSize(br); + aHeader.renderHeight = ReadFrameSize(br); + } else { + aHeader.renderWidth = aHeader.frameWidth; + aHeader.renderHeight = aHeader.frameHeight; + } + + aHeader.allowHighPrecisionMv = (uint8_t)br.ReadBit(); + if (br.ReadBit()) { + aHeader.interpFilter = 4; + } else { + static const uint8_t kLiteralToFilter[4] = { 1, 0, 2, 3 }; + aHeader.interpFilter = kLiteralToFilter[br.ReadBits(2)]; + } + } + } + + if (!aHeader.errorResilientMode) { + aHeader.refreshFrameContext = (uint8_t)br.ReadBit(); + aHeader.frameParallelDecodingMode = (uint8_t)br.ReadBit(); + } else { + aHeader.refreshFrameContext = 0; + aHeader.frameParallelDecodingMode = 1; + } + aHeader.frameContextIdx = (uint8_t)br.ReadBits(2); + + aHeader.usePrevFrameMvs = !aHeader.isIntra && !aHeader.errorResilientMode && + !mPrevious.isIntra && mPrevious.showFrame && mPrevious.frameWidth == aHeader.frameWidth && + mPrevious.frameHeight == aHeader.frameHeight; + if (aHeader.isIntra || aHeader.errorResilientMode) { + mRefDeltas[0] = 1; mRefDeltas[1] = 0; + mRefDeltas[2] = mRefDeltas[3] = -1; + mModeDeltas[0] = mModeDeltas[1] = 0; + aHeader.segmentationAbsOrDelta = 0; + memset(aHeader.segFeatureEnabled, 0, sizeof(aHeader.segFeatureEnabled)); + memset(aHeader.segFeatureData, 0, sizeof(aHeader.segFeatureData)); + aHeader.frameContextIdx = 0; + } + ParseLoopFilter(br, aHeader); + ParseQuantization(br, aHeader); + ParseSegmentation(br, aHeader); + ParseTileInfo(br, aHeader); + + aHeader.compressedHeaderSize = br.ReadBits(16); + + aHeader.uncompressedHeaderSizeBytes = (uint32_t)br.BytesConsumed(); + + if (aHeader.frameWidth == 0 || aHeader.frameHeight == 0) { + return false; + } + + return !br.Failed() && aHeader.compressedHeaderSize != 0 && + aHeader.uncompressedHeaderSizeBytes <= aSize && + aHeader.compressedHeaderSize < aSize - aHeader.uncompressedHeaderSizeBytes; +} + + +void VP9HeaderParser::Reset() { + memset(&mPrevious, 0, sizeof(mPrevious)); + memset(mPrevious.segmentationTreeProbs, 255, sizeof(mPrevious.segmentationTreeProbs)); + memset(mPrevious.segmentationPredProbs, 255, sizeof(mPrevious.segmentationPredProbs)); + memset(mRefFrameWidth, 0, sizeof(mRefFrameWidth)); + memset(mRefFrameHeight, 0, sizeof(mRefFrameHeight)); + mRefDeltas[0] = 1; mRefDeltas[1] = 0; mRefDeltas[2] = mRefDeltas[3] = -1; + mModeDeltas[0] = mModeDeltas[1] = 0; +} +bool VP9HeaderParser::Parse(const uint8_t* data, uint32_t size, VP9FrameHeader& h) const { + VP9HeaderParser candidate = *this; + return candidate.ParseInternal(data, size, h); +} +void VP9HeaderParser::Commit(const VP9FrameHeader& h) { + if (h.showExistingFrame) { return; } + mPrevious = h; + memcpy(mRefDeltas, h.refDeltas, sizeof(mRefDeltas)); + memcpy(mModeDeltas, h.modeDeltas, sizeof(mModeDeltas)); + for (int i = 0; i < 8; ++i) { + if (h.refreshFrameFlags & (1 << i)) { + mRefFrameWidth[i] = h.frameWidth; + mRefFrameHeight[i] = h.frameHeight; + } + } +} +bool VP9SplitSuperframe(const uint8_t* data, uint32_t size, + uint32_t (&offsets)[8], uint32_t (&sizes)[8], uint32_t& count) { + count = 0; + if (!data || !size) { return false; } + uint8_t marker = data[size - 1]; + if ((marker & 0xe0) != 0xc0) { + offsets[0] = 0; sizes[0] = size; count = 1; return true; + } + uint32_t frames = (marker & 7) + 1; + uint32_t magnitude = ((marker >> 3) & 3) + 1; + uint32_t indexSize = 2 + frames * magnitude; + if (size < indexSize || data[size - indexSize] != marker) { return false; } + uint32_t pos = size - indexSize + 1, total = 0; + for (uint32_t i = 0; i < frames; ++i) { + uint32_t length = 0; + for (uint32_t j = 0; j < magnitude; ++j) { length |= uint32_t(data[pos++]) << (j * 8); } + if (!length || length > size - indexSize - total) { return false; } + offsets[i] = total; sizes[i] = length; total += length; + } + if (total != size - indexSize) { return false; } + count = frames; + return true; +} + +} diff --git a/dom/media/platforms/wmf/VP9HeaderParser.h b/dom/media/platforms/wmf/VP9HeaderParser.h new file mode 100644 index 0000000000..13e2645b32 --- /dev/null +++ b/dom/media/platforms/wmf/VP9HeaderParser.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef VP9HeaderParser_h_ +#define VP9HeaderParser_h_ +#include +#include +namespace mozilla { +struct VP9FrameHeader { + uint8_t profile; + // 0 = key frame, 1 = non-key frame + uint8_t frameType; + uint8_t showFrame; + uint8_t showExistingFrame; // true if this packet just redisplays a ref + uint8_t frameToShowMapIdx; // valid only if showExistingFrame + uint8_t errorResilientMode; + uint8_t colorSpace; + uint8_t colorRange; + uint8_t subsamplingX; + uint8_t subsamplingY; + uint32_t frameWidth; + uint32_t frameHeight; + uint32_t renderWidth; + uint32_t renderHeight; + uint8_t refreshFrameFlags; + uint8_t refFrameIdx[3]; + uint8_t refFrameSignBias[4]; + uint8_t allowHighPrecisionMv; + uint8_t interpFilter; + uint8_t refreshFrameContext; + uint8_t frameParallelDecodingMode; + uint8_t frameContextIdx; + uint8_t resetFrameContext; + + uint8_t segmentationEnabled; + uint8_t segmentationUpdateMap; + uint8_t segmentationTemporalUpdate; + uint8_t segmentationAbsOrDelta; + uint8_t segmentationTreeProbs[7]; + uint8_t segmentationPredProbs[3]; + uint8_t segFeatureEnabled[8][4]; + int16_t segFeatureData[8][4]; + + uint8_t filterLevel; + uint8_t sharpnessLevel; + uint8_t modeRefLfEnabled; + uint8_t modeRefDeltaUpdate; + int8_t refDeltas[4]; + int8_t modeDeltas[2]; + + uint8_t baseQIndex; + int8_t deltaQYDc; + int8_t deltaQUvDc; + int8_t deltaQUvAc; + + uint8_t log2TileCols; + uint8_t log2TileRows; + + uint32_t compressedHeaderSize; + + uint32_t uncompressedHeaderSizeBytes; + + bool usePrevFrameMvs; + bool lossless; + bool isIntra; +}; + +class VP9BitReader { +public: + VP9BitReader(const uint8_t* aData, size_t aSize) + : mData(aData), mSize(aSize), mBytesRead(0), mBitsLeft(0), mCurrent(0), mError(false) + {} + + uint32_t ReadBits(int n); + uint32_t ReadBit() { return ReadBits(1); } + uint32_t ReadLiteral(int n) { return ReadBits(n); } + int32_t ReadSignedBits(int n); + bool Failed() const { return mError; } + bool Eof() const { return mBytesRead >= mSize && mBitsLeft == 0; } + size_t BytesConsumed() const { + size_t bitsConsumed = mBytesRead * 8 - (size_t)mBitsLeft; + return (bitsConsumed + 7) / 8; + } + +private: + const uint8_t* mData; + size_t mSize; + size_t mBytesRead; + int mBitsLeft; + uint8_t mCurrent; + bool mError; +}; + +class VP9HeaderParser { +public: + VP9HeaderParser() { Reset(); } + void Reset(); + bool Parse(const uint8_t* aData, uint32_t aSize, VP9FrameHeader& aHeader) const; + void Commit(const VP9FrameHeader& aHeader); +private: + bool ParseInternal(const uint8_t*, uint32_t, VP9FrameHeader&); + void ParseLoopFilter(VP9BitReader&, VP9FrameHeader&); + void ParseQuantization(VP9BitReader&, VP9FrameHeader&); + void ParseSegmentation(VP9BitReader&, VP9FrameHeader&); + void ParseTileInfo(VP9BitReader&, VP9FrameHeader&); + VP9FrameHeader mPrevious; + uint32_t mRefFrameWidth[8]; + uint32_t mRefFrameHeight[8]; + int8_t mRefDeltas[4]; + int8_t mModeDeltas[2]; +}; + +bool VP9SplitSuperframe(const uint8_t* aData, uint32_t aSize, + uint32_t (&aOffsets)[8], uint32_t (&aSizes)[8], + uint32_t& aCount); + +} +#endif diff --git a/dom/media/platforms/wmf/WMFDecoderModule.cpp b/dom/media/platforms/wmf/WMFDecoderModule.cpp index 045977d2e6..64991b0e08 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.cpp +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -6,6 +6,7 @@ #include "WMF.h" #include "WMFDecoderModule.h" #include "WMFVideoMFTManager.h" +#include "WMFVP9MFTManager.h" #include "WMFAudioMFTManager.h" #include "MFTDecoder.h" #include "mozilla/DebugOnly.h" @@ -80,6 +81,60 @@ WMFDecoderModule::Startup() return mWMFInitialized ? NS_OK : NS_ERROR_FAILURE; } +already_AddRefed +WMFDecoderModule::CreateVP9Decoder(const CreateDecoderParams& aParams) +{ + if (!MediaPrefs::PDMWMFVP9DecoderEnabled() || !sDXVAEnabled) { + return nullptr; + } + + const bool mftAllowed = MediaPrefs::PDMWMFVP9MFTEnabled() && HasVP9MFT(); + const bool dxva2Allowed = + MediaPrefs::PDMWMFVP9DXVA2Enabled() && IsVistaOrLater(); + const bool dxva2First = + dxva2Allowed && (!mftAllowed || MediaPrefs::PDMWMFVP9DXVA2Preferred()); + + if (mftAllowed && !dxva2First) { + nsAutoPtr mft( + new WMFVideoMFTManager(aParams.VideoConfig(), + aParams.mKnowsCompositor, + aParams.mImageContainer, + sDXVAEnabled)); + if (mft->Init()) { + RefPtr decoder = new WMFMediaDataDecoder( + mft.forget(), aParams.mTaskQueue, aParams.mCallback); + return decoder.forget(); + } + } + + if (dxva2Allowed) { + nsAutoPtr vp9( + new WMFVP9MFTManager(aParams.VideoConfig(), + aParams.mKnowsCompositor, + aParams.mImageContainer)); + if (vp9->Init()) { + RefPtr decoder = new WMFMediaDataDecoder( + vp9.forget(), aParams.mTaskQueue, aParams.mCallback); + return decoder.forget(); + } + } + + if (mftAllowed && dxva2First) { + nsAutoPtr mft( + new WMFVideoMFTManager(aParams.VideoConfig(), + aParams.mKnowsCompositor, + aParams.mImageContainer, + sDXVAEnabled)); + if (mft->Init()) { + RefPtr decoder = new WMFMediaDataDecoder( + mft.forget(), aParams.mTaskQueue, aParams.mCallback); + return decoder.forget(); + } + } + + return nullptr; +} + already_AddRefed WMFDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) { @@ -91,6 +146,10 @@ WMFDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams) return nullptr; } + if (VPXDecoder::IsVP9(aParams.VideoConfig().mMimeType)) { + return CreateVP9Decoder(aParams); + } + nsAutoPtr manager( new WMFVideoMFTManager(aParams.VideoConfig(), aParams.mKnowsCompositor, @@ -192,11 +251,28 @@ WMFDecoderModule::HasH264() } /* static */ bool -WMFDecoderModule::HasVP9() +WMFDecoderModule::HasVP9MFT() { return CanCreateWMFDecoder(); } +/* static */ bool +WMFDecoderModule::HasVP9DXVA2(layers::KnowsCompositor* aKnowsCompositor) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!aKnowsCompositor || !IsVistaOrLater() || !sDXVAEnabled || + !MediaPrefs::PDMWMFVP9DecoderEnabled() || + !MediaPrefs::PDMWMFVP9DXVA2Enabled() || + !gfx::gfxVars::CanUseHardwareVideoDecoding()) { + return false; + } + + nsCString failureReason; + nsAutoPtr manager(WMFVideoMFTManager::CreateVP9DXVA( + aKnowsCompositor, failureReason, DXVAVP9Manager::GetVP9DecoderGUID())); + return !!manager; +} + /* static */ bool WMFDecoderModule::HasAAC() { @@ -254,8 +330,17 @@ WMFDecoderModule::Supports(const TrackInfo& aTrackInfo, return true; } if (MediaPrefs::PDMWMFVP9DecoderEnabled() && sDXVAEnabled) { - if ((VPXDecoder::IsVP8(aTrackInfo.mMimeType) || - VPXDecoder::IsVP9(aTrackInfo.mMimeType)) && + if (VPXDecoder::IsVP9(aTrackInfo.mMimeType)) { + const bool dxva2 = + MediaPrefs::PDMWMFVP9DXVA2Enabled() && IsVistaOrLater(); + const bool mft = + MediaPrefs::PDMWMFVP9MFTEnabled() && CanCreateWMFDecoder(); + if (dxva2 || mft) { + return true; + } + } + // vp8 needs mft, no dxva for it , useless anyways. + if (VPXDecoder::IsVP8(aTrackInfo.mMimeType) && CanCreateWMFDecoder()) { return true; } diff --git a/dom/media/platforms/wmf/WMFDecoderModule.h b/dom/media/platforms/wmf/WMFDecoderModule.h index 92b977031a..c3c8945cc4 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.h +++ b/dom/media/platforms/wmf/WMFDecoderModule.h @@ -10,6 +10,10 @@ namespace mozilla { +namespace layers { +class KnowsCompositor; +} + class WMFDecoderModule : public PlatformDecoderModule { public: WMFDecoderModule(); @@ -24,6 +28,9 @@ public: already_AddRefed CreateAudioDecoder(const CreateDecoderParams& aParams) override; + already_AddRefed + CreateVP9Decoder(const CreateDecoderParams& aParams); + bool SupportsMimeType(const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const override; bool Supports(const TrackInfo& aTrackInfo, @@ -43,7 +50,8 @@ public: // require a "Media Feature Pack" to be installed. static bool HasAAC(); static bool HasH264(); - static bool HasVP9(); + static bool HasVP9MFT(); + static bool HasVP9DXVA2(layers::KnowsCompositor* aKnowsCompositor); private: bool mWMFInitialized; diff --git a/dom/media/platforms/wmf/WMFVP9MFTManager.cpp b/dom/media/platforms/wmf/WMFVP9MFTManager.cpp new file mode 100644 index 0000000000..96cad1cb42 --- /dev/null +++ b/dom/media/platforms/wmf/WMFVP9MFTManager.cpp @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WMFVP9MFTManager.h" +#include "WMFVideoMFTManager.h" +#include "ImageContainer.h" +#include "Layers.h" +#include "VideoUtils.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/Logging.h" +#include "nsPrintfCString.h" + +namespace mozilla { + +struct VP9ShownFrame { + RefPtr mImage; + nsIntRect mPicture; + bool mKeyframe; +}; + +WMFVP9MFTManager::WMFVP9MFTManager(const VideoInfo& aConfig, + layers::KnowsCompositor* aCompositor, layers::ImageContainer* aContainer) + : mVideoInfo(aConfig), mKnowsCompositor(aCompositor), mImageContainer(aContainer), mIsValid(false) +{} +WMFVP9MFTManager::~WMFVP9MFTManager() { Shutdown(); } +bool WMFVP9MFTManager::Init() +{ + if (!IsVistaOrLater()) { + mFailureReason.AssignLiteral("VP9 DXVA2 requires Windows Vista or later"); + } else if (!gfx::gfxVars::CanUseHardwareVideoDecoding()) { + mFailureReason.AssignLiteral("Hardware video decoding is disabled or blocklisted"); + } else if (!mKnowsCompositor) { + mFailureReason.AssignLiteral("No compositor available for VP9 DXVA2"); + } else if (mVideoInfo.HasAlpha()) { + mFailureReason.AssignLiteral("VP9 DXVA2 cannot decode streams with alpha"); + } else if (mVideoInfo.mBitDepth != 8) { + mFailureReason = nsPrintfCString( + "VP9 DXVA2 supports profile 0 only; stream is %u-bit", mVideoInfo.mBitDepth); + } else if (mVideoInfo.mImage.width < 16 || mVideoInfo.mImage.height < 16 || + mVideoInfo.mImage.width > 8192 || mVideoInfo.mImage.height > 8192) { + mFailureReason = nsPrintfCString( + "VP9 DXVA2 does not accept a coded size of %dx%d", + mVideoInfo.mImage.width, mVideoInfo.mImage.height); + } + if (!mFailureReason.IsEmpty()) { + return false; + } + + auto backend = mKnowsCompositor->GetCompositorBackendType(); + if (backend != layers::LayersBackend::LAYERS_D3D9 && + backend != layers::LayersBackend::LAYERS_D3D11) { + mFailureReason = nsPrintfCString( + "VP9 DXVA2 needs a Direct3D compositor; layers backend is %d", int(backend)); + return false; + } + + mDXVA2Manager = WMFVideoMFTManager::CreateVP9DXVA(mKnowsCompositor, + mFailureReason, DXVAVP9Manager::GetVP9DecoderGUID()); + if (!mDXVA2Manager) { + if (mFailureReason.IsEmpty()) { + mFailureReason.AssignLiteral( + "Could not create a D3D9 DXVA2 device for the VP9 profile-0 GUID"); + } + return false; + } + + mVP9Decoder = new DXVAVP9Manager(); + if (!mVP9Decoder->Init(mDXVA2Manager->GetDXVADeviceManager(), + mVideoInfo.mImage.width, mVideoInfo.mImage.height, mFailureReason)) { + Shutdown(); + return false; + } + + mIsValid = true; + mFailureReason.AssignLiteral("Using VP9 DXVA2 profile 0"); + return true; +} +HRESULT WMFVP9MFTManager::Input(MediaRawData* aSample) +{ + if (!mIsValid) { return E_UNEXPECTED; } + if (!aSample || aSample->Size() > UINT32_MAX) { return E_INVALIDARG; } + CheckedInt sampleEnd = CheckedInt(aSample->mTime) + aSample->mDuration; + if (!sampleEnd.isValid() || aSample->mDuration < 0) { return E_INVALIDARG; } + uint32_t offsets[8], sizes[8], count; + if (!VP9SplitSuperframe(aSample->Data(), uint32_t(aSample->Size()), offsets, sizes, count)) { + return E_INVALIDARG; + } + AutoTArray shown; + for (uint32_t i = 0; i < count; ++i) { + RefPtr surface; + bool display = false; + VP9FrameHeader header; + HRESULT hr = mVP9Decoder->DecodeFrame(aSample->Data() + offsets[i], sizes[i], + getter_AddRefs(surface), &display, &header); + if (FAILED(hr)) { Flush(); return hr; } + if (!display) { continue; } + if (mSeekTargetThreshold.isSome() && + sampleEnd.value() < mSeekTargetThreshold.ref().ToMicroseconds()) { + continue; + } + nsIntRect picture = mVideoInfo.ScaledImageRect(header.frameWidth, header.frameHeight); + RefPtr image; + hr = mDXVA2Manager->CopySurfaceToImage(surface, picture, getter_AddRefs(image)); + if (FAILED(hr)) { Flush(); return hr; } + VP9ShownFrame* entry = shown.AppendElement(); + entry->mImage = image; + entry->mPicture = picture; + entry->mKeyframe = !header.showExistingFrame && header.frameType == 0; + } + if (shown.IsEmpty()) { return S_OK; } + mSeekTargetThreshold.reset(); + + const uint32_t shownCount = shown.Length(); + const int64_t slice = aSample->mDuration / shownCount; + for (uint32_t i = 0; i < shownCount; ++i) { + const int64_t time = aSample->mTime + int64_t(i) * slice; + const int64_t duration = (i + 1 == shownCount) + ? (aSample->mTime + aSample->mDuration - time) + : slice; + RefPtr frame = VideoData::CreateFromImage(mVideoInfo, + aSample->mOffset, time, duration, shown[i].mImage, + shown[i].mKeyframe, -1, shown[i].mPicture); + if (!frame) { return E_OUTOFMEMORY; } + mPendingFrames.AppendElement(frame); + } + return S_OK; +} +HRESULT WMFVP9MFTManager::Output(int64_t, RefPtr& aOutput) +{ + aOutput = nullptr; + if (mPendingFrames.IsEmpty()) { return MF_E_TRANSFORM_NEED_MORE_INPUT; } + aOutput = mPendingFrames[0]; + mPendingFrames.RemoveElementAt(0); + return S_OK; +} +void WMFVP9MFTManager::Flush() +{ + mPendingFrames.Clear(); + mSeekTargetThreshold.reset(); + if (mVP9Decoder) { mVP9Decoder->Flush(); } +} +void WMFVP9MFTManager::Shutdown() +{ + mPendingFrames.Clear(); + mVP9Decoder = nullptr; + if (mDXVA2Manager) { DeleteOnMainThread(mDXVA2Manager); } + mIsValid = false; +} +bool WMFVP9MFTManager::IsHardwareAccelerated(nsACString& aReason) const +{ + aReason = mFailureReason; + return mIsValid; +} +} diff --git a/dom/media/platforms/wmf/WMFVP9MFTManager.h b/dom/media/platforms/wmf/WMFVP9MFTManager.h new file mode 100644 index 0000000000..2f890f8f7d --- /dev/null +++ b/dom/media/platforms/wmf/WMFVP9MFTManager.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef WMFVP9MFTManager_h_ +#define WMFVP9MFTManager_h_ +#include "WMF.h" +#include "WMFMediaDataDecoder.h" +#include "DXVA2Manager.h" +#include "DXVAVP9Manager.h" +#include "MediaInfo.h" +#include "nsTArray.h" +namespace mozilla { +class WMFVP9MFTManager : public MFTManager { +public: + WMFVP9MFTManager(const VideoInfo&, layers::KnowsCompositor*, layers::ImageContainer*); + ~WMFVP9MFTManager(); + bool Init(); + HRESULT Input(MediaRawData*) override; + HRESULT Output(int64_t, RefPtr&) override; + void Flush() override; + void Drain() override {} + void Shutdown() override; + bool IsHardwareAccelerated(nsACString&) const override; + TrackInfo::TrackType GetType() override { return TrackInfo::kVideoTrack; } + const char* GetDescriptionName() const override { return "VP9 DXVA2 profile 0 hardware decoder"; } +private: + VideoInfo mVideoInfo; + RefPtr mKnowsCompositor; + RefPtr mImageContainer; + nsAutoPtr mDXVA2Manager; + nsAutoPtr mVP9Decoder; + nsTArray> mPendingFrames; + nsCString mFailureReason; + bool mIsValid; +}; +} +#endif diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index c04f301288..89f8830603 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -314,6 +314,28 @@ FindD3D9BlacklistedDLL() { "media.wmf.disable-d3d9-for-dlls"); } +DXVA2Manager* +WMFVideoMFTManager::CreateVP9DXVA(layers::KnowsCompositor* aCompositor, + nsACString& aFailureReason, const GUID& aGUID) +{ + DXVA2Manager* result = nullptr; + nsCOMPtr event = NS_NewRunnableFunction([&]() { + const nsCString& blocked = FindD3D9BlacklistedDLL(); + if (!blocked.IsEmpty()) { + aFailureReason.AssignLiteral("D3D9 DLL is blocklisted"); + return; + } + result = DXVA2Manager::CreateD3D9DXVA(aCompositor, aFailureReason, &aGUID); + }); + if (NS_IsMainThread()) { + event->Run(); + } else { + nsCOMPtr mainThread = do_GetMainThread(); + SyncRunnable::DispatchToThread(mainThread, event); + } + return result; +} + class CreateDXVAManagerEvent : public Runnable { public: CreateDXVAManagerEvent(LayersBackend aBackend, diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.h b/dom/media/platforms/wmf/WMFVideoMFTManager.h index 072b9fe797..6c749113c8 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.h +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h @@ -26,6 +26,8 @@ public: ~WMFVideoMFTManager(); bool Init(); + static DXVA2Manager* CreateVP9DXVA(layers::KnowsCompositor* aCompositor, + nsACString& aFailureReason, const GUID& aGUID); HRESULT Input(MediaRawData* aSample) override; diff --git a/dom/media/platforms/wmf/moz.build b/dom/media/platforms/wmf/moz.build index fef9335bd6..75b706c81a 100644 --- a/dom/media/platforms/wmf/moz.build +++ b/dom/media/platforms/wmf/moz.build @@ -5,13 +5,17 @@ EXPORTS += [ 'DXVA2Manager.h', + 'DXVAVP9Manager.h', 'MFTDecoder.h', + 'VP9DXVA.h', + 'VP9HeaderParser.h', 'WMF.h', 'WMFAudioMFTManager.h', 'WMFDecoderModule.h', 'WMFMediaDataDecoder.h', 'WMFUtils.h', 'WMFVideoMFTManager.h', + 'WMFVP9MFTManager.h', ] UNIFIED_SOURCES += [ @@ -24,7 +28,10 @@ UNIFIED_SOURCES += [ ] SOURCES += [ + 'DXVAVP9Manager.cpp', + 'VP9HeaderParser.cpp', 'WMFUtils.cpp', + 'WMFVP9MFTManager.cpp', ] include('/ipc/chromium/chromium-config.mozbuild') diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 3c86d48351..2bcf534e75 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -405,6 +405,9 @@ pref("media.wmf.decoder.thread-count", -1); pref("media.wmf.low-latency.enabled", false); pref("media.wmf.skip-blacklist", false); pref("media.wmf.vp9.enabled", true); +pref("media.wmf.vp9.dxva2.enabled", true); +pref("media.wmf.vp9.mft.enabled", true); +pref("media.wmf.vp9.dxva2.preferred", false); pref("media.windows-media-foundation.allow-d3d11-dxva", true); pref("media.wmf.disable-d3d11-for-dlls", "igd11dxva64.dll: 20.19.15.4463, 20.19.15.4454, 20.19.15.4444, 20.19.15.4416, 20.19.15.4404, 20.19.15.4390, 20.19.15.4380, 20.19.15.4377, 20.19.15.4364, 20.19.15.4360, 20.19.15.4352, 20.19.15.4331, 20.19.15.4326, 20.19.15.4300; igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.15.4248, 10.18.14.4112, 10.18.10.3958, 10.18.10.3496, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055, 9.18.10.3006; igd10umd32.dll: 9.17.10.4229, 9.17.10.3040, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 9.17.10.2857, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 22.21.13.8253, 22.21.13.8233, 22.21.13.8205, 22.21.13.8189, 22.21.13.8178, 22.21.13.8165, 21.21.13.7892, 21.21.13.7878, 21.21.13.7866, 21.21.13.7849, 21.21.13.7654, 21.21.13.7653, 21.21.13.7633, 21.21.13.7619, 21.21.13.7563, 21.21.13.7306, 21.21.13.7290, 21.21.13.7270, 21.21.13.7254, 21.21.13.6939, 21.21.13.6926, 21.21.13.6909, 21.21.13.4201, 21.21.13.4200, 10.18.13.6881, 10.18.13.6839, 10.18.13.6510, 10.18.13.6472, 10.18.13.6143, 10.18.13.5946, 10.18.13.5923, 10.18.13.5921, 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5445, 10.18.13.5382, 10.18.13.5362, 9.18.13.4788, 9.18.13.4752, 9.18.13.4725, 9.18.13.4709, 9.18.13.4195, 9.18.13.4192, 9.18.13.4144, 9.18.13.4052, 9.18.13.3788, 9.18.13.3523, 9.18.13.3235, 9.18.13.3165, 9.18.13.2723, 9.18.13.2702, 9.18.13.1422, 9.18.13.1407, 9.18.13.1106, 9.18.13.546; atidxx32.dll: 21.19.151.3, 21.19.142.257, 21.19.137.514, 21.19.137.1, 21.19.134.1, 21.19.128.7, 21.19.128.4, 20.19.0.32837, 20.19.0.32832, 8.17.10.682, 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.581, 8.17.10.569, 8.17.10.560, 8.17.10.545, 8.17.10.539, 8.17.10.531, 8.17.10.525, 8.17.10.520, 8.17.10.519, 8.17.10.514, 8.17.10.511, 8.17.10.494, 8.17.10.489, 8.17.10.483, 8.17.10.453, 8.17.10.451, 8.17.10.441, 8.17.10.436, 8.17.10.432, 8.17.10.425, 8.17.10.418, 8.17.10.414, 8.17.10.401, 8.17.10.395, 8.17.10.385, 8.17.10.378, 8.17.10.362, 8.17.10.355, 8.17.10.342, 8.17.10.331, 8.17.10.318, 8.17.10.310, 8.17.10.286, 8.17.10.269, 8.17.10.261, 8.17.10.247, 8.17.10.240, 8.15.10.212; atidxx64.dll: 21.19.151.3, 21.19.142.257, 21.19.137.514, 21.19.137.1, 21.19.134.1, 21.19.128.7, 21.19.128.4, 20.19.0.32832, 8.17.10.682, 8.17.10.661, 8.17.10.644, 8.17.10.625; nvumdshim.dll: 10.18.13.6822"); pref("media.wmf.disable-d3d9-for-dlls", "igdumd64.dll: 8.15.10.2189, 8.15.10.2119, 8.15.10.2104, 8.15.10.2102, 8.771.1.0; atiumd64.dll: 7.14.10.833, 7.14.10.867, 7.14.10.885, 7.14.10.903, 7.14.10.911, 8.14.10.768, 9.14.10.1001, 9.14.10.1017, 9.14.10.1080, 9.14.10.1128, 9.14.10.1162, 9.14.10.1171, 9.14.10.1183, 9.14.10.1197, 9.14.10.945, 9.14.10.972, 9.14.10.984, 9.14.10.996");