Files
palemoon27/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
T
roytam1 faafb5fd9d import changes from `dev' branch of rmottola/Arctic-Fox:
- As suggested in PR 101, use OpenBSD assembler files. Update the NetBSD on them and use .S instead of .s, to indicate files to process (or preprocessor would fail on comments). (6a17dbacc3)
- Bug 1229769 - Expose Promise interface to WorkerDebugger #ifdef SPIDERMONKEY_PROMISE;r=bz (da9e838c23)
- Bug 1155969 - Make xpt.py flake8 compliant. r=ted (84f8eab5a3)
- Bug 977464 - Always relink XPT files for all changed XPIDL interfaces without requiring the IID to be revved; r=khuey (9b22512c41)
- Bug 977464 follow-up: Fix the indentation to use 4 spaces (bd68a8ebc3)
- Bug 1240053 - Consider the order of methods, their params, and constant important when comparing XPT interfaces to decide whether to relink XPT files; r=khuey (b9253dd183)
- Bug 1264377. Get rid of some unnecessary custom JSClass hook functions in xpconnect sandboxes and DOM simple globals. r=bholley (60950b416b)
- Bug 1258496 - Purge message manager cached scripts on 'message-manager-flush-caches' notification. r=smaug (028b229d02)
- Bug 1251298 - Null out |*idp| when necessary in DoInterfaceDescriptor. r=khuey. (dbdd15dae8)
- Bug 659625 - part1: implement Console::clear in dom/base/Console.cpp;r=baku (17c4b33789)
- Bug 659625 - part2: implement console.clear in devtools webconsole;r=bgrins (b72c6173ee)
- Bug 1248507 - p5. DecoderDoctorDiagnostics implementation - r=jya,bz (22f68130af)
- Bug 1248507 - p6. Minimal notification definition - r=bz (02f3eeb2f9)
- Bug 1248507 - p7. Notify decoder-doctor-notification listeners - r=jya,bz (2c2eb33388)
- Bug 1248507 - p8. FFMpeg checks: Console message - r=bz (50a993c143)
- Bug 1248507 - p9. FFMpeg checks: Notification definition - r=bz (0bcdcc090c)
- Bug 1248507 - p10. Detect and report when FFMpeg/Linux fails to load - r=jya (28137efda0)
- Bug 1190939: Decode VP9 4:4:4 properly. r=jya (98508bb48b)
- Bug 1232911 - [1.2] Allow to test for specific VPX MIME type version. r=cpearce (1b53e02981)
- Bug 1251887 - Add break to unintentional switch fallthrough in GfxInfoBase.cpp to fix -Wimplicit-fallthrough warning. r=milan (9969a7bec7)
- Bug 1232911 - [2.2] Add VPX decoding blocking support. r=snorp (fa860a9d4d)
- Bug 1249777: Added support for 10.11 in the blocklisting code as well. r=mstange (479f629083)
- Bug 1242084 - Fix GfxInfoBase nsStringBuffer leak. r=dvander (87b38ee72d)
- Bug 1222201: Only use container calculated dimensions. r=cpearce (693ebdf450)
- Bug 1190240 - Cannot compile WMFVideoMFTManager.cpp using Windows 10 SDK. r=cpearce (8ee2e315f5)
- Bug 1248496 - Enable D3D11 DXVA. r=ajones (a79df0baf2)
- Bug 1248496 - Show which DXVA API is being used in about:support. r=jya (1f6b1f0c8e)
- Bug 1257028 - Fallback to d3d9 decoding if d3d11 fails. r=cpearce (5ad7c159f1)
- Bug 1232045 - WebMDemuxer handles resolution changes. r=jya (18bdc79b1c)
- Bug 1243538: P1. Make MediaInfo::mImage an nsIntSize again and introduce a mImageRect member. r=mattwoodrow (a446cca01e)
- Bug 1243538: P2. Add convenience VideoInfo::ScaledImageRect. r=mattwoodrow (657e675b72)
- Bug 1243538: P3. Adjust libvpx decoder to allow different decoding size from metadata. r=mattwoodrow (50949ce02d)
- Bug 1243538: P4. Adjust ffvpx decoder to allow different decoding size from metadata. r=mattwoodrow (392c8939f5)
- Bug 1243538: P5. Adjust wmf decoder to allow different decoding size from metadata. r=cpearce (f50940564f)
- Bug 1239611 - Remove GonkNativeWindowClient r=nical (2c7ccb54a4)
- Bug 1170589 - Force decoder to use all allocated buffers. r=bwu (7e5c02e48a)
- Bug 1222923 - Enable MOZ_FMP4 on gonk L r=jolin (c04ad6ff55)
- Bug 1178214 - Return INIT_ERROR when video resolution exceeds hw codec capability. r=sotaro (bf3c45cde1)
- Bug 1147304 - Send codec specific data for MPEG4 codec type only. r=jya (ca48d110f4)
- Bug 1243538: P6. Adjust gonk decoder to allow different decoding size from metadata. r=alfredo (257e017762)
- Bug 1243538: [webm] P7. Let the decoder handle picture resizing. r=SingingTree (32dc4a5aac)
- Bug 1262727: [webm] Ensure first frame returned after seek is a keyframe. r=kinetik (f16140852a)
- Bug 1246536: [webm] Only use discard padding information on last packet. r=kinetik (0bac4f8855)
- Bug 1266013: Fix Firefox OS compile errors. r=gerald (f021717287)
- cleanup (390cdec6ee)
- Bug 1264991: Don't construct invalid channel configuration. r=gerald (661828e8b8)
- Bug 1265093: Fix CID 1358648. r=gerald (55468c1261)
- Bug 1262659 - Report HTTP Live Streaming playback requests. r=cpearce,bsmedberg (96b8cd2810)
- Bug 1265400 - Use unsigned long for AudioBuffer length and numberOfChannels; r=smaug (f74f27ea4e)
2024-05-18 23:12:44 +08:00

692 lines
22 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 <algorithm>
#include "WMFVideoMFTManager.h"
#include "MediaDecoderReader.h"
#include "WMFUtils.h"
#include "ImageContainer.h"
#include "VideoUtils.h"
#include "DXVA2Manager.h"
#include "nsThreadUtils.h"
#include "Layers.h"
#include "mozilla/layers/LayersTypes.h"
#include "MediaInfo.h"
#include "mozilla/Logging.h"
#include "gfx2DGlue.h"
#include "gfxWindowsPlatform.h"
#include "IMFYCbCrImage.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "nsPrintfCString.h"
#include "MediaTelemetryConstants.h"
extern mozilla::LogModule* GetPDMLog();
#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
using mozilla::layers::Image;
using mozilla::layers::IMFYCbCrImage;
using mozilla::layers::LayerManager;
using mozilla::layers::LayersBackend;
#if MOZ_WINSDK_MAXVER < 0x0A000000
// Windows 10+ SDK has VP80 and VP90 defines
const GUID MFVideoFormat_VP80 =
{
0x30385056,
0x0000,
0x0010,
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
};
const GUID MFVideoFormat_VP90 =
{
0x30395056,
0x0000,
0x0010,
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
};
#endif
const CLSID CLSID_WebmMfVp8Dec =
{
0x451e3cb7,
0x2622,
0x4ba5,
{0x8e, 0x1d, 0x44, 0xb3, 0xc4, 0x1d, 0x09, 0x24}
};
const CLSID CLSID_WebmMfVp9Dec =
{
0x7ab4bd2,
0x1979,
0x4fcd,
{0xa6, 0x97, 0xdf, 0x9a, 0xd1, 0x5b, 0x34, 0xfe}
};
namespace mozilla {
WMFVideoMFTManager::WMFVideoMFTManager(
const VideoInfo& aConfig,
mozilla::layers::LayersBackend aLayersBackend,
mozilla::layers::ImageContainer* aImageContainer,
bool aDXVAEnabled)
: mVideoInfo(aConfig)
, mVideoStride(0)
, mImageSize(aConfig.mImage)
, mImageContainer(aImageContainer)
, mDXVAEnabled(aDXVAEnabled)
, mLayersBackend(aLayersBackend)
, mNullOutputCount(0)
, mGotValidOutputAfterNullOutput(false)
, mGotExcessiveNullOutput(false)
// mVideoStride, mVideoWidth, mVideoHeight, mUseHwAccel are initialized in
// Init().
{
MOZ_COUNT_CTOR(WMFVideoMFTManager);
// Need additional checks/params to check vp8/vp9
if (aConfig.mMimeType.EqualsLiteral("video/mp4") ||
aConfig.mMimeType.EqualsLiteral("video/avc")) {
mStreamType = H264;
} else if (aConfig.mMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
mStreamType = VP8;
} else if (aConfig.mMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
mStreamType = VP9;
} else {
mStreamType = Unknown;
}
}
WMFVideoMFTManager::~WMFVideoMFTManager()
{
MOZ_COUNT_DTOR(WMFVideoMFTManager);
// Ensure DXVA/D3D9 related objects are released on the main thread.
if (mDXVA2Manager) {
DeleteOnMainThread(mDXVA2Manager);
}
// Record whether the video decoder successfully decoded, or output null
// samples but did/didn't recover.
uint32_t telemetry = (mNullOutputCount == 0) ? 0 :
(mGotValidOutputAfterNullOutput && mGotExcessiveNullOutput) ? 1 :
mGotExcessiveNullOutput ? 2 :
mGotValidOutputAfterNullOutput ? 3 :
4;
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
LOG(nsPrintfCString("Reporting telemetry VIDEO_MFT_OUTPUT_NULL_SAMPLES=%d", telemetry).get());
Telemetry::Accumulate(Telemetry::ID::VIDEO_MFT_OUTPUT_NULL_SAMPLES, telemetry);
});
AbstractThread::MainThread()->Dispatch(task.forget());
}
const GUID&
WMFVideoMFTManager::GetMFTGUID()
{
MOZ_ASSERT(mStreamType != Unknown);
switch (mStreamType) {
case H264: return CLSID_CMSH264DecoderMFT;
case VP8: return CLSID_WebmMfVp8Dec;
case VP9: return CLSID_WebmMfVp9Dec;
default: return GUID_NULL;
};
}
const GUID&
WMFVideoMFTManager::GetMediaSubtypeGUID()
{
MOZ_ASSERT(mStreamType != Unknown);
switch (mStreamType) {
case H264: return MFVideoFormat_H264;
case VP8: return MFVideoFormat_VP80;
case VP9: return MFVideoFormat_VP90;
default: return GUID_NULL;
};
}
class CreateDXVAManagerEvent : public nsRunnable {
public:
CreateDXVAManagerEvent(LayersBackend aBackend, nsCString& aFailureReason)
: mBackend(aBackend)
, mFailureReason(aFailureReason)
{}
NS_IMETHOD Run() {
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
nsACString* failureReason = &mFailureReason;
nsCString secondFailureReason;
if (mBackend == LayersBackend::LAYERS_D3D11 &&
Preferences::GetBool("media.windows-media-foundation.allow-d3d11-dxva", true) &&
IsWin8OrLater()) {
mDXVA2Manager = DXVA2Manager::CreateD3D11DXVA(*failureReason);
if (mDXVA2Manager) {
return NS_OK;
}
// Try again with d3d9, but record the failure reason
// into a new var to avoid overwriting the d3d11 failure.
failureReason = &secondFailureReason;
mFailureReason.Append(NS_LITERAL_CSTRING("; "));
}
mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(*failureReason);
// Make sure we include the messages from both attempts (if applicable).
mFailureReason.Append(secondFailureReason);
return NS_OK;
}
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
LayersBackend mBackend;
nsACString& mFailureReason;
};
bool
WMFVideoMFTManager::InitializeDXVA(bool aForceD3D9)
{
// If we use DXVA but aren't running with a D3D layer manager then the
// readback of decoded video frames from GPU to CPU memory grinds painting
// to a halt, and makes playback performance *worse*.
if (!mDXVAEnabled) {
mDXVAFailureReason.AssignLiteral("Hardware video decoding disabled or blacklisted");
return false;
}
MOZ_ASSERT(!mDXVA2Manager);
if (mLayersBackend != LayersBackend::LAYERS_D3D9 &&
mLayersBackend != LayersBackend::LAYERS_D3D11) {
mDXVAFailureReason.AssignLiteral("Unsupported layers backend");
return false;
}
// The DXVA manager must be created on the main thread.
RefPtr<CreateDXVAManagerEvent> event =
new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend, mDXVAFailureReason);
if (NS_IsMainThread()) {
event->Run();
} else {
NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
}
mDXVA2Manager = event->mDXVA2Manager;
return mDXVA2Manager != nullptr;
}
bool
WMFVideoMFTManager::Init()
{
bool success = InitInternal(/* aForceD3D9 = */ false);
if (success && mDXVA2Manager) {
// If we had some failures but eventually made it work,
// make sure we preserve the messages.
if (mDXVA2Manager->IsD3D11()) {
mDXVAFailureReason.Append(NS_LITERAL_CSTRING("Using D3D11 API"));
} else {
mDXVAFailureReason.Append(NS_LITERAL_CSTRING("Using D3D9 API"));
}
}
return success;
}
bool
WMFVideoMFTManager::InitInternal(bool aForceD3D9)
{
mUseHwAccel = false; // default value; changed if D3D setup succeeds.
bool useDxva = InitializeDXVA(aForceD3D9);
RefPtr<MFTDecoder> decoder(new MFTDecoder());
HRESULT hr = decoder->Create(GetMFTGUID());
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
RefPtr<IMFAttributes> attr(decoder->GetAttributes());
UINT32 aware = 0;
if (attr) {
attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
WMFDecoderModule::GetNumDecoderThreads());
if (WMFDecoderModule::LowLatencyMFTEnabled()) {
hr = attr->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);
if (SUCCEEDED(hr)) {
LOG("Enabling Low Latency Mode");
} else {
LOG("Couldn't enable Low Latency Mode");
}
}
}
if (useDxva) {
if (aware) {
// TODO: Test if I need this anywhere... Maybe on Vista?
//hr = attr->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
//NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
MOZ_ASSERT(mDXVA2Manager);
ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
hr = decoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
if (SUCCEEDED(hr)) {
mUseHwAccel = true;
} else {
mDXVA2Manager = nullptr;
mDXVAFailureReason = nsPrintfCString("MFT_MESSAGE_SET_D3D_MANAGER failed with code %X", hr);
}
}
else {
mDXVAFailureReason.AssignLiteral("Decoder returned false for MF_SA_D3D_AWARE");
}
}
mDecoder = decoder;
hr = SetDecoderMediaTypes();
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
LOG("Video Decoder initialized, Using DXVA: %s", (mUseHwAccel ? "Yes" : "No"));
return true;
}
HRESULT
WMFVideoMFTManager::SetDecoderMediaTypes()
{
// Setup the input/output media types.
RefPtr<IMFMediaType> inputType;
HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = inputType->SetGUID(MF_MT_SUBTYPE, GetMediaSubtypeGUID());
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = inputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
RefPtr<IMFMediaType> outputType;
hr = wmf::MFCreateMediaType(getter_AddRefs(outputType));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
GUID outputSubType = mUseHwAccel ? MFVideoFormat_NV12 : MFVideoFormat_YV12;
hr = outputType->SetGUID(MF_MT_SUBTYPE, outputSubType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
return mDecoder->SetMediaTypes(inputType, outputType);
}
HRESULT
WMFVideoMFTManager::Input(MediaRawData* aSample)
{
if (!mDecoder) {
// This can happen during shutdown.
return E_FAIL;
}
HRESULT hr = mDecoder->CreateInputSample(aSample->Data(),
uint32_t(aSample->Size()),
aSample->mTime,
&mLastInput);
NS_ENSURE_TRUE(SUCCEEDED(hr) && mLastInput != nullptr, hr);
mLastDuration = aSample->mDuration;
// Forward sample data to the decoder.
return mDecoder->Input(mLastInput);
}
// The MFTransform we use for decoding h264 video will silently fall
// back to software decoding (even if we've negotiated DXVA) if the GPU
// doesn't support decoding the given resolution. It will then upload
// the software decoded frames into d3d textures to preserve behaviour.
//
// Unfortunately this seems to cause corruption (see bug 1193547) and is
// slow because the upload is done into a non-shareable texture and requires
// us to copy it.
//
// This code tests if the given resolution can be supported directly on the GPU,
// and makes sure we only ask the MFT for DXVA if it can be supported properly.
bool
WMFVideoMFTManager::CanUseDXVA(IMFMediaType* aType)
{
MOZ_ASSERT(mDXVA2Manager);
// SupportsConfig only checks for valid h264 decoders currently.
if (mStreamType != H264) {
return true;
}
// Assume the current samples duration is representative for the
// entire video.
float framerate = 1000000.0 / mLastDuration;
return mDXVA2Manager->SupportsConfig(aType, framerate);
}
HRESULT
WMFVideoMFTManager::ConfigureVideoFrameGeometry()
{
RefPtr<IMFMediaType> mediaType;
HRESULT hr = mDecoder->GetOutputMediaType(mediaType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// If we enabled/disabled DXVA in response to a resolution
// change then we need to renegotiate our media types,
// and resubmit our previous frame (since the MFT appears
// to lose it otherwise).
if (mUseHwAccel && !CanUseDXVA(mediaType)) {
mDXVAEnabled = false;
if (!Init()) {
return E_FAIL;
}
mDecoder->Input(mLastInput);
return S_OK;
}
// Verify that the video subtype is what we expect it to be.
// When using hardware acceleration/DXVA2 the video format should
// be NV12, which is DXVA2's preferred format. For software decoding
// we use YV12, as that's easier for us to stick into our rendering
// pipeline than NV12. NV12 has interleaved UV samples, whereas YV12
// is a planar format.
GUID videoFormat;
hr = mediaType->GetGUID(MF_MT_SUBTYPE, &videoFormat);
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL);
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL);
nsIntRect pictureRegion;
hr = GetPictureRegion(mediaType, pictureRegion);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
UINT32 width = pictureRegion.width;
UINT32 height = pictureRegion.height;
mImageSize = nsIntSize(width, height);
// Calculate and validate the picture region and frame dimensions after
// scaling by the pixel aspect ratio.
pictureRegion = mVideoInfo.ScaledImageRect(width, height);
if (!IsValidVideoRegion(mImageSize, pictureRegion, mVideoInfo.mDisplay)) {
// Video track's frame sizes will overflow. Ignore the video track.
return E_FAIL;
}
if (mDXVA2Manager) {
hr = mDXVA2Manager->ConfigureForSize(width, height);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
}
// Success! Save state.
GetDefaultStride(mediaType, width, &mVideoStride);
LOG("WMFVideoMFTManager frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d)",
width, height,
mVideoStride,
pictureRegion.x, pictureRegion.y, pictureRegion.width, pictureRegion.height,
mVideoInfo.mDisplay.width, mVideoInfo.mDisplay.height);
return S_OK;
}
HRESULT
WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
int64_t aStreamOffset,
VideoData** aOutVideoData)
{
NS_ENSURE_TRUE(aSample, E_POINTER);
NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
*aOutVideoData = nullptr;
HRESULT hr;
RefPtr<IMFMediaBuffer> buffer;
// Must convert to contiguous buffer to use IMD2DBuffer interface.
hr = aSample->ConvertToContiguousBuffer(getter_AddRefs(buffer));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Try and use the IMF2DBuffer interface if available, otherwise fallback
// to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
// but only some systems (Windows 8?) support it.
BYTE* data = nullptr;
LONG stride = 0;
RefPtr<IMF2DBuffer> twoDBuffer;
hr = buffer->QueryInterface(static_cast<IMF2DBuffer**>(getter_AddRefs(twoDBuffer)));
if (SUCCEEDED(hr)) {
hr = twoDBuffer->Lock2D(&data, &stride);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
} else {
hr = buffer->Lock(&data, nullptr, nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
stride = mVideoStride;
}
// YV12, planar format: [YYYY....][VVVV....][UUUU....]
// i.e., Y, then V, then U.
VideoData::YCbCrBuffer b;
uint32_t videoWidth = mImageSize.width;
uint32_t videoHeight = mImageSize.height;
// Y (Y') plane
b.mPlanes[0].mData = data;
b.mPlanes[0].mStride = stride;
b.mPlanes[0].mHeight = videoHeight;
b.mPlanes[0].mWidth = videoWidth;
b.mPlanes[0].mOffset = 0;
b.mPlanes[0].mSkip = 0;
// The V and U planes are stored 16-row-aligned, so we need to add padding
// to the row heights to ensure the Y'CbCr planes are referenced properly.
uint32_t padding = 0;
if (videoHeight % 16 != 0) {
padding = 16 - (videoHeight % 16);
}
uint32_t y_size = stride * (videoHeight + padding);
uint32_t v_size = stride * (videoHeight + padding) / 4;
uint32_t halfStride = (stride + 1) / 2;
uint32_t halfHeight = (videoHeight + 1) / 2;
uint32_t halfWidth = (videoWidth + 1) / 2;
// U plane (Cb)
b.mPlanes[1].mData = data + y_size + v_size;
b.mPlanes[1].mStride = halfStride;
b.mPlanes[1].mHeight = halfHeight;
b.mPlanes[1].mWidth = halfWidth;
b.mPlanes[1].mOffset = 0;
b.mPlanes[1].mSkip = 0;
// V plane (Cr)
b.mPlanes[2].mData = data + y_size;
b.mPlanes[2].mStride = halfStride;
b.mPlanes[2].mHeight = halfHeight;
b.mPlanes[2].mWidth = halfWidth;
b.mPlanes[2].mOffset = 0;
b.mPlanes[2].mSkip = 0;
media::TimeUnit pts = GetSampleTime(aSample);
NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
media::TimeUnit duration = GetSampleDuration(aSample);
NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
RefPtr<layers::PlanarYCbCrImage> image =
new IMFYCbCrImage(buffer, twoDBuffer);
nsIntRect pictureRegion = mVideoInfo.ScaledImageRect(videoWidth, videoHeight);
VideoData::SetVideoDataToImage(image,
mVideoInfo,
b,
pictureRegion,
false);
RefPtr<VideoData> v =
VideoData::CreateFromImage(mVideoInfo,
mImageContainer,
aStreamOffset,
pts.ToMicroseconds(),
duration.ToMicroseconds(),
image.forget(),
false,
-1,
pictureRegion);
v.forget(aOutVideoData);
return S_OK;
}
HRESULT
WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
int64_t aStreamOffset,
VideoData** aOutVideoData)
{
NS_ENSURE_TRUE(aSample, E_POINTER);
NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT);
NS_ENSURE_TRUE(mUseHwAccel, E_ABORT);
*aOutVideoData = nullptr;
HRESULT hr;
nsIntRect pictureRegion =
mVideoInfo.ScaledImageRect(mImageSize.width, mImageSize.height);
RefPtr<Image> image;
hr = mDXVA2Manager->CopyToImage(aSample,
pictureRegion,
mImageContainer,
getter_AddRefs(image));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
NS_ENSURE_TRUE(image, E_FAIL);
media::TimeUnit pts = GetSampleTime(aSample);
NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
media::TimeUnit duration = GetSampleDuration(aSample);
NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
RefPtr<VideoData> v = VideoData::CreateFromImage(mVideoInfo,
mImageContainer,
aStreamOffset,
pts.ToMicroseconds(),
duration.ToMicroseconds(),
image.forget(),
false,
-1,
pictureRegion);
NS_ENSURE_TRUE(v, E_FAIL);
v.forget(aOutVideoData);
return S_OK;
}
// Blocks until decoded sample is produced by the deoder.
HRESULT
WMFVideoMFTManager::Output(int64_t aStreamOffset,
RefPtr<MediaData>& aOutData)
{
RefPtr<IMFSample> sample;
HRESULT hr;
aOutData = nullptr;
int typeChangeCount = 0;
// Loop until we decode a sample, or an unexpected error that we can't
// handle occurs.
while (true) {
hr = mDecoder->Output(&sample);
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
return MF_E_TRANSFORM_NEED_MORE_INPUT;
}
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
// Video stream output type change. Probably a geometric apperature
// change. Reconfigure the video geometry, so that we output the
// correct size frames.
MOZ_ASSERT(!sample);
hr = ConfigureVideoFrameGeometry();
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Catch infinite loops, but some decoders perform at least 2 stream
// changes on consecutive calls, so be permissive.
// 100 is arbitrarily > 2.
NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE);
// Loop back and try decoding again...
++typeChangeCount;
continue;
}
if (SUCCEEDED(hr)) {
if (!sample) {
LOG("Video MFTDecoder returned success but no output!");
// On some machines/input the MFT returns success but doesn't output
// a video frame. If we detect this, try again, but only up to a
// point; after 250 failures, give up. Note we count all failures
// over the life of the decoder, as we may end up exiting with a
// NEED_MORE_INPUT and coming back to hit the same error. So just
// counting with a local variable (like typeChangeCount does) may
// not work in this situation.
++mNullOutputCount;
if (mNullOutputCount > 250) {
LOG("Excessive Video MFTDecoder returning success but no output; giving up");
mGotExcessiveNullOutput = true;
return E_FAIL;
}
continue;
}
break;
}
// Else unexpected error, assert, and bail.
NS_WARNING("WMFVideoMFTManager::Output() unexpected error");
return hr;
}
RefPtr<VideoData> frame;
if (mUseHwAccel) {
hr = CreateD3DVideoFrame(sample, aStreamOffset, getter_AddRefs(frame));
} else {
hr = CreateBasicVideoFrame(sample, aStreamOffset, getter_AddRefs(frame));
}
// Frame should be non null only when we succeeded.
MOZ_ASSERT((frame != nullptr) == SUCCEEDED(hr));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
NS_ENSURE_TRUE(frame, E_FAIL);
aOutData = frame;
if (mNullOutputCount) {
mGotValidOutputAfterNullOutput = true;
}
return S_OK;
}
void
WMFVideoMFTManager::Shutdown()
{
mDecoder = nullptr;
DeleteOnMainThread(mDXVA2Manager);
}
bool
WMFVideoMFTManager::IsHardwareAccelerated(nsACString& aFailureReason) const
{
aFailureReason = mDXVAFailureReason;
return mDecoder && mUseHwAccel;
}
const char*
WMFVideoMFTManager::GetDescriptionName() const
{
if (mDecoder && mUseHwAccel && mDXVA2Manager) {
return (mDXVA2Manager->IsD3D11()) ?
"D3D11 Hardware Decoder" : "D3D9 Hardware Decoder";
} else {
return "wmf software video decoder";
}
}
void
WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
{
MOZ_ASSERT(aConfig.GetAsVideoInfo());
mVideoInfo = *aConfig.GetAsVideoInfo();
mImageSize = mVideoInfo.mImage;
}
} // namespace mozilla