/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using layers::D3D11RecycleAllocator;
using layers::D3D11ShareHandleImage;
using layers::Image;
using layers::ImageContainer;
using namespace layers;
using namespace gfx;
// The D3D format is the first DWORD of the subtype GUID.
GUID subtype = GUID_NULL;
HRESULT hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
pDesc->Format = (D3DFORMAT)subtype.Data1;
staticconstchar* DecoderGUIDToStr(const GUID& aGuid) { if (aGuid == DXVA2_ModeH264_VLD_NoFGT) { return"H264";
} if (aGuid == DXVA2_Intel_ClearVideo_ModeH264_VLD_NoFGT) { return"Intel H264";
} if (aGuid == DXVA2_ModeVP8_VLD) { return"VP8";
} if (aGuid == DXVA2_ModeVP9_VLD_Profile0) { return"VP9 Profile0";
} if (aGuid == DXVA2_ModeVP9_VLD_10bit_Profile2) { return"VP9 10bits Profile2";
} if (aGuid == DXVA2_ModeAV1_VLD_Profile0) { return"AV1 Profile0";
} if (aGuid == DXVA2_ModeAV1_VLD_Profile1) { return"AV1 Profile1";
} if (aGuid == DXVA2_ModeAV1_VLD_Profile2) { return"AV1 Profile2";
} if (aGuid == DXVA2_ModeAV1_VLD_12bit_Profile2) { return"AV1 12bits Profile2";
} if (aGuid == DXVA2_ModeAV1_VLD_12bit_Profile2_420) { return"AV1 12bits Profile2 420";
} if (aGuid == DXVA2_ModeHEVC_VLD_MAIN) { return"HEVC main";
} if (aGuid == DXVA2_ModeHEVC_VLD_MAIN10) { return"HEVC main10";
} return"none";
}
// Count of the number of DXVAManager's we've created. This is also the // number of videos we're decoding with DXVA. Use on main thread only. static Atomic<uint32_t> sDXVAVideosCount(0);
// This class's functions are not thread-safe, please use them carefully. // TODO : make this class better in bug1932998. class D3D11DXVA2Manager : public DXVA2Manager {
public:
D3D11DXVA2Manager();
virtual ~D3D11DXVA2Manager();
// Copies a region (aRegion) of the video frame stored in aVideoSample // into an image which is returned by aOutImage.
HRESULT CopyToImage(IMFSample* aVideoSample, const gfx::IntRect& aRegion,
Image** aOutImage) override;
HRESULT CopyToImage(ID3D11Texture2D* aVideoSample, UINT aSurfaceIndex, const gfx::IntRect& aRegion,
layers::Image** aOutImage) override;
bool SupportsZeroCopyNV12Texture() override { // The maximum number of video frames for which the client can use with zero // video frame copy. If the usage count exceeds the maximum count, the zero // video frame copy is disabled to prevent run out of video frames at // decoder. The max count is set to prevent accidental disabling of the zero // video frame copy during normal video playback. During normal video // playback, ZeroCopyUsageInfo::GetRefCount() was around 5 to 8. The maximum // count is set sufficiently higher than the GetRefCount() during normal // video playback. constint maxVideoFrameUsageCount = 14;
// This is used for check whether hw decoding is possible before using MFT for // decoding. bool CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc) const;
if (aInfo.mExtraData && !aInfo.mExtraData->IsEmpty()) {
VPXDecoder::VPXStreamInfo vp9Info;
VPXDecoder::ReadVPCCBox(vp9Info, aInfo.mExtraData);
profile = vp9Info.mProfile;
} else { // If no vpcC is present, we can't know the profile, which limits the // subsampling mode, but 4:2:0 is most supported so default to profiles 0 // and 2: // Profile 0 = 8bit, 4:2:0 // Profile 2 = 10/12bit, 4:2:0
profile = aInfo.mColorDepth == ColorDepth::COLOR_8 ? 0 : 2;
}
if (layers::ImageBridgeChild::GetSingleton() || !aKnowsCompositor) { // There's no proper KnowsCompositor for ImageBridge currently (and it // implements the interface), so just use that if it's available.
mTextureClientAllocator = new D3D11RecycleAllocator(
layers::ImageBridgeChild::GetSingleton().get(), mDevice,
gfx::SurfaceFormat::NV12);
if (ImageBridgeChild::GetSingleton() &&
StaticPrefs::media_wmf_use_sync_texture_AtStartup() &&
mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) { // We use a syncobject to avoid the cost of the mutex lock when // compositing, and because it allows color conversion ocurring directly // from this texture DXVA does not seem to accept IDXGIKeyedMutex textures // as input.
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
layers::ImageBridgeChild::GetSingleton()
->GetTextureFactoryIdentifier()
.mSyncHandle,
mDevice);
}
} else {
mTextureClientAllocator = new D3D11RecycleAllocator(
aKnowsCompositor, mDevice, gfx::SurfaceFormat::NV12);
mKnowsCompositor = aKnowsCompositor; if (StaticPrefs::media_wmf_use_sync_texture_AtStartup()) { // We use a syncobject to avoid the cost of the mutex lock when // compositing, and because it allows color conversion ocurring directly // from this texture DXVA does not seem to accept IDXGIKeyedMutex textures // as input.
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
aKnowsCompositor->GetTextureFactoryIdentifier().mSyncHandle, mDevice);
}
}
mTextureClientAllocator->SetMaxPoolSize(5);
if (!SUCCEEDED(hr)) {
aFailureReason = nsPrintfCString( "MFTDecoder::Create of Video Processor MFT for color conversion " "failed with code %lX",
hr); return;
}
hr = mft->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER,
ULONG_PTR(mDXGIDeviceManager.get())); if (!SUCCEEDED(hr)) {
aFailureReason = nsPrintfCString( "MFTDecoder::SendMFTMessage(MFT_MESSAGE_" "SET_D3D_MANAGER) failed with code %lX",
hr); return;
}
});
if (!SUCCEEDED(hr)) { return hr;
}
mTransform = mft;
RefPtr<IDXGIDevice> dxgiDevice;
hr = mDevice->QueryInterface(
static_cast<IDXGIDevice**>(getter_AddRefs(dxgiDevice))); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("QI to IDXGIDevice failed with code %lX", hr); return hr;
}
RefPtr<IDXGIAdapter> adapter;
hr = dxgiDevice->GetAdapter(adapter.StartAssignment()); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("IDXGIDevice::GetAdapter failed with code %lX", hr); return hr;
}
DXGI_ADAPTER_DESC adapterDesc;
hr = adapter->GetDesc(&adapterDesc); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("IDXGIAdapter::GetDesc failed with code %lX", hr); return hr;
}
mVendorID = adapterDesc.VendorId;
if ((adapterDesc.VendorId == 0x1022 || adapterDesc.VendorId == 0x1002) &&
!StaticPrefs::media_wmf_skip_blacklist()) { for (constauto& model : sAMDPreUVD4) { if (adapterDesc.DeviceId == model) {
mIsAMDPreUVD4 = true; break;
}
}
}
// Retrieve the DXGI_FORMAT for the current video sample.
RefPtr<IMFMediaBuffer> buffer;
HRESULT hr = aVideoSample->GetBufferByIndex(0, getter_AddRefs(buffer));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
void D3D11DXVA2Manager::RefreshIMFSampleWrappers() { for (auto it = mIMFSampleWrappers.begin(); it != mIMFSampleWrappers.end();) { auto wrapper = RefPtr<IMFSampleWrapper>(*it); if (!wrapper) { // wrapper is already destroyed.
it = mIMFSampleWrappers.erase(it); continue;
}
it++;
}
}
void D3D11DXVA2Manager::ReleaseAllIMFSamples() { for (auto it = mIMFSampleWrappers.begin(); it != mIMFSampleWrappers.end();
it++) {
RefPtr<IMFSampleWrapper> wrapper = RefPtr<IMFSampleWrapper>(*it); if (wrapper) {
wrapper->ClearVideoSample();
}
}
}
// Glossary of RGB formats for display: // DXGI_FORMAT_B8G8R8A8_UNORM <-> MFVideoFormat_ARGB32 // DXGI_FORMAT_R16G16B16A16_FLOAT <-> MFVideoFormat_A16B16G16R16F // DXGI_FORMAT_R10G10B10A2_UNORM <-> MFVideoFormat_A2R10G10B10 // (Yes MFVideoFormat_A2R10G10B10 is actually R10G10B10A2) // // YUV formats for decoder output: // DXGI_FORMAT_NV12 <-> MFVideoFormat_NV12 // DXGI_FORMAT_YV12 <-> MFVideoFormat_YV12 // DXGI_FORMAT_P010 <-> MFVideoFormat_P010 // DXGI_FORMAT_P016 <-> MFVideoFormat_P016 // // Format pairings for SDR colorspaces: // {DXGI_FORMAT_NV12, DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709} // {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709} // // Format pairings for HDR colorspaces: // {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020} // {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709} // // A few notes: // * NV12 can be directly displayed as an overlay, whereas all other // formats need to be converted to RGB for display. // * YV12 is only used for SW decode (and we have no SurfaceFormat to represent // it, gfx::SurfaceFormat::YUV420 has a different plane order). // * P010 and P016 are for HDR video, testing these as an overlay showed that // they do not seem to honor the HDR G2084 transfer function so we can // consider them simply not displayable. // * If a video has alpha channel the decoder would have to output RGBA because // Windows has no enums for YUV with alpha, we don't currently support RGBA // output from decoders so alpha is not currently relevant. // * Windows seems to do some magic HDR tonemapping on SDR displays without even // telling it to do anything, it's not clear what we can rely on about this, // but testing in the wild is probably the best answer for now.
// Convert a Media Foundation subtype GUID to a gfx::SurfaceFormat. static gfx::SurfaceFormat SurfaceFormatFromSubType(const GUID& aSubType) { // We can't use a switch on subType because GUID is a struct. if (aSubType == MFVideoFormat_ARGB32) { return gfx::SurfaceFormat::B8G8R8A8;
} if (aSubType == MFVideoFormat_A16B16G16R16F) { return gfx::SurfaceFormat::R16G16B16A16F;
} if (aSubType == MFVideoFormat_A2R10G10B10) { return gfx::SurfaceFormat::R10G10B10X2_UINT32;
} if (aSubType == MFVideoFormat_NV12) { return gfx::SurfaceFormat::NV12;
} if (aSubType == MFVideoFormat_YUY2) { return gfx::SurfaceFormat::YUY2;
} if (aSubType == MFVideoFormat_P010) { return gfx::SurfaceFormat::P010;
} if (aSubType == MFVideoFormat_P016) { return gfx::SurfaceFormat::P016;
}
MOZ_ASSERT_UNREACHABLE("Unknown subtype"); return gfx::SurfaceFormat::UNKNOWN;
}
// The aInputType subType here is one of the following: // * MFVideoFormat_NV12 // * MFVideoFormat_YV12 // * MFVideoFormat_P010 // * MFVideoFormat_P016
if (subType == mInputSubType && aWidth == mWidth && aHeight == mHeight &&
mYUVColorSpace == aColorSpace && mColorRange == aColorRange &&
mColorDepth == aColorDepth && mTransferFunction == aTransferFunction &&
mHDRMetadata == aHDRMetadata) { // If the media type hasn't changed, don't reconfigure. return S_OK;
}
// Create a copy of our input type.
RefPtr<IMFMediaType> inputType;
hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = aInputType->CopyAllItems(inputType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
UINT configCount = 0;
hr = videoDevice->GetVideoDecoderConfigCount(&aDesc, &configCount); if (FAILED(hr)) {
LOG("Failed to get decoder config count!"); returnfalse;
}
for (UINT i = 0; i < configCount; i++) {
D3D11_VIDEO_DECODER_CONFIG config;
hr = videoDevice->GetVideoDecoderConfig(&aDesc, i, &config); if (SUCCEEDED(hr)) {
RefPtr<ID3D11VideoDecoder> decoder;
hr = videoDevice->CreateVideoDecoder(&aDesc, &config,
decoder.StartAssignment()); return decoder != nullptr;
}
} returnfalse;
}
/* static */
DXVA2Manager* DXVA2Manager::CreateD3D11DXVA(
layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason,
ID3D11Device* aDevice, DXVA2Usage aUsage) { // DXVA processing takes up a lot of GPU resources, so limit the number of // videos we use DXVA with at any one time.
uint32_t dxvaLimit = StaticPrefs::media_wmf_dxva_max_videos();
if (sDXVAVideosCount == dxvaLimit && aUsage == DXVA2Usage::Playback) {
aFailureReason.AssignLiteral("Too many DXVA videos playing"); return nullptr;
}
if (!image->AllocateTexture(mTextureClientAllocator, mDevice)) {
LOG("Failed to allocate texture!"); return E_FAIL;
}
RefPtr<TextureClient> client =
image->GetTextureClient(ImageBridgeChild::GetSingleton().get()); if (!client) {
LOG("Failed to get texture client!"); return E_FAIL;
}
UINT height = std::min(inDesc.Height, outDesc.Height);
PerformanceRecorder<PlaybackStage> perfRecorder(
MediaStage::CopyDecodedVideo, height); // The D3D11TextureClientAllocator may return a different texture format // than preferred. In which case the destination texture will be BGRA32. // Eg. when NV12 is blocked by Gfx. if (outDesc.Format == inDesc.Format) { // Our video frame is stored in a non-sharable ID3D11Texture2D. We need // to create a copy of that frame as a sharable resource, save its share // handle, and put that handle into the rendering pipeline.
UINT width = std::min(inDesc.Width, outDesc.Width);
D3D11_BOX srcBox = {0, 0, 0, width, height, 1};
mContext->CopySubresourceRegion(texture, 0, 0, 0, 0, aInTexture.mTexture,
aInTexture.mIndex, &srcBox);
} else { // Convert YUV to RGB. auto* processor = GetOrCreateVideoProcessor(); if (!processor) {
LOG("Failed to get a video processor"); return E_FAIL;
}
VideoProcessorD3D11::InputTextureInfo info(
ToColorSpace2(mYUVColorSpace), mColorRange, mTransferFunction,
aInTexture.mIndex, aInTexture.mTexture); if (!processor->CallVideoProcessorBlt(info, texture.get())) {
LOG("Failed on CallVideoProcessorBlt!"); return E_FAIL;
}
}
perfRecorder.Record();
}
auto* textureData = client->GetInternalData()->AsD3D11TextureData(); auto* fencesHolderMap = CompositeProcessD3D11FencesHolderMap::Get();
MOZ_ASSERT(textureData); constbool useFence =
textureData && textureData->mFencesHolderId.isSome() && fencesHolderMap; if (useFence) {
textureData->IncrementAndSignalWriteFence();
} elseif (!mutex &&
mDevice != DeviceManagerDx::Get()->GetCompositorDevice() &&
mSyncObject) { static StaticMutex sMutex MOZ_UNANNOTATED; // Ensure that we only ever attempt to synchronise via the sync object // serially as when using the same D3D11 device for multiple video decoders // it can lead to deadlocks.
StaticMutexAutoLock lock(sMutex); // It appears some race-condition may allow us to arrive here even when // mSyncObject is null. It's better to avoid that crash.
client->SyncWithObject(mSyncObject); if (!mSyncObject->Synchronize(true)) { return DXGI_ERROR_DEVICE_RESET;
}
}
image.forget(aOutImage); return S_OK;
}
VideoProcessorD3D11* D3D11DXVA2Manager::GetOrCreateVideoProcessor() { if (mProcessor) { return mProcessor;
}
mProcessor = VideoProcessorD3D11::Create(mDevice); if (!mProcessor) {
LOG("Failed to create video processor D3D11"); return nullptr;
}
HRESULT hr = mProcessor->Init(gfx::IntSize(mWidth, mHeight)); if (FAILED(hr)) {
mProcessor = nullptr;
LOG("Failed to init video processor D3D11, hr={:x}", hr);
} return mProcessor;
}
} // namespace mozilla
#undef LOG #undef LOGV
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.