Bug 1274498 - Refuse to play H.264 files which have one or more dimensions less than 48 pixels. r=jya, a=ritu
authorChris Pearce <cpearce@mozilla.com>
Wed, 07 Sep 2016 14:16:29 +1200
changeset 350190 30ab4c8ae8d154f63d3bdad45b4ac2c4e15ea041
parent 350189 0a1f64d2110eb2f589a1615cab6d446bccbd5c87
child 350191 76e76e26bfe70e13bf6c91bfb2530f9acc42687a
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, ritu
bugs1274498, 373288
milestone50.0a2
Bug 1274498 - Refuse to play H.264 files which have one or more dimensions less than 48 pixels. r=jya, a=ritu We've had large numbers of shutdown hangs with the Windows H.264 decoder stuck calling IMFTransform::ProcessOutput(), blocking shutdown. I can reproduce this with videos with dimensions less than 32 pixels. Chrome also encountered this with the WMF decoder: https://bugs.chromium.org/p/chromium/issues/detail?id=373288 The WMF H.264 Decoder is documented to have a minimum resolution of 48x48 pixels. So this patch causes us to reject H.264 files with either width or height less than 48 pixels. I have been able to play files down to 34x34 pixels on Windows 10, but it seems safest to just follow the what's documented in MSDN, and reject files that are smaller than the documented minimum. MozReview-Commit-ID: 5peP6UGnAaB
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
dom/media/platforms/wmf/WMFVideoMFTManager.h
dom/media/test/manifest.js
dom/media/test/mochitest.ini
dom/media/test/red-46x48.mp4
dom/media/test/red-46x48.mp4^headers^
dom/media/test/red-48x46.mp4
dom/media/test/red-48x46.mp4^headers^
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -84,16 +84,17 @@ WMFVideoMFTManager::WMFVideoMFTManager(
   , mVideoStride(0)
   , mImageSize(aConfig.mImage)
   , mImageContainer(aImageContainer)
   , mDXVAEnabled(aDXVAEnabled)
   , mLayersBackend(aLayersBackend)
   , mNullOutputCount(0)
   , mGotValidOutputAfterNullOutput(false)
   , mGotExcessiveNullOutput(false)
+  , mIsValid(true)
   // 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")) {
@@ -365,18 +366,42 @@ WMFVideoMFTManager::InitializeDXVA(bool 
     NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
   }
   mDXVA2Manager = event->mDXVA2Manager;
 
   return mDXVA2Manager != nullptr;
 }
 
 bool
