Bug 1496529 - P2. Make H264Converter use provided decoder's TaskQueue. r=bryce
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 09 Oct 2018 23:05:40 +0000
changeset 496086 f2b367bc188af94df47032d10cab669d0dc0c474
parent 496085 8995622893ecaa74ae47835a75677496c4368637
child 496087 19cdb829699f0bcaa047c7a29bb9d6b494f4e412
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbryce
bugs1496529
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1496529 - P2. Make H264Converter use provided decoder's TaskQueue. r=bryce The H264Converter can be used on a thread that isn't a nsThread or a TaskQueue, so having the H264Converter dispatching tasks isn't going to work So instead we run all the code on the decoder's taskqueue using promise chaining. All internal methods are made to assert that they are running on the task queue accordingly Depends on D7860 Differential Revision: https://phabricator.services.mozilla.com/D7861
dom/media/platforms/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -29,203 +29,229 @@ H264Converter::H264Converter(PlatformDec
   , mDecoder(nullptr)
   , mGMPCrashHelper(aParams.mCrashHelper)
   , mLastError(NS_OK)
   , mType(aParams.mType)
   , mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent)
   , mDecoderOptions(aParams.mOptions)
   , mRate(aParams.mRate)
 {
+  mInConstructor = true;
   mLastError = CreateDecoder(mOriginalConfig, aParams.mDiagnostics);
+  mInConstructor = false;
   if (mDecoder) {
     MOZ_ASSERT(H264::HasSPS(mOriginalConfig.mExtraData));
     // The video metadata contains out of band SPS/PPS (AVC1) store it.
     mOriginalExtraData = mOriginalConfig.mExtraData;
   }
 }
 
 H264Converter::~H264Converter()
 {
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 H264Converter::Init()
 {
-  if (mDecoder) {
-    return mDecoder->Init();
-  }
+  RefPtr<H264Converter> self = this;
+  return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    if (mDecoder) {
+      return mDecoder->Init();
+    }
 
-  // We haven't been able to initialize a decoder due to a missing SPS/PPS.
-  return MediaDataDecoder::InitPromise::CreateAndResolve(
-           TrackType::kVideoTrack, __func__);
+    // We haven't been able to initialize a decoder due to a missing SPS/PPS.
+    return MediaDataDecoder::InitPromise::CreateAndResolve(
+      TrackType::kVideoTrack, __func__);
+  });
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 H264Converter::Decode(MediaRawData* aSample)
 {
-  MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Flush operatin didn't complete");
+  RefPtr<H264Converter> self = this;
+  RefPtr<MediaRawData> sample = aSample;
+  return InvokeAsync(mTaskQueue, __func__, [self, this, sample]() {
+    MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(),
+                       "Flush operatin didn't complete");
 
-  MOZ_RELEASE_ASSERT(!mDecodePromiseRequest.Exists() &&
-                       !mInitPromiseRequest.Exists(),
-                     "Can't request a new decode until previous one completed");
+    MOZ_RELEASE_ASSERT(
+      !mDecodePromiseRequest.Exists() && !mInitPromiseRequest.Exists(),
+      "Can't request a new decode until previous one completed");
 
-  if (!AnnexB::ConvertSampleToAVCC(aSample)) {
-    // We need AVCC content to be able to later parse the SPS.
-    // This is a no-op if the data is already AVCC.
-    return DecodePromise::CreateAndReject(
-      MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("ConvertSampleToAVCC")),
-      __func__);
-  }
+    if (!AnnexB::ConvertSampleToAVCC(sample)) {
+      // We need AVCC content to be able to later parse the SPS.
+      // This is a no-op if the data is already AVCC.
+      return DecodePromise::CreateAndReject(
+        MediaResult(NS_ERROR_OUT_OF_MEMORY,
+                    RESULT_DETAIL("ConvertSampleToAVCC")),
+        __func__);
+    }
+
+    if (!AnnexB::IsAVCC(sample)) {
+      return DecodePromise::CreateAndReject(
+        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                    RESULT_DETAIL("Invalid H264 content")),
+        __func__);
+    }
 
