This commit is contained in:
wuggy 2026-09-15 14:03:26 -07:00
commit f56f753c74
18 changed files with 1747 additions and 34 deletions

View file

@ -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<JS::Value> aP
}
#if defined(XP_WIN) && defined(MOZ_WMF)
nsCOMPtr<nsIWidget> 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());

View file

@ -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;

View file

@ -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

View file

@ -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<D3D9SurfaceImage> image = new D3D9SurfaceImage();
hr = image->AllocateAndCopy(mTextureClientAllocator, surface, aRegion);
HRESULT hr = image->AllocateAndCopy(mTextureClientAllocator, surface, aRegion);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
RefPtr<IDirect3DSurface9> 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<D3D9DXVA2Manager> 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) {

View file

@ -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; }

View file

@ -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 <algorithm>
#include <string.h>
// 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<IDirectXVideoDecoderService> 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<void**>(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<uint8_t*>(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

View file

@ -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<IDirect3DDeviceManager9> mDeviceManager;
RefPtr<IDirectXVideoDecoderService> mDecoderService;
RefPtr<IDirectXVideoDecoder> mDecoder;
static const uint32_t kMinSurfaces = 9;
static const uint32_t kDefaultSurfaces = 10;
static const uint32_t kMaxSurfaces = 24;
RefPtr<IDirect3DSurface9> 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

View file

@ -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 <stddef.h>
#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

View file

@ -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 <algorithm>
#include <string.h>
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;
}
}

View file

@ -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 <stdint.h>
#include <stddef.h>
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

View file

@ -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<MediaDataDecoder>
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<WMFVideoMFTManager> mft(
new WMFVideoMFTManager(aParams.VideoConfig(),
aParams.mKnowsCompositor,
aParams.mImageContainer,
sDXVAEnabled));
if (mft->Init()) {
RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(
mft.forget(), aParams.mTaskQueue, aParams.mCallback);
return decoder.forget();
}
}
if (dxva2Allowed) {
nsAutoPtr<WMFVP9MFTManager> vp9(
new WMFVP9MFTManager(aParams.VideoConfig(),
aParams.mKnowsCompositor,
aParams.mImageContainer));
if (vp9->Init()) {
RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(
vp9.forget(), aParams.mTaskQueue, aParams.mCallback);
return decoder.forget();
}
}
if (mftAllowed && dxva2First) {
nsAutoPtr<WMFVideoMFTManager> mft(
new WMFVideoMFTManager(aParams.VideoConfig(),
aParams.mKnowsCompositor,
aParams.mImageContainer,
sDXVAEnabled));
if (mft->Init()) {
RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(
mft.forget(), aParams.mTaskQueue, aParams.mCallback);
return decoder.forget();
}
}
return nullptr;
}
already_AddRefed<MediaDataDecoder>
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<WMFVideoMFTManager> manager(
new WMFVideoMFTManager(aParams.VideoConfig(),
aParams.mKnowsCompositor,
@ -192,11 +251,28 @@ WMFDecoderModule::HasH264()
}
/* static */ bool
WMFDecoderModule::HasVP9()
WMFDecoderModule::HasVP9MFT()
{
return CanCreateWMFDecoder<CLSID_WebmMfVpxDec>();
}
/* 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<DXVA2Manager> 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<CLSID_WebmMfVpxDec>();
if (dxva2 || mft) {
return true;
}
}
// vp8 needs mft, no dxva for it , useless anyways.
if (VPXDecoder::IsVP8(aTrackInfo.mMimeType) &&
CanCreateWMFDecoder<CLSID_WebmMfVpxDec>()) {
return true;
}

View file

@ -10,6 +10,10 @@
namespace mozilla {
namespace layers {
class KnowsCompositor;
}
class WMFDecoderModule : public PlatformDecoderModule {
public:
WMFDecoderModule();
@ -24,6 +28,9 @@ public:
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const CreateDecoderParams& aParams) override;
already_AddRefed<MediaDataDecoder>
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;

View file

@ -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<layers::Image> 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<int64_t> sampleEnd = CheckedInt<int64_t>(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<VP9ShownFrame, 2> shown;
for (uint32_t i = 0; i < count; ++i) {
RefPtr<IDirect3DSurface9> 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<layers::Image> 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<VideoData> 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<MediaData>& 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;
}
}

View file

@ -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<MediaData>&) 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<layers::KnowsCompositor> mKnowsCompositor;
RefPtr<layers::ImageContainer> mImageContainer;
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
nsAutoPtr<DXVAVP9Manager> mVP9Decoder;
nsTArray<RefPtr<MediaData>> mPendingFrames;
nsCString mFailureReason;
bool mIsValid;
};
}
#endif

View file

@ -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<nsIRunnable> 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<nsIThread> mainThread = do_GetMainThread();
SyncRunnable::DispatchToThread(mainThread, event);
}
return result;
}
class CreateDXVAManagerEvent : public Runnable {
public:
CreateDXVAManagerEvent(LayersBackend aBackend,

View file

@ -26,6 +26,8 @@ public:
~WMFVideoMFTManager();
bool Init();
static DXVA2Manager* CreateVP9DXVA(layers::KnowsCompositor* aCompositor,
nsACString& aFailureReason, const GUID& aGUID);
HRESULT Input(MediaRawData* aSample) override;

View file

@ -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')

View file

@ -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");