+WMFVideoMFTManager::ValidateVideoInfo()
+{
+  // The WMF H.264 decoder is documented to have a minimum resolution
+  // 48x48 pixels. We've observed the decoder working for output smaller than
+  // that, but on some output it hangs in IMFTransform::ProcessOutput(), so
+  // we just reject streams which are less than the documented minimum.
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd797815(v=vs.85).aspx
+  static const int32_t MIN_H264_FRAME_DIMENSION = 48;
+  if (mStreamType == H264 &&
+      (mVideoInfo.mImage.width < MIN_H264_FRAME_DIMENSION ||
+       mVideoInfo.mImage.height < MIN_H264_FRAME_DIMENSION)) {
+    LogToBrowserConsole(NS_LITERAL_STRING(
+      "Can't decode H.264 stream with width or height less than 48 pixels."));
+    mIsValid = false;
+  }
+
+  return mIsValid;
+}
+
+bool
 WMFVideoMFTManager::Init()
 {
+  if (!ValidateVideoInfo()) {
+    return false;
+  }
+
   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 {
@@ -478,16 +503,20 @@ WMFVideoMFTManager::SetDecoderMediaTypes
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   return mDecoder->SetMediaTypes(inputType, outputType);
 }
 
 HRESULT
 WMFVideoMFTManager::Input(MediaRawData* aSample)
 {
+  if (!mIsValid) {
+    return E_FAIL;
+  }
+
   if (!mDecoder) {
     // This can happen during shutdown.
     return E_FAIL;
   }
 
   HRESULT hr = mDecoder->CreateInputSample(aSample->Data(),
                                            uint32_t(aSample->Size()),
                                            aSample->mTime,
@@ -902,11 +931,12 @@ WMFVideoMFTManager::IsHardwareAccelerate
 }
 
 void
 WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
 {
   MOZ_ASSERT(aConfig.GetAsVideoInfo());
   mVideoInfo = *aConfig.GetAsVideoInfo();
   mImageSize = mVideoInfo.mImage;
+  ValidateVideoInfo();
 }
 
 } // namespace mozilla
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -46,16 +46,18 @@ public:
   {
     nsCString failureReason;
     return IsHardwareAccelerated(failureReason)
       ? "wmf hardware video decoder" : "wmf software video decoder";
   }
 
 private:
 
+  bool ValidateVideoInfo();
+
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
 
   HRESULT ConfigureVideoFrameGeometry();
 
   HRESULT CreateBasicVideoFrame(IMFSample* aSample,
                                 int64_t aStreamOffset,
@@ -96,13 +98,14 @@ private:
   StreamType mStreamType;
 
   const GUID& GetMFTGUID();
   const GUID& GetMediaSubtypeGUID();
 
   uint32_t mNullOutputCount;
   bool mGotValidOutputAfterNullOutput;
   bool mGotExcessiveNullOutput;
+  bool mIsValid;
 };
 
 } // namespace mozilla
 
 #endif // WMFVideoMFTManager_h_
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -521,16 +521,30 @@ var gErrorTests = [
   { name:"bug504843.ogv", type:"video/ogg" },
   { name:"bug501279.ogg", type:"audio/ogg" },
   { name:"bug580982.webm", type:"video/webm" },
   { name:"bug603918.webm", type:"video/webm" },
   { name:"bug604067.webm", type:"video/webm" },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
+function IsWindowsVistaOrLater() {
+  var re = /Windows NT (\d+.\d)/;
+  var winver = manifestNavigator().userAgent.match(re);
+  return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
+}
+
+// Windows' H.264 decoder cannot handle H.264 streams with resolution
+// less than 48x48 pixels. We refuse to play and error on such streams.
+if (IsWindowsVistaOrLater() &&
+    manifestVideo().canPlayType('video/mp4; codecs="avc1.42E01E"')) {
+  gErrorTests = gErrorTests.concat({name: "red-46x48.mp4", type:"video/mp4"},
+                                   {name: "red-48x46.mp4", type:"video/mp4"});
+}
+
 // These are files that have nontrivial duration and are useful for seeking within.
 var gSeekTests = [
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"audio.wav", type:"audio/x-wav", duration:0.031247 },
   { name:"seek.ogv", type:"video/ogg", duration:3.966 },
   { name:"320x240.ogv", type:"video/ogg", duration:0.266 },
   { name:"seek.webm", type:"video/webm", duration:3.966 },
   { name:"sine.webm", type:"audio/webm", duration:4.001 },
@@ -550,22 +564,16 @@ var gFastSeekTests = [
   // Note: Not all keyframes in the file are actually referenced in the Cues in this file.
   { name:"seek.webm", type:"video/webm", keyframes:[0, 0.8, 1.6, 2.4, 3.2]},
   // Note: the sync points are the points on both the audio and video streams
   // before the keyframes. You can't just assume that the keyframes are the sync
   // points, as the audio required for that sync point may be before the keyframe.
   { name:"bug516323.indexed.ogv", type:"video/ogg", keyframes:[0, 0.46, 3.06] },
 ];
 
-function IsWindows8OrLater() {
-  var re = /Windows NT (\d.\d)/;
-  var winver = manifestNavigator().userAgent.match(re);
-  return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.2;
-}
-
 // These files are WebMs without cues. They're seekable within their buffered
 // ranges. If work renders WebMs fully seekable these files should be moved
 // into gSeekTests
 var gCuelessWebMTests = [
   { name:"no-cues.webm", type:"video/webm", duration:3.967 },
 ];
 
 // These are files that are non seekable, due to problems with the media,
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -473,16 +473,20 @@ support-files =
   r11025_s16_c1_trailing.wav^headers^
   r11025_u8_c1.wav
   r11025_u8_c1.wav^headers^
   r11025_u8_c1_trunc.wav
   r11025_u8_c1_trunc.wav^headers^
   r16000_u8_c1_list.wav
   r16000_u8_c1_list.wav^headers^
   reactivate_helper.html
+  red-46x48.mp4
+  red-46x48.mp4^headers^
+  red-48x46.mp4
+  red-48x46.mp4^headers^
   redirect.sjs
   referer.sjs
   region.vtt
   resolution-change.webm
   resolution-change.webm^headers^
   sample.3gp
   sample.3g2
   sample-fisbone-skeleton4.ogv
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0760cc1c165dc13a50cf0e8b85f75ac7efb5c6f7
GIT binary patch
literal 1548
zc$|GyL5mzk6t0<&U?3pEf<ZP=;(8EwX1jZ4C%Xd;ktH|>BVNowgtoe>db*jau9~Wv
z*_pklASwj$wm-m|;3)`ta1Vk9!9U=NpgAZ;qsc{t9GtxB+0D$ZCPi1juj+m8y?XCe
zH$n(!ZmJ8bBtmuMCCrG7X()9&L<l{R8O}lXk7UGw{Oa7<+3d?7uiifR^|1HF{`&X$
z@VUS4PNGg5H!xL(<FLJjP1I>^;me!b+ff*RqSpk%#<kb3zT9Zzm#*&slW_`)9i^v&
zXAVa}&}u|MxCxuNbGo~+vAet592bl$LCU5w{S6<bnL8<fqqHkjX}g$`l+ZZDhG%gL
zGoA`X2XWX9x&bC75mU?KVAARalQ3*y$>W?)upOm<E@N${U<&WVusCc6U<5BLiwS3m
z_ZWgjVo2HNaoE8$H%gKOOhfDp7oxC$wkBJQIskMi;{c98*q$nR9EIy)h%;hcqV1s2
zo-ubY)QQS6%U#@vu*(ftct=910m)&JEcUD@C|_&@xHMG^T1evjIf~M8BM6XSKNTZG
zrU_M269;PwC31!oCAa{hA)b>NBDsZPrHP&bE?67^CX8sGMVh1q@sU_zc$L>KFZ#Jl
z0V&PPq^~q+=bIW#2Yd>?<7hKDf{83je{{-APDd_o2e=CED{8pS!P?N|VYXgxXiQRr
zWeR2WS)~Q=AwRel*aN48?++gO)Z(Do0jPbLN|H(3*#=-8*Kr#c8d?U|11!Kb8NzMw
zy+!Z9eSVRD_#S#@_V3>}e~P|4|HCgghBv;xxAzwNp!Mq7+qYIfx&8Z{Z+||he{}Bi
zcMssY|0$IkgPD-yoS_rr%x~4KA^$8!?Ii2;`U-mNsKfI#q&@)rrW;f-&-~Y$kNx;K
z{~NanGpLqpoH+|}l?&?@7Ds+7+uFyH6$Aj5>~!u|s`>gi)uNFzVU9KagI#7`_aAVN
zluU5Xsdb@g8Gx0steh?%bB)b)s|+s6QW_UVM&KiS-?~(Q+_%oMCC>Z)pY@1R_{0O$
z1KE2d<Mg;Vo54E(uRAq{F3fJBi+zxv9bUVC?<wCCUoBLBa^;2n8akW*ap&f`x3aD(
z>v4?QQQ=qNwDjcu!?^;wy}7D>cd<v+65ls19Wm$I@rLA;^yAs((nGrp(ON90(J1Eq
ceg0FYyu(ZI7bnLBc!i9!%?^Ac%`gZb08m<M@&Et;
new file mode 100644
--- /dev/null
+++ b/dom/media/test/red-46x48.mp4^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d83de4027d11f07f6e76c02f797a540ab9f0eb1b
GIT binary patch
literal 1548
zc$|GyL5mzk6t0<&pb!vY!5}M?xE{ov+3ud%$?iZyWC_l}h!=AZp{=f}o-U@UtEReU
zc4jXshzdcx?GNxKc#t6I!955b1pk05g65zYjV6~Mc*x{c&u(URH7UCKeO2#!@6~&+
zx)DM+b5kQqtq`gsFJVSxOhaYbAwuY}%yJI8e-tAQ<k#oU&Sqczbou7NZ->1v_t$^G
zhtK_edlGfpxPhs*9Ea^KY@<$V3t!ya-j2cm6ul-8Hm<yO`Q=6%zjSp6n2b|U>}WHU
zJaae-f>t96!cExBoip8yjosbd=2$SUB`KQP_BVW#X6}>(jy6u{qU>TyQbOYpTb{)&
z%y=p_9mHWb=mwY+L{3W{2a{Gen1o>qD<0>3g3D0~=pr`d6infrSQdxP0F2;;m6&jr
zc#k1iB$gC?9)}%FbE_3ez%<0paw$a#Xlt^?r~^QUDh}WXgzf2q$5FT*hBzaoOH4Ts
z#xv#)h9=QjR&p0NBJ6St7T%H6dO&hmB#XUL3d$E70WNG6g9=5QKZhtBw~_z}_ER~s
zWSUT|3~{igP$FlED8K~}E%BVp62(g>R+^Y8V8P-DFk!^_EYc(u#7APn@+z-gF8aAk
z0cpdFq^}KV=bHvh2Yd>?<7hKDf{7A^KRV?Frz01)16&376}7y~!P?T~LAG9RX-ra!
zRSISGS)~H_kRMzN?19tL_XiJsYH`r)05m>KCCMc2Yy&7AH*p&a11*E=0T$qz4B<BT
z-lBKlKEKF6d=EW6yLb2c&(Zgf|M=^*;k9q>?7hW4XuZ1j_Knp~ZvJuWyI&6KAD#Q+
z-2=Gpe<`iUU?$Z#XXwN@^LsUG$UlowJIOk|zJwk*>hL@TsSg0Z=>}EIGynDGV?R31
z|HchqHml_tXRZXf%4O*m7Ds+7+uFy92?78sb~^V<)qMTiYSGA<w8t9%!7eke`VY8A
z3MRSd)Vk2L48Y1*RZf?WxyI(YRR$MjDUI_ZEAbKjw{)olxnH`HEpguW|E!0U!YAHD
zJ&?VJGER>RvzfO8uRAq{&d+Y33w@BE9bUQj`IEjUzFMgM#L5f%HFP%r^VaosZ&kXg
ztj95GM}=R7)6$ds59SK!_U5Yk?ZqC|OMKtBbi|x*#~X@Q(vN4COAqa`#2C4pMnlZ|
c`~0U)dxy`!Uz{8l;1x2?HaqZ*G{Ydg4<15mwg3PC
new file mode 100644
--- /dev/null
+++ b/dom/media/test/red-48x46.mp4^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store