-  if (!AnnexB::IsAVCC(aSample)) {
-    return DecodePromise::CreateAndReject(
-      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
-                  RESULT_DETAIL("Invalid H264 content")),
-      __func__);
-  }
+    MediaResult rv(NS_OK);
+    if (!mDecoder) {
+      // It is not possible to create an AVCC H264 decoder without SPS.
+      // As such, creation will fail if the extra_data just extracted doesn't
+      // contain a SPS.
+      rv = CreateDecoderAndInit(sample);
+      if (rv == NS_ERROR_NOT_INITIALIZED) {
+        // We are missing the required SPS to create the decoder.
+        // Ignore for the time being, the MediaRawData will be dropped.
+        return DecodePromise::CreateAndResolve(DecodedData(), __func__);
+      }
+    } else {
+      // Initialize the members that we couldn't if the extradata was given
+      // during H264Converter's construction.
+      if (!mNeedAVCC) {
+        mNeedAVCC =
+          Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
+      }
+      if (!mCanRecycleDecoder) {
+        mCanRecycleDecoder = Some(CanRecycleDecoder());
+      }
+      rv = CheckForSPSChange(sample);
+    }
 
-  MediaResult rv(NS_OK);
-  if (!mDecoder) {
-    // It is not possible to create an AVCC H264 decoder without SPS.
-    // As such, creation will fail if the extra_data just extracted doesn't
-    // contain a SPS.
-    rv = CreateDecoderAndInit(aSample);
-    if (rv == NS_ERROR_NOT_INITIALIZED) {
-      // We are missing the required SPS to create the decoder.
-      // Ignore for the time being, the MediaRawData will be dropped.
+    if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
+      // The decoder is pending initialization.
+      RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
+      return p;
+    }
+
+    if (NS_FAILED(rv)) {
+      return DecodePromise::CreateAndReject(rv, __func__);
+    }
+
+    if (mNeedKeyframe && !sample->mKeyframe) {
       return DecodePromise::CreateAndResolve(DecodedData(), __func__);
     }
-  } else {
-    // Initialize the members that we couldn't if the extradata was given during
-    // H264Converter's construction.
-    if (!mNeedAVCC) {
-      mNeedAVCC =
-        Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
-    }
-    if (!mCanRecycleDecoder) {
-      mCanRecycleDecoder = Some(CanRecycleDecoder());
-    }
-    rv = CheckForSPSChange(aSample);
-  }
-
-  if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
-    // The decoder is pending initialization.
-    RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
-    return p;
-  }
 
-  if (NS_FAILED(rv)) {
-    return DecodePromise::CreateAndReject(rv, __func__);
-  }
-
-  if (mNeedKeyframe && !aSample->mKeyframe) {
-    return DecodePromise::CreateAndResolve(DecodedData(), __func__);
-  }
+    auto res = !*mNeedAVCC
+                 ? AnnexB::ConvertSampleToAnnexB(sample, mNeedKeyframe)
+                 : Ok();
+    if (res.isErr()) {
+      return DecodePromise::CreateAndReject(
+        MediaResult(res.unwrapErr(), RESULT_DETAIL("ConvertSampleToAnnexB")),
+        __func__);
+    }
 
-  auto res = !*mNeedAVCC
-             ? AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)
-             : Ok();
-  if (res.isErr()) {
-    return DecodePromise::CreateAndReject(
-      MediaResult(res.unwrapErr(), RESULT_DETAIL("ConvertSampleToAnnexB")),
-      __func__);
-  }
+    mNeedKeyframe = false;
 
-  mNeedKeyframe = false;
+    sample->mExtraData = mCurrentConfig.mExtraData;
 
-  aSample->mExtraData = mCurrentConfig.mExtraData;
-
-  return mDecoder->Decode(aSample);
+    return mDecoder->Decode(sample);
+  });
 }
 
 RefPtr<MediaDataDecoder::FlushPromise>
 H264Converter::Flush()
 {
-  mDecodePromiseRequest.DisconnectIfExists();
-  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-  mNeedKeyframe = true;
-  mPendingFrames.Clear();
+  RefPtr<H264Converter> self = this;
+  return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    mDecodePromiseRequest.DisconnectIfExists();
+    mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+    mNeedKeyframe = true;
+    mPendingFrames.Clear();
 
-  MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Previous flush didn't complete");
+    MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(),
+                       "Previous flush didn't complete");
 
