From 30c7ed831908a2bba27506ec0aa2228ab13ea253 Mon Sep 17 00:00:00 2001 From: wuggy Date: Mon, 14 Sep 2026 16:26:38 -0700 Subject: [PATCH 1/4] I fucked up pt.2 --- browser/components/webextensions/ext-browserAction.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/browser/components/webextensions/ext-browserAction.js b/browser/components/webextensions/ext-browserAction.js index 2c82ac701a..981885052d 100644 --- a/browser/components/webextensions/ext-browserAction.js +++ b/browser/components/webextensions/ext-browserAction.js @@ -140,6 +140,15 @@ BrowserAction.prototype = { }, }); + // Older CustomizableUI versions remember that an extension widget was + // seen, but do not auto-add removable widgets again when its saved + // placement is missing (for example after an upgrade or profile reset). + // Ensure browser actions remain discoverable in the main toolbar while + // respecting an existing user-selected placement. + if (!CustomizableUI.getPlacementOfWidget(this.id)) { + CustomizableUI.addWidgetToArea(this.id, CustomizableUI.AREA_NAVBAR); + } + this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners (evt, tab) => { this.updateWindow(tab.ownerGlobal); }); From 14148243206e8da96563f68d3817b98a00db2b6c Mon Sep 17 00:00:00 2001 From: EAZYBLACK Date: Tue, 15 Sep 2026 18:34:43 +0300 Subject: [PATCH 2/4] Add VP9 Hardware Decoding with DXVA (Non-MFT), Vista+ Thanks to Maggie (imjustsomeoneiguess) on discord for helping with a lot of this VP9 media code. This is the first browser so far to support VP9 Non MFT --- dom/base/nsDOMWindowUtils.cpp | 25 +- dom/interfaces/base/nsIDOMWindowUtils.idl | 9 +- dom/media/MediaPrefs.h | 3 + dom/media/platforms/wmf/DXVA2Manager.cpp | 39 +- dom/media/platforms/wmf/DXVA2Manager.h | 6 +- dom/media/platforms/wmf/DXVAVP9Manager.cpp | 590 ++++++++++++++++++ dom/media/platforms/wmf/DXVAVP9Manager.h | 82 +++ dom/media/platforms/wmf/VP9DXVA.h | 111 ++++ dom/media/platforms/wmf/VP9HeaderParser.cpp | 464 ++++++++++++++ dom/media/platforms/wmf/VP9HeaderParser.h | 120 ++++ dom/media/platforms/wmf/WMFDecoderModule.cpp | 91 ++- dom/media/platforms/wmf/WMFDecoderModule.h | 10 +- dom/media/platforms/wmf/WMFVP9MFTManager.cpp | 158 +++++ dom/media/platforms/wmf/WMFVP9MFTManager.h | 39 ++ .../platforms/wmf/WMFVideoMFTManager.cpp | 22 + dom/media/platforms/wmf/WMFVideoMFTManager.h | 2 + dom/media/platforms/wmf/moz.build | 7 + modules/libpref/init/all.js | 3 + 18 files changed, 1747 insertions(+), 34 deletions(-) create mode 100644 dom/media/platforms/wmf/DXVAVP9Manager.cpp create mode 100644 dom/media/platforms/wmf/DXVAVP9Manager.h create mode 100644 dom/media/platforms/wmf/VP9DXVA.h create mode 100644 dom/media/platforms/wmf/VP9HeaderParser.cpp create mode 100644 dom/media/platforms/wmf/VP9HeaderParser.h create mode 100644 dom/media/platforms/wmf/WMFVP9MFTManager.cpp create mode 100644 dom/media/platforms/wmf/WMFVP9MFTManager.h 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"); From de4d9aa091dcd7b5eac3d112aea86a8cfdb8be04 Mon Sep 17 00:00:00 2001 From: wuggy Date: Tue, 15 Sep 2026 14:03:09 -0700 Subject: [PATCH 3/4] Revert "fix ubO pt.5" This reverts commit ae54ac8e28f0da4299e4307c111b73d5f8ce1aa4. --- .../webextensions/schemas/context_menus.json | 5 +-- toolkit/components/webextensions/ext-dns.js | 41 ------------------- .../webextensions/extensions-toolkit.manifest | 1 - .../components/webextensions/schemas/dns.json | 4 -- toolkit/modules/addons/MatchPattern.jsm | 4 +- 5 files changed, 3 insertions(+), 52 deletions(-) delete mode 100644 toolkit/components/webextensions/ext-dns.js delete mode 100644 toolkit/components/webextensions/schemas/dns.json diff --git a/browser/components/webextensions/schemas/context_menus.json b/browser/components/webextensions/schemas/context_menus.json index 7f9702fa55..b31af51f3f 100644 --- a/browser/components/webextensions/schemas/context_menus.json +++ b/browser/components/webextensions/schemas/context_menus.json @@ -11,8 +11,7 @@ "choices": [{ "type": "string", "enum": [ - "contextMenus", - "menus" + "contextMenus" ] }] } @@ -21,7 +20,7 @@ { "namespace": "contextMenus", "description": "Use the browser.contextMenus API to add items to the browser's context menu. You can choose what types of objects your context menu additions apply to, such as images, hyperlinks, and pages.", - "permissions": ["contextMenus", "menus"], + "permissions": ["contextMenus"], "properties": { "ACTION_MENU_TOP_LEVEL_LIMIT": { "value": 6, diff --git a/toolkit/components/webextensions/ext-dns.js b/toolkit/components/webextensions/ext-dns.js deleted file mode 100644 index b131d0b6e5..0000000000 --- a/toolkit/components/webextensions/ext-dns.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; - -var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -function dnsResolve(hostname, flags = []) { - let mask = 0; - for (let flag of flags) { - switch (flag) { - case "bypass_cache": mask |= Ci.nsIDNSService.RESOLVE_BYPASS_CACHE; break; - case "canonical_name": mask |= Ci.nsIDNSService.RESOLVE_CANONICAL_NAME; break; - case "disable_ipv4": mask |= Ci.nsIDNSService.RESOLVE_DISABLE_IPV4; break; - case "disable_ipv6": mask |= Ci.nsIDNSService.RESOLVE_DISABLE_IPV6; break; - case "offline": mask |= Ci.nsIDNSService.RESOLVE_OFFLINE; break; - case "priority_low": mask |= Ci.nsIDNSService.RESOLVE_PRIORITY_LOW; break; - case "priority_medium": mask |= Ci.nsIDNSService.RESOLVE_PRIORITY_MEDIUM; break; - case "allow_name_collisions": mask |= Ci.nsIDNSService.RESOLVE_ALLOW_NAME_COLLISION; break; - } - } - let dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService); - return new Promise((resolve, reject) => { - let listener = { - onLookupComplete(request, record, status) { - if (Components.isSuccessCode(status)) { - let addresses = []; - try { record.rewind(); while (record.hasMore()) addresses.push(record.getNextAddrAsString()); } catch (e) {} - resolve({addresses, canonicalName: record.canonicalName || undefined, isTRR: false}); - } else { - reject(Components.Exception("DNS resolution failed", status)); - } - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSListener]), - }; - dns.asyncResolve(hostname, mask, listener, Services.tm.mainThread); - }); -} - -extensions.registerSchemaAPI("dns", "addon_parent", () => ({ - dns: { resolve: dnsResolve }, -})); diff --git a/toolkit/components/webextensions/extensions-toolkit.manifest b/toolkit/components/webextensions/extensions-toolkit.manifest index a86c6778a6..4ec65a9844 100644 --- a/toolkit/components/webextensions/extensions-toolkit.manifest +++ b/toolkit/components/webextensions/extensions-toolkit.manifest @@ -47,4 +47,3 @@ category webextension-schemas test chrome://extensions/content/schemas/test.json category webextension-schemas top_sites chrome://extensions/content/schemas/top_sites.json category webextension-schemas web_navigation chrome://extensions/content/schemas/web_navigation.json category webextension-schemas web_request chrome://extensions/content/schemas/web_request.json -category webextension-schemas dns chrome://extensions/content/schemas/dns.json diff --git a/toolkit/components/webextensions/schemas/dns.json b/toolkit/components/webextensions/schemas/dns.json deleted file mode 100644 index 7484656e59..0000000000 --- a/toolkit/components/webextensions/schemas/dns.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"namespace":"manifest","types":[{"$extend":"Permission","choices":[{"type":"string","enum":["dns"]}]}]}, - {"namespace":"dns","permissions":["dns"],"types":[{"id":"DNSRecord","type":"object","properties":{"addresses":{"type":"array","items":{"type":"string"}},"canonicalName":{"type":"string","optional":true},"isTRR":{"type":"boolean"}}}],"functions":[{"name":"resolve","type":"function","async":"promise","parameters":[{"name":"hostname","type":"string"},{"name":"flags","type":"array","items":{"type":"string"},"optional":true}],"returns":{"$ref":"DNSRecord"}}]} -] diff --git a/toolkit/modules/addons/MatchPattern.jsm b/toolkit/modules/addons/MatchPattern.jsm index fd46e1e786..4dff81fd23 100644 --- a/toolkit/modules/addons/MatchPattern.jsm +++ b/toolkit/modules/addons/MatchPattern.jsm @@ -18,9 +18,7 @@ this.EXPORTED_SYMBOLS = ["MatchPattern", "MatchGlobs", "MatchURLFilters"]; /* globals MatchPattern, MatchGlobs */ -// WebExtensions also use WebSocket URLs for request filtering and -// moz-extension URLs internally when validating web-accessible resources. -const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "data", "ws", "wss", "moz-extension"]; +const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "data"]; const PERMITTED_SCHEMES_REGEXP = PERMITTED_SCHEMES.join("|"); // This function converts a glob pattern (containing * and possibly ? From 9230d4f43693c1ab29e12dec0d0d0f4adc12ba38 Mon Sep 17 00:00:00 2001 From: wuggy Date: Tue, 15 Sep 2026 14:15:53 -0700 Subject: [PATCH 4/4] Add YouTube codec check and their settings --- .../preferences/in-content/content.xul | 20 +++++ .../youtube-codec-check.user.js | 77 +++++++++++++++++++ .../components/internaluserscripts.js | 48 ++++++++++++ .../preferences/internaluserscripts.js | 3 + browser/internaluserscripts/moz.build | 1 + .../chrome/browser/preferences/content.dtd | 8 ++ 6 files changed, 157 insertions(+) create mode 100644 browser/internaluserscripts/bundled-scripts/youtube-codec-check.user.js diff --git a/browser/components/preferences/in-content/content.xul b/browser/components/preferences/in-content/content.xul index 0cfdc424bf..07f9de0409 100644 --- a/browser/components/preferences/in-content/content.xul +++ b/browser/components/preferences/in-content/content.xul @@ -68,8 +68,28 @@ + #endif + + +