Bug 1347101: P2. Don't rely on MF_E_TRANSFORM_STREAM_CHANGE result. r?mattwoodrow draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 17 Mar 2017 01:21:23 +0100
changeset 503134 353617c6d626603695bd511e110a5a296d1bf48b
parent 503133 4ac7a0d69035cee9ff64fe1b1f9621855ff8e870
child 550366 39444e6e5fd0ebbd68619265b67f6365a5ca2a3a
push id50512
push userbmo:jyavenard@mozilla.com
push dateWed, 22 Mar 2017 22:03:02 +0000
reviewersmattwoodrow
bugs1347101
milestone55.0a1
Bug 1347101: P2. Don't rely on MF_E_TRANSFORM_STREAM_CHANGE result. r?mattwoodrow Under some circumstances, and seen on Windows 8, a decoded sample can be returned without the MFT returning MF_E_TRANSFORM_STREAM_CHANGE. For historical reasons, we required that message to be returned at least once to set the output image size. This was required as the decoder used to be recycled with different video streams. This is no longer the case, we can rely on the video info instead. It also greatly simplifies the code MozReview-Commit-ID: H14KBiNWrjQ
dom/media/platforms/wmf/MFTDecoder.cpp
dom/media/platforms/wmf/MFTDecoder.h
dom/media/platforms/wmf/WMFAudioMFTManager.cpp
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
dom/media/platforms/wmf/WMFVideoMFTManager.h
--- a/dom/media/platforms/wmf/MFTDecoder.cpp
+++ b/dom/media/platforms/wmf/MFTDecoder.cpp
@@ -46,17 +46,17 @@ MFTDecoder::SetMediaTypes(IMFMediaType* 
                           void* aData)
 {
   mOutputType = aOutputType;
 
   // Set the input type to the one the caller gave us...
   HRESULT hr = mDecoder->SetInputType(0, aInputType, 0);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  hr = SetDecoderOutputType(aCallback, aData);
+  hr = SetDecoderOutputType(true /* match all attributes */, aCallback, aData);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
@@ -71,31 +71,44 @@ MFTDecoder::GetAttributes()
 {
   RefPtr<IMFAttributes> attr;
   HRESULT hr = mDecoder->GetAttributes(getter_AddRefs(attr));
   NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
   return attr.forget();
 }
 
 HRESULT
-MFTDecoder::SetDecoderOutputType(ConfigureOutputCallback aCallback, void* aData)
+MFTDecoder::SetDecoderOutputType(bool aMatchAllAttributes,
+                                 ConfigureOutputCallback aCallback,
+                                 void* aData)
 {
   NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER);
 
+  GUID currentSubtype = {0};
+  HRESULT hr = mOutputType->GetGUID(MF_MT_SUBTYPE, &currentSubtype);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
   // Iterate the enumerate the output types, until we find one compatible
   // with what we need.
-  HRESULT hr;
   RefPtr<IMFMediaType> outputType;
   UINT32 typeIndex = 0;
   while (SUCCEEDED(mDecoder->GetOutputAvailableType(
     0, typeIndex++, getter_AddRefs(outputType)))) {
-    BOOL resultMatch;
-    hr = mOutputType->Compare(
-      outputType, MF_ATTRIBUTES_MATCH_OUR_ITEMS, &resultMatch);
-    if (SUCCEEDED(hr) && resultMatch == TRUE) {
+    GUID outSubtype = {0};
+    hr = outputType->GetGUID(MF_MT_SUBTYPE, &outSubtype);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+    BOOL resultMatch = currentSubtype == outSubtype;
+
+    if (resultMatch && aMatchAllAttributes) {
+      hr = mOutputType->Compare(outputType, MF_ATTRIBUTES_MATCH_OUR_ITEMS,
+                                &resultMatch);
+      NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+    }
+    if (resultMatch == TRUE) {
       if (aCallback) {
         hr = aCallback(outputType, aData);
         NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
       }
       hr = mDecoder->SetOutputType(0, outputType, 0);
       NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
       hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo);
@@ -219,23 +232,16 @@ MFTDecoder::Output(RefPtr<IMFSample>* aO
   if (output.pEvents) {
     // We must release this, as per the IMFTransform::ProcessOutput()
     // MSDN documentation.
     output.pEvents->Release();
     output.pEvents = nullptr;
   }
 
   if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
-    // Type change, probably geometric aperture change.
-    // Reconfigure decoder output type, so that GetOutputMediaType()
-    // returns the new type, and return the error code to caller.
-    // This is an expected failure, so don't warn on encountering it.
-    hr = SetDecoderOutputType(nullptr, nullptr);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-    // Return the error, so that the caller knows to retry.
     return MF_E_TRANSFORM_STREAM_CHANGE;
   }
 
   if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
     // Not enough input to produce output. This is an expected failure,
     // so don't warn on encountering it.
     return hr;
   }
--- a/dom/media/platforms/wmf/MFTDecoder.h
+++ b/dom/media/platforms/wmf/MFTDecoder.h
@@ -82,21 +82,20 @@ public:
 
   // Sends a flush message to the MFT. This causes it to discard all
   // input data. Use before seeking.
   HRESULT Flush();
 
   // Sends a message to the MFT.
   HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, ULONG_PTR aData);
 
-
-  HRESULT SetDecoderOutputType(ConfigureOutputCallback aCallback, void* aData);
+  HRESULT SetDecoderOutputType(bool aMatchAllAttributes,
+                               ConfigureOutputCallback aCallback,
+                               void* aData);
 private:
-
-
   HRESULT CreateOutputSample(RefPtr<IMFSample>* aOutSample);
 
   MFT_INPUT_STREAM_INFO mInputStreamInfo;
   MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
 
   RefPtr<IMFTransform> mDecoder;
 
   RefPtr<IMFMediaType> mOutputType;
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -175,16 +175,22 @@ WMFAudioMFTManager::Init()
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
   hr = outputType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float);
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
   hr = outputType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 32);
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
+  hr = outputType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, mAudioChannels);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+
+  hr = outputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+
   hr = decoder->SetMediaTypes(inputType, outputType);
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
   mDecoder = decoder;
 
   return true;
 }
 