-  /*
-    When we detect a change of content in the H264 stream, we first drain the
-    current decoder (1), flush (2), shut it down (3) create a new decoder and
-    initialize it (4). It is possible for H264Converter::Flush to be called
-    during any of those times.
-    If during (1):
-      - mDrainRequest will not be empty.
-      - The old decoder can still be used, with the current extradata as stored
-        in mCurrentConfig.mExtraData.
+    /*
+      When we detect a change of content in the H264 stream, we first drain the
+      current decoder (1), flush (2), shut it down (3) create a new decoder and
+      initialize it (4). It is possible for H264Converter::Flush to be called
+      during any of those times.
+      If during (1):
+        - mDrainRequest will not be empty.
+        - The old decoder can still be used, with the current extradata as
+      stored in mCurrentConfig.mExtraData.
 
-    If during (2):
-      - mFlushRequest will not be empty.
-      - The old decoder can still be used, with the current extradata as stored
-        in mCurrentConfig.mExtraData.
+      If during (2):
+        - mFlushRequest will not be empty.
+        - The old decoder can still be used, with the current extradata as
+      stored in mCurrentConfig.mExtraData.
 
-    If during (3):
-      - mShutdownRequest won't be empty.
-      - mDecoder is empty.
-      - The old decoder is no longer referenced by the H264Converter.
+      If during (3):
+        - mShutdownRequest won't be empty.
+        - mDecoder is empty.
+        - The old decoder is no longer referenced by the H264Converter.
 
-    If during (4):
-      - mInitPromiseRequest won't be empty.
-      - mDecoder is set but not usable yet.
-  */
+      If during (4):
+        - mInitPromiseRequest won't be empty.
+        - mDecoder is set but not usable yet.
+    */
 