@@ -228,16 +234,19 @@ WMFAudioMFTManager::Output(int64_t aStre
   HRESULT hr;
   int typeChangeCount = 0;
   while (true) {
     hr = mDecoder->Output(&sample);
     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
       return hr;
     }
     if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+      hr = mDecoder->SetDecoderOutputType(true /* check all attribute */,
+                                          nullptr,
+                                          nullptr);
       hr = UpdateOutputType();
       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);
       ++typeChangeCount;
       continue;
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -104,18 +104,18 @@ GetCompositorBackendType(layers::KnowsCo
 }
 
 WMFVideoMFTManager::WMFVideoMFTManager(
                             const VideoInfo& aConfig,
                             layers::KnowsCompositor* aKnowsCompositor,
                             layers::ImageContainer* aImageContainer,
                             bool aDXVAEnabled)
   : mVideoInfo(aConfig)
+  , mImageSize(aConfig.mImage)
   , mVideoStride(0)
-  , mImageSize(aConfig.mImage)
   , mImageContainer(aImageContainer)
   , mDXVAEnabled(aDXVAEnabled)
   , mKnowsCompositor(aKnowsCompositor)
   // mVideoStride, mVideoWidth, mVideoHeight, mUseHwAccel are initialized in
   // Init().
 {
   MOZ_COUNT_CTOR(WMFVideoMFTManager);
 
@@ -571,19 +571,46 @@ WMFVideoMFTManager::InitInternal(bool aF
     Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
                           uint32_t(media::MediaDecoderBackend::WMFSoftware));
   }
 
   mDecoder = decoder;
   hr = SetDecoderMediaTypes();
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
+  RefPtr<IMFMediaType> outputType;
+  hr = mDecoder->GetOutputMediaType(outputType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  if (mUseHwAccel && !CanUseDXVA(outputType)) {
+    mDXVAEnabled = false;
+    // DXVA initialization actually failed, re-do initialisation.
+    return InitInternal(aForceD3D9);
+  }
+
   LOG("Video Decoder initialized, Using DXVA: %s",
       (mUseHwAccel ? "Yes" : "No"));
 
+  if (mDXVA2Manager) {
+    hr = mDXVA2Manager->ConfigureForSize(mVideoInfo.ImageRect().width,
+                                         mVideoInfo.ImageRect().height);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  } else {
+    GetDefaultStride(outputType, mVideoInfo.ImageRect().width, &mVideoStride);
+  }
+  LOG("WMFVideoMFTManager frame geometry stride=%u picture=(%d, %d, %d, %d) "
+      "display=(%d,%d)",
+      mVideoStride,
+      mVideoInfo.ImageRect().x,
+      mVideoInfo.ImageRect().y,
+      mVideoInfo.ImageRect().width,
+      mVideoInfo.ImageRect().height,
+      mVideoInfo.mDisplay.width,
+      mVideoInfo.mDisplay.height);
+
   return true;
 }
 
 HRESULT
 WMFVideoMFTManager::SetDecoderMediaTypes()
 {
   // Setup the input/output media types.
   RefPtr<IMFMediaType> inputType;
@@ -595,36 +622,38 @@ WMFVideoMFTManager::SetDecoderMediaTypes
 
   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);
 
-  // MSFT MFT needs this frame size set for VP9?
-  if (mStreamType == VP9 || mStreamType == VP8) {
-    hr =
-      inputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  hr = inputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-    hr = MFSetAttributeSize(inputType,
-                            MF_MT_FRAME_SIZE,
-                            mVideoInfo.ImageRect().width,
-                            mVideoInfo.ImageRect().height);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-  }
+  hr = MFSetAttributeSize(inputType,
+                          MF_MT_FRAME_SIZE,
+                          mVideoInfo.ImageRect().width,
+                          mVideoInfo.ImageRect().height);
+  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);
 
+  hr = MFSetAttributeSize(outputType,
+                          MF_MT_FRAME_SIZE,
+                          mVideoInfo.ImageRect().width,
+                          mVideoInfo.ImageRect().height);
+  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
@@ -634,28 +663,29 @@ WMFVideoMFTManager::Input(MediaRawData* 
     return E_FAIL;
   }
 
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
 
+  RefPtr<IMFSample> inputSample;
   HRESULT hr = mDecoder->CreateInputSample(aSample->Data(),
                                            uint32_t(aSample->Size()),
                                            aSample->mTime,
-                                           &mLastInput);
-  NS_ENSURE_TRUE(SUCCEEDED(hr) && mLastInput != nullptr, hr);
+                                           &inputSample);
+  NS_ENSURE_TRUE(SUCCEEDED(hr) && inputSample != nullptr, hr);
 
   mLastDuration = aSample->mDuration;
   mLastTime = aSample->mTime;
   mSamplesCount++;
 
   // Forward sample data to the decoder.
-  return mDecoder->Input(mLastInput);
+  return mDecoder->Input(inputSample);
 }
 
 class SupportsConfigEvent : public Runnable {
 public:
   SupportsConfigEvent(DXVA2Manager* aDXVA2Manager,
                       IMFMediaType* aMediaType,
                       float aFramerate)
     : mDXVA2Manager(aDXVA2Manager)
@@ -716,79 +746,16 @@ WMFVideoMFTManager::CanUseDXVA(IMFMediaT
     // This logic needs to run on the main thread
     mozilla::SyncRunnable::DispatchToThread(
       SystemGroup::EventTargetFor(mozilla::TaskCategory::Other), event);
   }
 
   return event->mSupportsConfig;
 }
 
-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);
@@ -973,31 +940,44 @@ WMFVideoMFTManager::Output(int64_t aStre
 
   // 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();
+      // Video stream output type change, probably geometric aperture change.
+      // We must reconfigure the decoder output type.
+      hr = mDecoder->SetDecoderOutputType(false /* check all attribute */,
+                                          nullptr,
+                                          nullptr);
       NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+      if (!mUseHwAccel) {
+        // The stride may have changed, recheck for it.
+        RefPtr<IMFMediaType> outputType;
+        hr = mDecoder->GetOutputMediaType(outputType);
+        NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+        hr = GetDefaultStride(outputType, mVideoInfo.ImageRect().width,
+                              &mVideoStride);
+        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
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -68,40 +68,37 @@ public:
 
 private:
   bool ValidateVideoInfo();
 
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
 
-  HRESULT ConfigureVideoFrameGeometry();
-
   HRESULT CreateBasicVideoFrame(IMFSample* aSample,
                                 int64_t aStreamOffset,
                                 VideoData** aOutVideoData);
 
   HRESULT CreateD3DVideoFrame(IMFSample* aSample,
                               int64_t aStreamOffset,
                               VideoData** aOutVideoData);
 
   HRESULT SetDecoderMediaTypes();
 
   bool CanUseDXVA(IMFMediaType* aType);
 
   // Video frame geometry.
-  VideoInfo mVideoInfo;
+  const VideoInfo mVideoInfo;
+  const nsIntSize mImageSize;
   uint32_t mVideoStride;
-  nsIntSize mImageSize;
 
   RefPtr<layers::ImageContainer> mImageContainer;
   RefPtr<layers::KnowsCompositor> mKnowsCompositor;
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
 
-  RefPtr<IMFSample> mLastInput;
   float mLastDuration;
   int64_t mLastTime = 0;
   bool mDraining = false;
   int64_t mSamplesCount = 0;
 
   bool mDXVAEnabled;
   bool mUseHwAccel;