-  if (mDrainRequest.Exists() || mFlushRequest.Exists() ||
-      mShutdownRequest.Exists() || mInitPromiseRequest.Exists()) {
-    // We let the current decoder complete and will resume after.
-    return mFlushPromise.Ensure(__func__);
-  }
-  if (mDecoder) {
-    return mDecoder->Flush();
-  }
-  return FlushPromise::CreateAndResolve(true, __func__);
+    if (mDrainRequest.Exists() || mFlushRequest.Exists() ||
+        mShutdownRequest.Exists() || mInitPromiseRequest.Exists()) {
+      // We let the current decoder complete and will resume after.
+      RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
+      return p;
+    }
+    if (mDecoder) {
+      return mDecoder->Flush();
+    }
+    return FlushPromise::CreateAndResolve(true, __func__);
+  });
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 H264Converter::Drain()
 {
-  MOZ_RELEASE_ASSERT(!mDrainRequest.Exists());
-  mNeedKeyframe = true;
-  if (mDecoder) {
-    return mDecoder->Drain();
-  }
-  return DecodePromise::CreateAndResolve(DecodedData(), __func__);
+  RefPtr<H264Converter> self = this;
+  return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    MOZ_RELEASE_ASSERT(!mDrainRequest.Exists());
+    mNeedKeyframe = true;
+    if (mDecoder) {
+      return mDecoder->Drain();
+    }
+    return DecodePromise::CreateAndResolve(DecodedData(), __func__);
+  });
 }
 
 RefPtr<ShutdownPromise>
 H264Converter::Shutdown()
 {
-  mInitPromiseRequest.DisconnectIfExists();
-  mDecodePromiseRequest.DisconnectIfExists();
-  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-  mDrainRequest.DisconnectIfExists();
-  mFlushRequest.DisconnectIfExists();
-  mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-  mShutdownRequest.DisconnectIfExists();
+  RefPtr<H264Converter> self = this;
+  return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    mInitPromiseRequest.DisconnectIfExists();
+    mDecodePromiseRequest.DisconnectIfExists();
+    mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+    mDrainRequest.DisconnectIfExists();
+    mFlushRequest.DisconnectIfExists();
+    mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+    mShutdownRequest.DisconnectIfExists();
 
-  if (mShutdownPromise) {
-    // We have a shutdown in progress, return that promise instead as we can't
-    // shutdown a decoder twice.
-    return mShutdownPromise.forget();
-  }
-  return ShutdownDecoder();
+    if (mShutdownPromise) {
+      // We have a shutdown in progress, return that promise instead as we can't
+      // shutdown a decoder twice.
+      RefPtr<ShutdownPromise> p = mShutdownPromise.forget();
+      return p;
+    }
+    return ShutdownDecoder();
+  });
 }
 
 RefPtr<ShutdownPromise>
 H264Converter::ShutdownDecoder()
 {
-  mNeedAVCC.reset();
-  if (mDecoder) {
-    RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
-    return decoder->Shutdown();
-  }
-  return ShutdownPromise::CreateAndResolve(true, __func__);
+  RefPtr<H264Converter> self = this;
+  return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    mNeedAVCC.reset();
+    if (mDecoder) {
+      RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
+      return decoder->Shutdown();
+    }
+    return ShutdownPromise::CreateAndResolve(true, __func__);
+  });
 }
 
 bool
 H264Converter::IsHardwareAccelerated(nsACString& aFailureReason) const
 {
   if (mDecoder) {
     return mDecoder->IsHardwareAccelerated(aFailureReason);
   }
@@ -248,16 +274,20 @@ H264Converter::SetSeekThreshold(const me
     MediaDataDecoder::SetSeekThreshold(aTime);
   }
 }
 
 MediaResult
 H264Converter::CreateDecoder(const VideoInfo& aConfig,
                              DecoderDoctorDiagnostics* aDiagnostics)
 {
+  // This is the only one of two methods to run outside the TaskQueue when
+  // called from the constructor.
+  MOZ_ASSERT(mInConstructor || mTaskQueue->IsCurrentThreadIn());
+
   if (!H264::HasSPS(aConfig.mExtraData)) {
     // nothing found yet, will try again later
     return NS_ERROR_NOT_INITIALIZED;
   }
   UpdateConfigFromExtraData(aConfig.mExtraData);
 
   SPSData spsdata;
   if (H264::DecodeSPSFromExtraData(aConfig.mExtraData, spsdata)) {
@@ -306,16 +336,18 @@ H264Converter::CreateDecoder(const Video
   mNeedKeyframe = true;
 
   return NS_OK;
 }
 
 MediaResult
 H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
 {
+  AssertOnTaskQueue();
+
   RefPtr<MediaByteBuffer> extra_data =
     H264::ExtractExtraData(aSample);
   bool inbandExtradata = H264::HasSPS(extra_data);
   if (!inbandExtradata &&
       !H264::HasSPS(mCurrentConfig.mExtraData)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
@@ -326,17 +358,17 @@ H264Converter::CreateDecoderAndInit(Medi
   MediaResult rv =
     CreateDecoder(mCurrentConfig, /* DecoderDoctorDiagnostics* */ nullptr);
 
   if (NS_SUCCEEDED(rv)) {
     RefPtr<H264Converter> self = this;
     RefPtr<MediaRawData> sample = aSample;
     mDecoder->Init()
       ->Then(
-        AbstractThread::GetCurrent()->AsTaskQueue(),
+        mTaskQueue,
         __func__,
         [self, sample, this](const TrackType aTrackType) {
           mInitPromiseRequest.Complete();
           mNeedAVCC =
             Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
           mCanRecycleDecoder = Some(CanRecycleDecoder());
 
           if (!mFlushPromise.IsEmpty()) {
@@ -373,16 +405,18 @@ H264Converter::CanRecycleDecoder() const
   MOZ_ASSERT(mDecoder);
   return StaticPrefs::MediaDecoderRecycleEnabled() &&
          mDecoder->SupportDecoderRecycling();
 }
 
 void
 H264Converter::DecodeFirstSample(MediaRawData* aSample)
 {
+  AssertOnTaskQueue();
+
   if (mNeedKeyframe && !aSample->mKeyframe) {
     mDecodePromise.Resolve(mPendingFrames, __func__);
     mPendingFrames.Clear();
     return;
   }
 
   auto res = !*mNeedAVCC
              ? AnnexB::ConvertSampleToAnnexB(aSample, mNeedKeyframe)
@@ -410,16 +444,18 @@ H264Converter::DecodeFirstSample(MediaRa
              mDecodePromise.Reject(aError, __func__);
            })
     ->Track(mDecodePromiseRequest);
 }
 
 MediaResult
 H264Converter::CheckForSPSChange(MediaRawData* aSample)
 {
+  AssertOnTaskQueue();
+
   RefPtr<MediaByteBuffer> extra_data =
     aSample->mKeyframe ? H264::ExtractExtraData(aSample) : nullptr;
   if (!H264::HasSPS(extra_data)) {
     MOZ_ASSERT(mCanRecycleDecoder.isSome());
     if (!*mCanRecycleDecoder) {
       // If the decoder can't be recycled, the out of band extradata will never
       // change as the H264Converter will be recreated by the MediaFormatReader
       // instead. So there's no point in testing for changes.
@@ -454,16 +490,18 @@ H264Converter::CheckForSPSChange(MediaRa
   // create a new one.
   DrainThenFlushDecoder(aSample);
   return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
 }
 
 void
 H264Converter::DrainThenFlushDecoder(MediaRawData* aPendingSample)
 {
+  AssertOnTaskQueue();
+
   RefPtr<MediaRawData> sample = aPendingSample;
   RefPtr<H264Converter> self = this;
   mDecoder->Drain()
     ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
            __func__,
            [self, sample, this](const MediaDataDecoder::DecodedData& aResults) {
              mDrainRequest.Complete();
              if (!mFlushPromise.IsEmpty()) {
@@ -489,33 +527,35 @@ H264Converter::DrainThenFlushDecoder(Med
              }
              mDecodePromise.Reject(aError, __func__);
            })
     ->Track(mDrainRequest);
 }
 
 void H264Converter::FlushThenShutdownDecoder(MediaRawData* aPendingSample)
 {
+  AssertOnTaskQueue();
+
   RefPtr<MediaRawData> sample = aPendingSample;
   RefPtr<H264Converter> self = this;
   mDecoder->Flush()
-    ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
+    ->Then(mTaskQueue,
            __func__,
            [self, sample, this]() {
              mFlushRequest.Complete();
 
              if (!mFlushPromise.IsEmpty()) {
                // A Flush is pending, abort the current operation.
                mFlushPromise.Resolve(true, __func__);
                return;
              }
 
              mShutdownPromise = ShutdownDecoder();
              mShutdownPromise
-               ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
+               ->Then(mTaskQueue,
                       __func__,
                       [self, sample, this]() {
                         mShutdownRequest.Complete();
                         mShutdownPromise = nullptr;
 
                         if (!mFlushPromise.IsEmpty()) {
                           // A Flush is pending, abort the current operation.
                           mFlushPromise.Resolve(true, __func__);
@@ -544,16 +584,20 @@ void H264Converter::FlushThenShutdownDec
              mDecodePromise.Reject(aError, __func__);
            })
     ->Track(mFlushRequest);
 }
 
 void
 H264Converter::UpdateConfigFromExtraData(MediaByteBuffer* aExtraData)
 {
+  // This is the only one of two methods to run outside the TaskQueue when
+  // called from the constructor.
+  MOZ_ASSERT(mInConstructor || mTaskQueue->IsCurrentThreadIn());
+
   SPSData spsdata;
   if (H264::DecodeSPSFromExtraData(aExtraData, spsdata) &&
       spsdata.pic_width > 0 && spsdata.pic_height > 0) {
     H264::EnsureSPSIsSane(spsdata);
     mCurrentConfig.mImage.width = spsdata.pic_width;
     mCurrentConfig.mImage.height = spsdata.pic_height;
     mCurrentConfig.mDisplay.width = spsdata.display_width;
     mCurrentConfig.mDisplay.height = spsdata.display_height;
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -3,18 +3,18 @@
 /* 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 mozilla_H264Converter_h
 #define mozilla_H264Converter_h
 
 #include "PlatformDecoderModule.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
-
 namespace mozilla {
 
 class DecoderDoctorDiagnostics;
 
 DDLoggedTypeDeclNameAndBase(H264Converter, MediaDataDecoder);
 
 // H264Converter is a MediaDataDecoder wrapper used to ensure that
 // only AVCC or AnnexB is fed to the underlying MediaDataDecoder.
@@ -61,16 +61,21 @@ public:
       return mDecoder->NeedsConversion();
     }
     // Default so no conversion is performed.
     return ConversionRequired::kNeedAVCC;
   }
   MediaResult GetLastError() const { return mLastError; }
 
 private:
+  void AssertOnTaskQueue()
+  {
+    MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  }
+
   // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
   // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
   // will set mError accordingly.
   MediaResult CreateDecoder(const VideoInfo& aConfig,
                          DecoderDoctorDiagnostics* aDiagnostics);
   MediaResult CreateDecoderAndInit(MediaRawData* aSample);
   MediaResult CheckForSPSChange(MediaRawData* aSample);
   void UpdateConfigFromExtraData(MediaByteBuffer* aExtraData);
@@ -105,13 +110,15 @@ private:
   Maybe<bool> mNeedAVCC;
   MediaResult mLastError;
   bool mNeedKeyframe = true;
   const TrackInfo::TrackType mType;
   MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
   const CreateDecoderParams::OptionSet mDecoderOptions;
   const CreateDecoderParams::VideoFrameRate mRate;
   Maybe<bool> mCanRecycleDecoder;
+  // Used for debugging purposes only
+  Atomic<bool> mInConstructor;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_H264Converter_h