Bug 1407810 - Use DDLogger in media stack - r=jwwang
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 10 Oct 2017 17:55:27 +1100
changeset 708986 bce6b611bced2bd57e565339ecc9fd79ecbdc59f
parent 708985 2628267820ff8146306f4d58617fd0a628177746
child 708987 7867f6cde036221113d187145f772a3eea2e18d9
push id92501
push userdgottwald@mozilla.com
push dateThu, 07 Dec 2017 11:26:02 +0000
reviewersjwwang
bugs1407810
milestone59.0a1
Bug 1407810 - Use DDLogger in media stack - r=jwwang Mostly-mechanical additions: - Log constructions&destructions, usually by just inheriting from DecoderDoctorLifeLogger, otherwise with explicit log commands (for internal classes for which DecoderDoctorTraits can't be specialized), - Log links between most objects, e.g.: Media element -> decoder -> state machine -> reader -> demuxer -> resource, etc. And logging some important properties and events (JS events, duration change, frames being decoded, etc.) More will be added later on, from just converting MOZ_LOGs, and as needed. MozReview-Commit-ID: KgNhHSz35t0
dom/html/HTMLAudioElement.cpp
dom/html/HTMLMediaElement.cpp
dom/html/HTMLVideoElement.cpp
dom/media/ADTSDemuxer.cpp
dom/media/ADTSDemuxer.h
dom/media/BaseMediaResource.h
dom/media/BufferMediaResource.h
dom/media/ChannelMediaDecoder.cpp
dom/media/ChannelMediaDecoder.h
dom/media/ChannelMediaResource.h
dom/media/MediaCache.h
dom/media/MediaDataDemuxer.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/MediaResource.cpp
dom/media/MediaResource.h
dom/media/MediaResourceCallback.h
dom/media/eme/MediaKeySession.h
dom/media/eme/MediaKeys.cpp
dom/media/eme/MediaKeys.h
dom/media/flac/FlacDemuxer.cpp
dom/media/flac/FlacDemuxer.h
dom/media/gtest/MockMediaResource.h
dom/media/gtest/TestMP3Demuxer.cpp
dom/media/gtest/mp4_demuxer/TestParser.cpp
dom/media/hls/HLSDemuxer.h
dom/media/ipc/RemoteVideoDecoder.h
dom/media/mediasource/ContainerParser.cpp
dom/media/mediasource/ContainerParser.h
dom/media/mediasource/MediaSource.cpp
dom/media/mediasource/MediaSource.h
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceDecoder.h
dom/media/mediasource/MediaSourceDemuxer.cpp
dom/media/mediasource/MediaSourceDemuxer.h
dom/media/mediasource/SourceBuffer.cpp
dom/media/mediasource/SourceBuffer.h
dom/media/mediasource/SourceBufferResource.h
dom/media/mediasource/TrackBuffersManager.cpp
dom/media/mediasource/TrackBuffersManager.h
dom/media/mp3/MP3Demuxer.cpp
dom/media/mp3/MP3Demuxer.h
dom/media/mp4/BufferStream.h
dom/media/mp4/ByteStream.h
dom/media/mp4/MP4Demuxer.cpp
dom/media/mp4/MP4Demuxer.h
dom/media/mp4/MP4Metadata.cpp
dom/media/mp4/MP4Metadata.h
dom/media/mp4/MoofParser.cpp
dom/media/mp4/MoofParser.h
dom/media/mp4/ResourceStream.cpp
dom/media/mp4/ResourceStream.h
dom/media/ogg/OggDemuxer.cpp
dom/media/ogg/OggDemuxer.h
dom/media/platforms/PlatformDecoderModule.h
dom/media/platforms/agnostic/AOMDecoder.h
dom/media/platforms/agnostic/DummyMediaDataDecoder.h
dom/media/platforms/agnostic/OpusDecoder.h
dom/media/platforms/agnostic/TheoraDecoder.h
dom/media/platforms/agnostic/VPXDecoder.h
dom/media/platforms/agnostic/VorbisDecoder.h
dom/media/platforms/agnostic/WAVDecoder.h
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
dom/media/platforms/agnostic/eme/EMEDecoderModule.h
dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
dom/media/platforms/android/RemoteDataDecoder.h
dom/media/platforms/apple/AppleATDecoder.h
dom/media/platforms/apple/AppleVTDecoder.h
dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
dom/media/platforms/omx/OmxDataDecoder.h
dom/media/platforms/wmf/WMFMediaDataDecoder.h
dom/media/platforms/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
dom/media/platforms/wrappers/MediaDataDecoderProxy.h
dom/media/wave/WaveDemuxer.cpp
dom/media/wave/WaveDemuxer.h
dom/media/webm/WebMDemuxer.cpp
dom/media/webm/WebMDemuxer.h
--- a/dom/html/HTMLAudioElement.cpp
+++ b/dom/html/HTMLAudioElement.cpp
@@ -25,20 +25,22 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ELEMENT_CLONE(HTMLAudioElement)
 
 HTMLAudioElement::HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo)
   : HTMLMediaElement(aNodeInfo)
 {
+  DecoderDoctorLogger::LogConstruction(this);
 }
 
 HTMLAudioElement::~HTMLAudioElement()
 {
+  DecoderDoctorLogger::LogDestruction(this);
 }
 
 bool
 HTMLAudioElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::controls) ||
          HTMLMediaElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1750,16 +1750,17 @@ void HTMLMediaElement::ShutdownDecoder()
   RemoveMediaElementFromURITable();
   NS_ASSERTION(mDecoder, "Must have decoder to shut down");
 
   mWaitingForKeyListener.DisconnectIfExists();
   if (mMediaSource) {
     mMediaSource->CompletePendingTransactions();
   }
   mDecoder->Shutdown();
+  DDUNLINKCHILD(mDecoder.get());
   mDecoder = nullptr;
 }
 
 void HTMLMediaElement::AbortExistingLoads()
 {
   // Abort any already-running instance of the resource selection algorithm.
   mLoadWaitStatus = NOT_WAITING;
 
@@ -1802,16 +1803,18 @@ void HTMLMediaElement::AbortExistingLoad
   }
   if (mSrcStream) {
     EndSrcMediaStreamPlayback();
   }
 
   RemoveMediaElementFromURITable();
   mLoadingSrc = nullptr;
   mLoadingSrcTriggeringPrincipal = nullptr;
+  DDLOG(DDLogCategory::Property, "loading_src", "");
+  DDUNLINKCHILD(mMediaSource.get());
   mMediaSource = nullptr;
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
   {
     DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
   }
 
@@ -2074,17 +2077,21 @@ void HTMLMediaElement::SelectResource()
     if (NS_SUCCEEDED(rv)) {
       LOG(LogLevel::Debug, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
       NS_ASSERTION(!mIsLoadingFromSourceChildren,
         "Should think we're not loading from source children by default");
 
       RemoveMediaElementFromURITable();
       mLoadingSrc = uri;
       mLoadingSrcTriggeringPrincipal = mSrcAttrTriggeringPrincipal;
+      DDLOG(DDLogCategory::Property,
+            "loading_src",
+            nsCString(NS_ConvertUTF16toUTF8(src)));
       mMediaSource = mSrcMediaSource;
+      DDLINKCHILD("mediasource", mMediaSource.get());
       UpdatePreloadAction();
       if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE &&
           !IsMediaStreamURI(mLoadingSrc) && !mMediaSource) {
         // preload:none media, suspend the load here before we make any
         // network requests.
         SuspendLoad();
         return;
       }
@@ -2398,17 +2405,21 @@ void HTMLMediaElement::LoadFromSourceChi
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       DealWithFailedElement(child);
       return;
     }
 
     RemoveMediaElementFromURITable();
     mLoadingSrc = uri;
     mLoadingSrcTriggeringPrincipal = childSrc->GetSrcTriggeringPrincipal();
+    DDLOG(DDLogCategory::Property,
+          "loading_src",
+          nsCString(NS_ConvertUTF16toUTF8(src)));
     mMediaSource = childSrc->GetSrcMediaSource();
+    DDLINKCHILD("mediasource", mMediaSource.get());
     NS_ASSERTION(mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING,
                  "Network state should be loading");
 
     if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE &&
         !IsMediaStreamURI(mLoadingSrc) && !mMediaSource) {
       // preload:none media, suspend the load here before we make any
       // network requests.
       SuspendLoad();
@@ -4071,16 +4082,18 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mMediaTracksConstructed(false),
     mVisibilityState(Visibility::UNTRACKED),
     mErrorSink(new ErrorSink(this)),
     mAudioChannelWrapper(new AudioChannelAgentCallback(this))
 {
   MOZ_ASSERT(mMainThreadEventTarget);
   MOZ_ASSERT(mAbstractMainThread);
 
+  DecoderDoctorLogger::LogConstruction(this);
+
   ErrorResult rv;
 
   double defaultVolume = Preferences::GetFloat("media.default_volume", 1.0);
   SetVolume(defaultVolume, rv);
 
   RegisterActivityObserver();
   NotifyOwnerDocumentActivityChanged();
 
@@ -4146,16 +4159,18 @@ HTMLMediaElement::~HTMLMediaElement()
   }
 
   if (mAudioChannelWrapper) {
     mAudioChannelWrapper->Shutdown();
     mAudioChannelWrapper = nullptr;
   }
 
   WakeLockRelease();
+
+  DecoderDoctorLogger::LogDestruction(this);
 }
 
 void HTMLMediaElement::StopSuspendingAfterFirstFrame()
 {
   mAllowSuspendAfterFirstFrame = false;
   if (!mSuspendedAfterFirstFrame)
     return;
   mSuspendedAfterFirstFrame = false;
@@ -6111,16 +6126,18 @@ HTMLMediaElement::ChangeReadyState(nsMed
     return;
   }
 
   nsMediaReadyState oldState = mReadyState;
   mReadyState = aState;
   LOG(LogLevel::Debug,
       ("%p Ready state changed to %s", this, gReadyStateToString[aState]));
 
+  DDLOG(DDLogCategory::Property, "ready_state", gReadyStateToString[aState]);
+
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     return;
   }
 
   UpdateAudioChannelPlayingState();
 
   // Handle raising of "waiting" event during seek (see 4.8.10.9)
   // or
@@ -6177,16 +6194,18 @@ void HTMLMediaElement::ChangeNetworkStat
 {
   if (mNetworkState == aState) {
     return;
   }
 
   nsMediaNetworkState oldState = mNetworkState;
   mNetworkState = aState;
   LOG(LogLevel::Debug, ("%p Network state changed to %s", this, gNetworkStateToString[aState]));
+  DDLOG(
+    DDLogCategory::Property, "network_state", gNetworkStateToString[aState]);
 
   if (oldState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
     // Stop progress notification when exiting NETWORK_LOADING.
     StopProgress();
   }
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
     // Start progress notification when entering NETWORK_LOADING.
@@ -6394,16 +6413,19 @@ nsresult HTMLMediaElement::DispatchEvent
                                               false);
 }
 
 void
 HTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
 {
   LOG_EVENT(LogLevel::Debug, ("%p Queuing event %s", this,
             NS_ConvertUTF16toUTF8(aName).get()));
+  DDLOG(DDLogCategory::Event,
+        "HTMLMediaElement",
+        nsCString(NS_ConvertUTF16toUTF8(aName)));
 
   // Save events that occur while in the bfcache. These will be dispatched
   // if the page comes out of the bfcache.
   if (mEventDeliveryPaused) {
     mPendingEvents.AppendElement(aName);
     return;
   }
 
@@ -6644,16 +6666,17 @@ void HTMLMediaElement::NotifyOwnerDocume
   }
 
   bool pauseElement = ShouldElementBePaused();
   SuspendOrResumeElement(pauseElement, !IsActive());
 
   // If the owning document has become inactive we should shutdown the CDM.
   if (!OwnerDoc()->IsCurrentActiveDocument() && mMediaKeys) {
       mMediaKeys->Shutdown();
+      DDUNLINKCHILD(mMediaKeys.get());
       mMediaKeys = nullptr;
       if (mDecoder) {
         ShutdownDecoder();
       }
     }
 
   AddRemoveSelfReference();
 }
@@ -7544,16 +7567,17 @@ HTMLMediaElement::NextFrameStatus()
 void
 HTMLMediaElement::SetDecoder(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(aDecoder); // Use ShutdownDecoder() to clear.
   if (mDecoder) {
     ShutdownDecoder();
   }
   mDecoder = aDecoder;
+  DDLINKCHILD("decoder", mDecoder.get());
 }
 
 float
 HTMLMediaElement::ComputedVolume() const
 {
   return mMuted ? 0.0f : mAudioChannelWrapper ?
     mAudioChannelWrapper->GetEffectiveVolume() : mVolume;
 }
--- a/dom/html/HTMLVideoElement.cpp
+++ b/dom/html/HTMLVideoElement.cpp
@@ -43,20 +43,22 @@ namespace dom {
 static bool sVideoStatsEnabled;
 
 NS_IMPL_ELEMENT_CLONE(HTMLVideoElement)
 
 HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo)
   : HTMLMediaElement(aNodeInfo)
   , mIsOrientationLocked(false)
 {
+  DecoderDoctorLogger::LogConstruction(this);
 }
 
 HTMLVideoElement::~HTMLVideoElement()
 {
+  DecoderDoctorLogger::LogDestruction(this);
 }
 
 nsresult HTMLVideoElement::GetVideoSize(nsIntSize* size)
 {
   if (!mMediaInfo.HasVideo()) {
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -258,23 +258,25 @@ InitAudioSpecificConfig(const Frame& fra
 
 using media::TimeUnit;
 
 // ADTSDemuxer
 
 ADTSDemuxer::ADTSDemuxer(MediaResource* aSource)
   : mSource(aSource)
 {
+  DDLINKCHILD("source", aSource);
 }
 
 bool
 ADTSDemuxer::InitInternal()
 {
   if (!mTrackDemuxer) {
     mTrackDemuxer = new ADTSTrackDemuxer(mSource);
+    DDLINKCHILD("track demuxer", mTrackDemuxer.get());
   }
   return mTrackDemuxer->Init();
 }
 
 RefPtr<ADTSDemuxer::InitPromise>
 ADTSDemuxer::Init()
 {
   if (!InitInternal()) {
@@ -321,16 +323,17 @@ ADTSTrackDemuxer::ADTSTrackDemuxer(Media
   , mOffset(0)
   , mNumParsedFrames(0)
   , mFrameIndex(0)
   , mTotalFrameLen(0)
   , mSamplesPerFrame(0)
   , mSamplesPerSecond(0)
   , mChannels(0)
 {
+  DDLINKCHILD("source", aSource);
   Reset();
 }
 
 ADTSTrackDemuxer::~ADTSTrackDemuxer()
 {
   delete mParser;
 }
 
--- a/dom/media/ADTSDemuxer.h
+++ b/dom/media/ADTSDemuxer.h
@@ -16,17 +16,21 @@ namespace mozilla {
 
 namespace adts {
 class Frame;
 class FrameParser;
 }
 
 class ADTSTrackDemuxer;
 
-class ADTSDemuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(ADTSDemuxer, MediaDataDemuxer);
+
+class ADTSDemuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<ADTSDemuxer>
 {
 public:
   // MediaDataDemuxer interface.
   explicit ADTSDemuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer>
   GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
@@ -37,17 +41,21 @@ public:
 
 private:
   bool InitInternal();
 
   RefPtr<MediaResource> mSource;
   RefPtr<ADTSTrackDemuxer> mTrackDemuxer;
 };
 
-class ADTSTrackDemuxer : public MediaTrackDemuxer
+DDLoggedTypeNameAndBase(ADTSTrackDemuxer, MediaTrackDemuxer);
+
+class ADTSTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<ADTSTrackDemuxer>
 {
 public:
   explicit ADTSTrackDemuxer(MediaResource* aSource);
 
   // Initializes the track demuxer by reading the first frame for meta data.
   // Returns initialization success state.
   bool Init();
 
--- a/dom/media/BaseMediaResource.h
+++ b/dom/media/BaseMediaResource.h
@@ -12,17 +12,21 @@
 #include "nsIChannel.h"
 #include "nsIURI.h"
 #include "nsIStreamListener.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 
-class BaseMediaResource : public MediaResource
+DDLoggedTypeDeclNameAndBase(BaseMediaResource, MediaResource);
+
+class BaseMediaResource
+  : public MediaResource
+  , public DecoderDoctorLifeLogger<BaseMediaResource>
 {
 public:
   /**
    * Create a resource, reading data from the channel. Call on main thread only.
    * The caller must follow up by calling resource->Open().
    */
   static already_AddRefed<BaseMediaResource> Create(
     MediaResourceCallback* aCallback,
--- a/dom/media/BufferMediaResource.h
+++ b/dom/media/BufferMediaResource.h
@@ -7,21 +7,25 @@
 #define BufferMediaResource_h_
 
 #include "MediaResource.h"
 #include "nsISeekableStream.h"
 #include <algorithm>
 
 namespace mozilla {
 
+DDLoggedTypeDeclNameAndBase(BufferMediaResource, MediaResource);
+
 // A simple MediaResource based on an in memory buffer.  This class accepts
 // the address and the length of the buffer, and simulates a read/seek API
 // on top of it.  The Read implementation involves copying memory, which is
 // unfortunate, but the MediaResource interface mandates that.
-class BufferMediaResource : public MediaResource
+class BufferMediaResource
+  : public MediaResource
+  , public DecoderDoctorLifeLogger<BufferMediaResource>
 {
 public:
   BufferMediaResource(const uint8_t* aBuffer, uint32_t aLength)
     : mBuffer(aBuffer)
     , mLength(aLength)
   {
   }
 
--- a/dom/media/ChannelMediaDecoder.cpp
+++ b/dom/media/ChannelMediaDecoder.cpp
@@ -18,31 +18,45 @@ extern LazyLogModule gMediaDecoderLog;
   MOZ_LOG(                                                                     \
     gMediaDecoderLog, LogLevel::Debug, ("Decoder=%p " x, this, ##__VA_ARGS__))
 
 ChannelMediaDecoder::ResourceCallback::ResourceCallback(
   AbstractThread* aMainThread)
   : mAbstractMainThread(aMainThread)
 {
   MOZ_ASSERT(aMainThread);
+  DecoderDoctorLogger::LogConstructionAndBase(
+    "ChannelMediaDecoder::ResourceCallback",
+    this,
+    static_cast<const MediaResourceCallback*>(this));
+}
+
+ChannelMediaDecoder::ResourceCallback::~ResourceCallback()
+{
+  DecoderDoctorLogger::LogDestruction("ChannelMediaDecoder::ResourceCallback",
+                                      this);
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::Connect(ChannelMediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mDecoder = aDecoder;
+  DecoderDoctorLogger::LinkParentAndChild(
+    "ChannelMediaDecoder::ResourceCallback", this, "decoder", mDecoder);
   mTimer = NS_NewTimer(mAbstractMainThread->AsEventTarget());
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::Disconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mDecoder) {
+    DecoderDoctorLogger::UnlinkParentAndChild(
+      "ChannelMediaDecoder::ResourceCallback", this, mDecoder);
     mDecoder = nullptr;
     mTimer->Cancel();
     mTimer = nullptr;
   }
 }
 
 AbstractThread*
 ChannelMediaDecoder::ResourceCallback::AbstractMainThread() const
@@ -57,16 +71,21 @@ ChannelMediaDecoder::ResourceCallback::G
   return mDecoder ? mDecoder->GetOwner() : nullptr;
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::NotifyNetworkError(
   const MediaResult& aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  DDLOGEX2("ChannelMediaDecoder::ResourceCallback",
+           this,
+           DDLogCategory::Log,
+           "network_error",
+           aError);
   if (mDecoder) {
     mDecoder->NetworkError(aError);
   }
 }
 
 /* static */ void
 ChannelMediaDecoder::ResourceCallback::TimerCallback(nsITimer* aTimer,
                                                      void* aClosure)
@@ -77,16 +96,22 @@ ChannelMediaDecoder::ResourceCallback::T
   thiz->mDecoder->NotifyReaderDataArrived();
   thiz->mTimerArmed = false;
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::NotifyDataArrived()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  DDLOGEX2("ChannelMediaDecoder::ResourceCallback",
+           this,
+           DDLogCategory::Log,
+           "data_arrived",
+           true);
+
   if (!mDecoder) {
     return;
   }
 
   mDecoder->DownloadProgressed();
 
   if (mTimerArmed) {
     return;
@@ -99,36 +124,51 @@ ChannelMediaDecoder::ResourceCallback::N
   mTimer->InitWithNamedFuncCallback(
     TimerCallback, this, sDelay, nsITimer::TYPE_ONE_SHOT,
     "ChannelMediaDecoder::ResourceCallback::TimerCallback");
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::NotifyDataEnded(nsresult aStatus)
 {
+  DDLOGEX2("ChannelMediaDecoder::ResourceCallback",
+           this,
+           DDLogCategory::Log,
+           "data_ended",
+           aStatus);
   MOZ_ASSERT(NS_IsMainThread());
   if (mDecoder) {
     mDecoder->NotifyDownloadEnded(aStatus);
   }
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::NotifyPrincipalChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  DDLOGEX2("ChannelMediaDecoder::ResourceCallback",
+           this,
+           DDLogCategory::Log,
+           "principal_changed",
+           true);
   if (mDecoder) {
     mDecoder->NotifyPrincipalChanged();
   }
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::NotifySuspendedStatusChanged(
   bool aSuspendedByCache)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  DDLOGEX2("ChannelMediaDecoder::ResourceCallback",
+           this,
+           DDLogCategory::Log,
+           "suspended_status_changed",
+           aSuspendedByCache);
   MediaDecoderOwner* owner = GetMediaOwner();
   if (owner) {
     AbstractThread::AutoEnter context(owner->AbstractMainThread());
     owner->NotifySuspendedByCache(aSuspendedByCache);
   }
 }
 
 void
@@ -241,16 +281,17 @@ ChannelMediaDecoder::Load(nsIChannel* aC
   MOZ_ASSERT(!mResource);
   MOZ_ASSERT(aStreamListener);
 
   mResource =
     BaseMediaResource::Create(mResourceCallback, aChannel, aIsPrivateBrowsing);
   if (!mResource) {
     return NS_ERROR_FAILURE;
   }
+  DDLINKCHILD("resource", mResource.get());
 
   nsresult rv = MediaShutdownManager::Instance().Register(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = mResource->Open(aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -266,16 +307,17 @@ ChannelMediaDecoder::Load(BaseMediaResou
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mResource);
 
   mResource = aOriginal->CloneData(mResourceCallback);
   if (!mResource) {
     return NS_ERROR_FAILURE;
   }
+  DDLINKCHILD("resource", mResource.get());
 
   nsresult rv = MediaShutdownManager::Instance().Register(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   SetStateMachine(CreateStateMachine());
   NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
--- a/dom/media/ChannelMediaDecoder.h
+++ b/dom/media/ChannelMediaDecoder.h
@@ -13,17 +13,21 @@
 
 class nsIChannel;
 class nsIStreamListener;
 
 namespace mozilla {
 
 class BaseMediaResource;
 
-class ChannelMediaDecoder : public MediaDecoder
+DDLoggedTypeDeclNameAndBase(ChannelMediaDecoder, MediaDecoder);
+
+class ChannelMediaDecoder
+  : public MediaDecoder
+  , public DecoderDoctorLifeLogger<ChannelMediaDecoder>
 {
   // Used to register with MediaResource to receive notifications which will
   // be forwarded to MediaDecoder.
   class ResourceCallback : public MediaResourceCallback
   {
     // Throttle calls to MediaDecoder::NotifyDataArrived()
     // to be at most once per 500ms.
     static const uint32_t sDelay = 500;
@@ -31,16 +35,18 @@ class ChannelMediaDecoder : public Media
   public:
     explicit ResourceCallback(AbstractThread* aMainThread);
     // Start to receive notifications from ResourceCallback.
     void Connect(ChannelMediaDecoder* aDecoder);
     // Called upon shutdown to stop receiving notifications.
     void Disconnect();
 
   private:
+    ~ResourceCallback();
+
     /* MediaResourceCallback functions */
     AbstractThread* AbstractMainThread() const override;
     MediaDecoderOwner* GetMediaOwner() const override;
     void NotifyNetworkError(const MediaResult& aError) override;
     void NotifyDataArrived() override;
     void NotifyDataEnded(nsresult aStatus) override;
     void NotifyPrincipalChanged() override;
     void NotifySuspendedStatusChanged(bool aSuspendedByCache) override;
--- a/dom/media/ChannelMediaResource.h
+++ b/dom/media/ChannelMediaResource.h
@@ -48,25 +48,29 @@ private:
   void SuspendInternal();
 
   nsIChannel* mChannel = nullptr;
   MediaCacheStream& mCacheStream;
   uint32_t mSuspendCount = 0;
   bool mIsChannelSuspended = false;
 };
 
+DDLoggedTypeDeclNameAndBase(ChannelMediaResource, BaseMediaResource);
+
 /**
  * This is the MediaResource implementation that wraps Necko channels.
  * Much of its functionality is actually delegated to MediaCache via
  * an underlying MediaCacheStream.
  *
  * All synchronization is performed by MediaCacheStream; all off-main-
  * thread operations are delegated directly to that object.
  */
-class ChannelMediaResource : public BaseMediaResource
+class ChannelMediaResource
+  : public BaseMediaResource
+  , public DecoderDoctorLifeLogger<ChannelMediaResource>
 {
 public:
   ChannelMediaResource(MediaResourceCallback* aDecoder,
                        nsIChannel* aChannel,
                        nsIURI* aURI,
                        bool aIsPrivateBrowsing = false);
   ~ChannelMediaResource();
 
--- a/dom/media/MediaCache.h
+++ b/dom/media/MediaCache.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #ifndef MediaCache_h_
 #define MediaCache_h_
 
+#include "DecoderDoctorLogger.h"
 #include "Intervals.h"
 #include "mozilla/Result.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 
@@ -176,23 +177,26 @@ class ReentrantMonitorAutoEnter;
  * can trigger new HTTP requests; due to redirects to other domains,
  * each HTTP load can return data with a different principal. This
  * principal must be passed to NotifyDataReceived, and MediaCache
  * will detect when different principals are associated with data in the
  * same stream, and replace them with a null principal.
  */
 class MediaCache;
 
+DDLoggedTypeDeclName(MediaCacheStream);
+
 /**
  * If the cache fails to initialize then Init will fail, so nonstatic
  * methods of this class can assume gMediaCache is non-null.
  *
  * This class can be directly embedded as a value.
  */
-class MediaCacheStream {
+class MediaCacheStream : public DecoderDoctorLifeLogger<MediaCacheStream>
+{
   using AutoLock = ReentrantMonitorAutoEnter;
 
 public:
   // This needs to be a power of two
   static const int64_t BLOCK_SIZE = 32768;
 
   enum ReadMode {
     MODE_METADATA,
--- a/dom/media/MediaDataDemuxer.h
+++ b/dom/media/MediaDataDemuxer.h
@@ -2,37 +2,41 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #if !defined(MediaDataDemuxer_h)
 #define MediaDataDemuxer_h
 
+#include "DecoderDoctorLogger.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/UniquePtr.h"
 
 #include "MediaData.h"
 #include "MediaInfo.h"
 #include "MediaResult.h"
 #include "TimeUnits.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/RefPtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
 class MediaTrackDemuxer;
 class TrackMetadataHolder;
 
+DDLoggedTypeDeclName(MediaDataDemuxer);
+DDLoggedTypeName(MediaTrackDemuxer);
+
 // Allows reading the media data: to retrieve the metadata and demux samples.
 // MediaDataDemuxer isn't designed to be thread safe.
 // When used by the MediaFormatDecoder, care is taken to ensure that the demuxer
 // will never be called from more than one thread at once.
-class MediaDataDemuxer
+class MediaDataDemuxer : public DecoderDoctorLifeLogger<MediaDataDemuxer>
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataDemuxer)
 
   typedef
     MozPromise<MediaResult, MediaResult, /* IsExclusive = */ true> InitPromise;
 
   // Initializes the demuxer. Other methods cannot be called unless
@@ -89,17 +93,17 @@ public:
   virtual bool ShouldComputeStartTime() const { return true; }
 
 protected:
   virtual ~MediaDataDemuxer()
   {
   }
 };
 
-class MediaTrackDemuxer
+class MediaTrackDemuxer : public DecoderDoctorLifeLogger<MediaTrackDemuxer>
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaTrackDemuxer)
 
   class SamplesHolder
   {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SamplesHolder)
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -835,16 +835,20 @@ MediaDecoder::ChangeState(PlayState aSta
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!IsShutdown(), "SHUTDOWN is the final state.");
   AbstractThread::AutoEnter context(AbstractMainThread());
 
   if (mNextState == aState) {
     mNextState = PLAY_STATE_PAUSED;
   }
 
+  if (mPlayState != aState) {
+    DDLOG(DDLogCategory::Property, "play_state", ToPlayStateStr(aState));
+  }
+
   LOG("ChangeState %s => %s", PlayStateStr(), ToPlayStateStr(aState));
   mPlayState = aState;
 
   if (mPlayState == PLAY_STATE_PLAYING) {
     GetOwner()->ConstructMediaTracks(mInfo);
   } else if (IsEnded()) {
     GetOwner()->RemoveMediaTracks();
   }
@@ -857,16 +861,17 @@ MediaDecoder::UpdateLogicalPositionInter
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
 
   double currentPosition = CurrentPosition().ToSeconds();
   if (mPlayState == PLAY_STATE_ENDED) {
     currentPosition = std::max(currentPosition, mDuration);
   }
   bool logicalPositionChanged = mLogicalPosition != currentPosition;
   mLogicalPosition = currentPosition;
+  DDLOG(DDLogCategory::Property, "currentTime", mLogicalPosition);
 
   // Invalidate the frame so any video data is displayed.
   // Do this before the timeupdate event so that if that
   // event runs JavaScript that queries the media size, the
   // frame has reflowed and the size updated beforehand.
   Invalidate();
 
   if (logicalPositionChanged) {
@@ -1146,21 +1151,24 @@ MediaDecoder::DisconnectMirrors()
   mIsAudioDataAudible.DisconnectIfConnected();
 }
 
 void
 MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
-  mDecoderStateMachine = aStateMachine;
   if (aStateMachine) {
+    mDecoderStateMachine = aStateMachine;
+    DDLINKCHILD("decoder state machine", mDecoderStateMachine.get());
     ConnectMirrors(aStateMachine);
     UpdateVideoDecodeMode();
-  } else {
+  } else if (mDecoderStateMachine) {
+    DDUNLINKCHILD(mDecoderStateMachine.get());
+    mDecoderStateMachine = nullptr;
     DisconnectMirrors();
   }
 }
 
 ImageContainer*
 MediaDecoder::GetImageContainer()
 {
   return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer()
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -76,17 +76,19 @@ struct MOZ_STACK_CLASS MediaDecoderInit
     , mMinimizePreroll(aMinimizePreroll)
     , mHasSuspendTaint(aHasSuspendTaint)
     , mLooping(aLooping)
     , mContainerType(aContainerType)
   {
   }
 };
 
-class MediaDecoder
+DDLoggedTypeDeclName(MediaDecoder);
+
+class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder>
 {
 public:
   typedef MozPromise<bool /* aIgnored */, bool /* aIgnored */,
                      /* IsExclusive = */ true>
     SeekPromise;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoder)
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1926,16 +1926,20 @@ public:
     if (!mSentPlaybackEndedEvent) {
       auto clockTime =
         std::max(mMaster->AudioEndTime(), mMaster->VideoEndTime());
       // Correct the time over the end once looping was turned on.
       Reader()->AdjustByLooping(clockTime);
       if (mMaster->mDuration.Ref()->IsInfinite()) {
         // We have a finite duration when playback reaches the end.
         mMaster->mDuration = Some(clockTime);
+        DDLOGEX(mMaster,
+                DDLogCategory::Property,
+                "duration_us",
+                mMaster->mDuration.Ref()->ToMicroseconds());
       }
       mMaster->UpdatePlaybackPosition(clockTime);
 
       // Ensure readyState is updated before firing the 'ended' event.
       mMaster->mOnNextFrameStatus.Notify(
         MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE);
 
       mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackEnded);
@@ -2168,16 +2172,21 @@ DecodeMetadataState::OnMetadataRead(Meta
     mMaster->mDuration = Info().mMetadataDuration;
   }
 
   // If we don't know the duration by this point, we assume infinity, per spec.
   if (mMaster->mDuration.Ref().isNothing()) {
     mMaster->mDuration = Some(TimeUnit::FromInfinity());
   }
 
+  DDLOGEX(mMaster,
+          DDLogCategory::Property,
+          "duration_us",
+          mMaster->mDuration.Ref()->ToMicroseconds());
+
   if (mMaster->HasVideo()) {
     SLOG("Video decode HWAccel=%d videoQueueSize=%d",
          Reader()->VideoIsHardwareAccelerated(),
          mMaster->GetAmpleVideoFrames());
   }
 
   MOZ_ASSERT(mMaster->mDuration.Ref().isSome());
 
@@ -2682,16 +2691,18 @@ MediaDecoderStateMachine::MediaDecoderSt
 #ifdef XP_WIN
   , mShouldUseHiResTimers(Preferences::GetBool("media.hi-res-timers.enabled", true))
 #endif
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   InitVideoQueuePrefs();
+
+  DDLINKCHILD("reader", aReader);
 }
 
 #undef INIT_WATCHABLE
 #undef INIT_MIRROR
 #undef INIT_CANONICAL
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
@@ -2956,17 +2967,22 @@ void
 MediaDecoderStateMachine::UpdatePlaybackPositionInternal(const TimeUnit& aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("UpdatePlaybackPositionInternal(%" PRId64 ")", aTime.ToMicroseconds());
 
   mCurrentPosition = aTime;
   NS_ASSERTION(mCurrentPosition.Ref() >= TimeUnit::Zero(),
                "CurrentTime should be positive!");
-  mDuration = Some(std::max(mDuration.Ref().ref(), mCurrentPosition.Ref()));
+  if (mDuration.Ref().ref() < mCurrentPosition.Ref()) {
+    mDuration = Some(mCurrentPosition.Ref());
+    DDLOG(DDLogCategory::Property,
+          "duration_us",
+          mDuration.Ref()->ToMicroseconds());
+  }
 }
 
 void
 MediaDecoderStateMachine::UpdatePlaybackPosition(const TimeUnit& aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   UpdatePlaybackPositionInternal(aTime);
 
@@ -3109,16 +3125,19 @@ void MediaDecoderStateMachine::BufferedR
     return;
   }
 
   // Use estimated duration from buffer ranges when mDuration is unknown or
   // the estimated duration is larger.
   if (mDuration.Ref().isNothing() || mDuration.Ref()->IsInfinite() ||
       end > mDuration.Ref().ref()) {
     mDuration = Some(end);
+    DDLOG(DDLogCategory::Property,
+          "duration_us",
+          mDuration.Ref()->ToMicroseconds());
   }
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -151,28 +151,31 @@ struct MediaPlaybackEvent
 };
 
 enum class VideoDecodeMode : uint8_t
 {
   Normal,
   Suspend
 };
 
+DDLoggedTypeDeclName(MediaDecoderStateMachine);
+
 /*
   The state machine class. This manages the decoding and seeking in the
   MediaDecoderReader on the decode task queue, and A/V sync on the shared
   state machine thread, and controls the audio "push" thread.
 
   All internal state is synchronised via the decoder monitor. State changes
   are propagated by scheduling the state machine to run another cycle on the
   shared state machine thread.
 
   See MediaDecoder.h for more details.
 */
 class MediaDecoderStateMachine
+  : public DecoderDoctorLifeLogger<MediaDecoderStateMachine>
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine)
 
   using TrackSet = MediaFormatReader::TrackSet;
 
 public:
   typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
   typedef mozilla::layers::ImageContainer::FrameID FrameID;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -524,54 +524,82 @@ MediaFormatReader::DecoderData::Flush()
     TrackType type = mType == MediaData::AUDIO_DATA
                      ? TrackType::kAudioTrack
                      : TrackType::kVideoTrack;
     mFlushing = true;
     MOZ_DIAGNOSTIC_ASSERT(!mShutdownPromise);
     mShutdownPromise = new SharedShutdownPromiseHolder();
     RefPtr<SharedShutdownPromiseHolder> p = mShutdownPromise;
     RefPtr<MediaDataDecoder> d = mDecoder;
-    mDecoder->Flush()
-      ->Then(mOwner->OwnerThread(), __func__,
-             [type, this, p, d]() {
-               if (!p->IsEmpty()) {
-                 // Shutdown happened before flush completes. Let's continue to
-                 // shut down the decoder. Note we don't access |this| because
-                 // this decoder is no longer managed by MFR::DecoderData.
-                 d->Shutdown()->ChainTo(p->Steal(), __func__);
-                 return;
-               }
-               mFlushing = false;
-               mShutdownPromise = nullptr;
-               mOwner->ScheduleUpdate(type);
-             },
-             [type, this, p, d](const MediaResult& aError) {
-               if (!p->IsEmpty()) {
-                 d->Shutdown()->ChainTo(p->Steal(), __func__);
-                 return;
-               }
-               mFlushing = false;
-               mShutdownPromise = nullptr;
-               mOwner->NotifyError(type, aError);
-             });
+    DDLOGEX2("MediaFormatReader::DecoderData",
+             this,
+             DDLogCategory::Log,
+             "flushing",
+             DDNoValue{});
+    mDecoder->Flush()->Then(mOwner->OwnerThread(),
+                            __func__,
+                            [type, this, p, d]() {
+                              DDLOGEX2("MediaFormatReader::DecoderData",
+                                       this,
+                                       DDLogCategory::Log,
+                                       "flushed",
+                                       DDNoValue{});
+                              if (!p->IsEmpty()) {
+                                // Shutdown happened before flush completes.
+                                // Let's continue to shut down the decoder. Note
+                                // we don't access |this| because this decoder
+                                // is no longer managed by MFR::DecoderData.
+                                d->Shutdown()->ChainTo(p->Steal(), __func__);
+                                return;
+                              }
+                              mFlushing = false;
+                              mShutdownPromise = nullptr;
+                              mOwner->ScheduleUpdate(type);
+                            },
+                            [type, this, p, d](const MediaResult& aError) {
+                              DDLOGEX2("MediaFormatReader::DecoderData",
+                                       this,
+                                       DDLogCategory::Log,
+                                       "flush_error",
+                                       aError);
+                              if (!p->IsEmpty()) {
+                                d->Shutdown()->ChainTo(p->Steal(), __func__);
+                                return;
+                              }
+                              mFlushing = false;
+                              mShutdownPromise = nullptr;
+                              mOwner->NotifyError(type, aError);
+                            });
   }
   mFlushed = true;
 }
 
 class MediaFormatReader::DecoderFactory
 {
   using InitPromise = MediaDataDecoder::InitPromise;
   using TokenPromise = GlobalAllocPolicy::Promise;
   using Token = GlobalAllocPolicy::Token;
 
 public:
   explicit DecoderFactory(MediaFormatReader* aOwner)
     : mAudio(aOwner->mAudio, TrackInfo::kAudioTrack, aOwner->OwnerThread())
     , mVideo(aOwner->mVideo, TrackInfo::kVideoTrack, aOwner->OwnerThread())
-    , mOwner(WrapNotNull(aOwner)) { }
+    , mOwner(WrapNotNull(aOwner))
+  {
+    DecoderDoctorLogger::LogConstruction("MediaFormatReader::DecoderFactory",
+                                         this);
+    DecoderDoctorLogger::LinkParentAndChild(
+      aOwner, "decoder factory", "MediaFormatReader::DecoderFactory", this);
+  }
+
+  ~DecoderFactory()
+  {
+    DecoderDoctorLogger::LogDestruction("MediaFormatReader::DecoderFactory",
+                                        this);
+  }
 
   void CreateDecoder(TrackType aTrack);
 
   // Shutdown any decoder pending initialization and reset mAudio/mVideo to its
   // pristine state so CreateDecoder() is ready to be called again immediately.
   void ShutdownDecoder(TrackType aTrack)
   {
     MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
@@ -632,17 +660,35 @@ MediaFormatReader::DecoderFactory::Creat
 
 class MediaFormatReader::DecoderFactory::Wrapper : public MediaDataDecoder
 {
   using Token = GlobalAllocPolicy::Token;
 
 public:
   Wrapper(already_AddRefed<MediaDataDecoder> aDecoder,
           already_AddRefed<Token> aToken)
-    : mDecoder(aDecoder), mToken(aToken) {}
+    : mDecoder(aDecoder)
+    , mToken(aToken)
+  {
+    DecoderDoctorLogger::LogConstructionAndBase(
+      "MediaFormatReader::DecoderFactory::Wrapper",
+      this,
+      static_cast<const MediaDataDecoder*>(this));
+    DecoderDoctorLogger::LinkParentAndChild(
+      "MediaFormatReader::DecoderFactory::Wrapper",
+      this,
+      "decoder",
+      mDecoder.get());
+  }
+
+  ~Wrapper()
+  {
+    DecoderDoctorLogger::LogDestruction(
+      "MediaFormatReader::DecoderFactory::Wrapper", this);
+  }
 
   RefPtr<InitPromise> Init() override { return mDecoder->Init(); }
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override
   {
     return mDecoder->Decode(aSample);
   }
   RefPtr<DecodePromise> Drain() override { return mDecoder->Drain(); }
   RefPtr<FlushPromise> Flush() override { return mDecoder->Flush(); }
@@ -712,21 +758,32 @@ MediaFormatReader::DecoderFactory::RunSt
       MOZ_ASSERT(!aData.mInitRequest.Exists());
 
       MediaResult rv = DoCreateDecoder(aData);
       if (NS_FAILED(rv)) {
         NS_WARNING("Error constructing decoders");
         aData.mToken = nullptr;
         aData.mStage = Stage::None;
         aData.mOwnerData.mDescription = rv.Description();
+        DDLOGEX2("MediaFormatReader::DecoderFactory",
+                 this,
+                 DDLogCategory::Log,
+                 "create_decoder_error",
+                 rv);
         mOwner->NotifyError(aData.mTrack, rv);
         return;
       }
 
       aData.mDecoder = new Wrapper(aData.mDecoder.forget(), aData.mToken.forget());
+      DecoderDoctorLogger::LinkParentAndChild(
+        aData.mDecoder.get(),
+        "decoder",
+        "MediaFormatReader::DecoderFactory",
+        this);
+
       DoInitDecoder(aData);
       aData.mStage = Stage::WaitForInit;
       break;
     }
 
     case Stage::WaitForInit: {
       MOZ_ASSERT(aData.mDecoder);
       MOZ_ASSERT(aData.mInitRequest.Exists());
@@ -801,33 +858,53 @@ MediaFormatReader::DecoderFactory::DoCre
   return result;
 }
 
 void
 MediaFormatReader::DecoderFactory::DoInitDecoder(Data& aData)
 {
   auto& ownerData = aData.mOwnerData;
 
+  DDLOGEX2("MediaFormatReader::DecoderFactory",
+           this,
+           DDLogCategory::Log,
+           "initialize_decoder",
+           DDNoValue{});
   aData.mDecoder->Init()
     ->Then(mOwner->OwnerThread(), __func__,
            [this, &aData, &ownerData](TrackType aTrack) {
              aData.mInitRequest.Complete();
              aData.mStage = Stage::None;
              MutexAutoLock lock(ownerData.mMutex);
              ownerData.mDecoder = aData.mDecoder.forget();
              ownerData.mDescription = ownerData.mDecoder->GetDescriptionName();
+             DDLOGEX2("MediaFormatReader::DecoderFactory",
+                      this,
+                      DDLogCategory::Log,
+                      "decoder_initialized",
+                      DDNoValue{});
+             DecoderDoctorLogger::LinkParentAndChild(
+               "MediaFormatReader::DecoderData",
+               &ownerData,
+               "decoder",
+               ownerData.mDecoder.get());
              mOwner->SetVideoDecodeThreshold();
              mOwner->ScheduleUpdate(aTrack);
            },
            [this, &aData, &ownerData](const MediaResult& aError) {
              aData.mInitRequest.Complete();
              MOZ_RELEASE_ASSERT(!ownerData.mDecoder,
                                 "Can't have a decoder already set");
              aData.mStage = Stage::None;
              mOwner->mShutdownPromisePool->ShutdownDecoder(aData.mDecoder.forget());
+             DDLOGEX2("MediaFormatReader::DecoderFactory",
+                      this,
+                      DDLogCategory::Log,
+                      "initialize_decoder_error",
+                      aError);
              mOwner->NotifyError(aData.mTrack, aError);
            })
     ->Track(aData.mInitRequest);
 }
 
 // DemuxerProxy ensures that the original main demuxer is only ever accessed
 // via its own dedicated task queue.
 // This ensure that the reader's taskqueue will never blocked while a demuxer
@@ -969,16 +1046,25 @@ class MediaFormatReader::DemuxerProxy::W
 public:
   Wrapper(MediaTrackDemuxer* aTrackDemuxer, AutoTaskQueue* aTaskQueue)
     : mMutex("TrackDemuxer Mutex")
     , mTaskQueue(aTaskQueue)
     , mGetSamplesMayBlock(aTrackDemuxer->GetSamplesMayBlock())
     , mInfo(aTrackDemuxer->GetInfo())
     , mTrackDemuxer(aTrackDemuxer)
   {
+    DecoderDoctorLogger::LogConstructionAndBase(
+      "MediaFormatReader::DemuxerProxy::Wrapper",
+      this,
+      static_cast<const MediaTrackDemuxer*>(this));
+    DecoderDoctorLogger::LinkParentAndChild(
+      "MediaFormatReader::DemuxerProxy::Wrapper",
+      this,
+      "track demuxer",
+      aTrackDemuxer);
   }
 
   UniquePtr<TrackInfo> GetInfo() const override
   {
     if (!mInfo) {
       return nullptr;
     }
     return mInfo->Clone();
@@ -1088,16 +1174,18 @@ private:
   ~Wrapper()
   {
     RefPtr<MediaTrackDemuxer> trackDemuxer = mTrackDemuxer.forget();
     nsresult rv =
       mTaskQueue->Dispatch(NS_NewRunnableFunction(
         "MediaFormatReader::DemuxerProxy::Wrapper::~Wrapper",
         [trackDemuxer]() { trackDemuxer->BreakCycles(); }));
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+    DecoderDoctorLogger::LogDestruction(
+      "MediaFormatReader::DemuxerProxy::Wrapper", this);
   }
 
   void UpdateRandomAccessPoint()
   {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     if (!mTrackDemuxer) {
       // Detached.
       return;
@@ -1145,28 +1233,38 @@ MediaFormatReader::DemuxerProxy::Init()
              if (data->mNumAudioTrack) {
                RefPtr<MediaTrackDemuxer> d =
                  data->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
                if (d) {
                  RefPtr<Wrapper> wrapper =
                    new DemuxerProxy::Wrapper(d, taskQueue);
                  wrapper->UpdateBuffered();
                  data->mAudioDemuxer = wrapper;
+                 DecoderDoctorLogger::LinkParentAndChild(
+                   data->mDemuxer.get(),
+                   "decoder factory wrapper",
+                   "MediaFormatReader::DecoderFactory::Wrapper",
+                   wrapper.get());
                }
              }
              data->mNumVideoTrack =
                data->mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
              if (data->mNumVideoTrack) {
                RefPtr<MediaTrackDemuxer> d =
                  data->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
                if (d) {
                  RefPtr<Wrapper> wrapper =
                    new DemuxerProxy::Wrapper(d, taskQueue);
                  wrapper->UpdateBuffered();
                  data->mVideoDemuxer = wrapper;
+                 DecoderDoctorLogger::LinkParentAndChild(
+                   data->mDemuxer.get(),
+                   "decoder factory wrapper",
+                   "MediaFormatReader::DecoderFactory::Wrapper",
+                   wrapper.get());
                }
              }
              data->mCrypto = data->mDemuxer->GetCrypto();
              data->mSeekable = data->mDemuxer->IsSeekable();
              data->mSeekableOnlyInBufferedRange =
                data->mDemuxer->IsSeekableOnlyInBufferedRanges();
              data->mShouldComputeStartTime =
                data->mDemuxer->ShouldComputeStartTime();
@@ -1222,16 +1320,21 @@ MediaFormatReader::MediaFormatReader(Med
   , mBuffered(mTaskQueue,
               TimeIntervals(),
               "MediaFormatReader::mBuffered (Canonical)")
   , mFrameStats(aInit.mFrameStats)
   , mMediaDecoderOwnerID(aInit.mMediaDecoderOwnerID)
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
+  DDLINKCHILD(
+    "audio decoder data", "MediaFormatReader::DecoderDataWithPromise", &mAudio);
+  DDLINKCHILD(
+    "video decoder data", "MediaFormatReader::DecoderDataWithPromise", &mVideo);
+  DDLINKCHILD("demuxer", aDemuxer);
   mOnTrackWaitingForKeyListener = OnTrackWaitingForKey().Connect(
     mTaskQueue, this, &MediaFormatReader::NotifyWaitingForKey);
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
   MOZ_COUNT_DTOR(MediaFormatReader);
   MOZ_ASSERT(mShutdown);
@@ -1746,56 +1849,79 @@ MediaFormatReader::OnDemuxFailed(TrackTy
   MOZ_ASSERT(OnTaskQueue());
   LOG("Failed to demux %s, failure:%s",
       aTrack == TrackType::kVideoTrack ? "video" : "audio",
       aError.ErrorName().get());
   auto& decoder = GetDecoderData(aTrack);
   decoder.mDemuxRequest.Complete();
   switch (aError.Code()) {
     case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
+      DDLOG(DDLogCategory::Log,
+            aTrack == TrackType::kVideoTrack ? "video_demux_interruption"
+                                             : "audio_demux_interruption",
+            aError);
       if (!decoder.mWaitingForData) {
         decoder.RequestDrain();
       }
       NotifyEndOfStream(aTrack);
       break;
     case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
+      DDLOG(DDLogCategory::Log,
+            aTrack == TrackType::kVideoTrack ? "video_demux_interruption"
+                                             : "audio_demux_interruption",
+            aError);
       if (!decoder.mWaitingForData) {
         decoder.RequestDrain();
       }
       NotifyWaitingForData(aTrack);
       break;
     case NS_ERROR_DOM_MEDIA_CANCELED:
+      DDLOG(DDLogCategory::Log,
+            aTrack == TrackType::kVideoTrack ? "video_demux_interruption"
+                                             : "audio_demux_interruption",
+            aError);
       if (decoder.HasPromise()) {
         decoder.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
       }
       break;
     default:
+      DDLOG(DDLogCategory::Log,
+            aTrack == TrackType::kVideoTrack ? "video_demux_error"
+                                             : "audio_demux_error",
+            aError);
       NotifyError(aTrack, aError);
       break;
   }
 }
 
 void
 MediaFormatReader::DoDemuxVideo()
 {
   using SamplesPromise = MediaTrackDemuxer::SamplesPromise;
 
+  DDLOG(DDLogCategory::Log, "video_demuxing", DDNoValue{});
   auto p = mVideo.mTrackDemuxer->GetSamples(1);
 
   if (mVideo.mFirstDemuxedSampleTime.isNothing()) {
     RefPtr<MediaFormatReader> self = this;
-    p = p->Then(OwnerThread(), __func__,
-                [self] (RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
-                  self->OnFirstDemuxCompleted(TrackInfo::kVideoTrack, aSamples);
-                  return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__);
-                },
-                [self] (const MediaResult& aError) {
-                  self->OnFirstDemuxFailed(TrackInfo::kVideoTrack, aError);
-                  return SamplesPromise::CreateAndReject(aError, __func__);
-                });
+    p = p->Then(
+      OwnerThread(),
+      __func__,
+      [self](RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
+        DDLOGEX(
+          self.get(), DDLogCategory::Log, "video_first_demuxed", DDNoValue{});
+        self->OnFirstDemuxCompleted(TrackInfo::kVideoTrack, aSamples);
+        return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__);
+      },
+      [self](const MediaResult& aError) {
+        DDLOGEX(
+          self.get(), DDLogCategory::Log, "video_first_demuxing_error", aError);
+        self->OnFirstDemuxFailed(TrackInfo::kVideoTrack, aError);
+        return SamplesPromise::CreateAndReject(aError, __func__);
+      });
   }
 
   p->Then(OwnerThread(), __func__, this,
           &MediaFormatReader::OnVideoDemuxCompleted,
           &MediaFormatReader::OnVideoDemuxFailed)
    ->Track(mVideo.mDemuxRequest);
 }
 
@@ -1803,16 +1929,19 @@ void
 MediaFormatReader::OnVideoDemuxCompleted(
   RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples)
 {
   LOGV("%zu video samples demuxed (sid:%d)",
        aSamples->mSamples.Length(),
        aSamples->mSamples[0]->mTrackInfo
        ? aSamples->mSamples[0]->mTrackInfo->GetID()
        : 0);
+  DDLOG(DDLogCategory::Log,
+        "video_demuxed_samples",
+        uint64_t(aSamples->mSamples.Length()));
   mVideo.mDemuxRequest.Complete();
   mVideo.mQueuedSamples.AppendElements(aSamples->mSamples);
   ScheduleUpdate(TrackInfo::kVideoTrack);
 }
 
 RefPtr<MediaFormatReader::AudioDataPromise>
 MediaFormatReader::RequestAudioData()
 {
@@ -1849,29 +1978,36 @@ MediaFormatReader::RequestAudioData()
   return p;
 }
 
 void
 MediaFormatReader::DoDemuxAudio()
 {
   using SamplesPromise = MediaTrackDemuxer::SamplesPromise;
 
+  DDLOG(DDLogCategory::Log, "audio_demuxing", DDNoValue{});
   auto p = mAudio.mTrackDemuxer->GetSamples(1);
 
   if (mAudio.mFirstDemuxedSampleTime.isNothing()) {
     RefPtr<MediaFormatReader> self = this;
-    p = p->Then(OwnerThread(), __func__,
-                [self] (RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
-                  self->OnFirstDemuxCompleted(TrackInfo::kAudioTrack, aSamples);
-                  return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__);
-                },
-                [self] (const MediaResult& aError) {
-                  self->OnFirstDemuxFailed(TrackInfo::kAudioTrack, aError);
-                  return SamplesPromise::CreateAndReject(aError, __func__);
-                });
+    p = p->Then(
+      OwnerThread(),
+      __func__,
+      [self](RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
+        DDLOGEX(
+          self.get(), DDLogCategory::Log, "audio_first_demuxed", DDNoValue{});
+        self->OnFirstDemuxCompleted(TrackInfo::kAudioTrack, aSamples);
+        return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__);
+      },
+      [self](const MediaResult& aError) {
+        DDLOGEX(
+          self.get(), DDLogCategory::Log, "audio_first_demuxing_error", aError);
+        self->OnFirstDemuxFailed(TrackInfo::kAudioTrack, aError);
+        return SamplesPromise::CreateAndReject(aError, __func__);
+      });
   }
 
   p->Then(OwnerThread(), __func__, this,
           &MediaFormatReader::OnAudioDemuxCompleted,
           &MediaFormatReader::OnAudioDemuxFailed)
    ->Track(mAudio.mDemuxRequest);
 }
 
@@ -1879,35 +2015,118 @@ void
 MediaFormatReader::OnAudioDemuxCompleted(
   RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples)
 {
   LOGV("%zu audio samples demuxed (sid:%d)",
        aSamples->mSamples.Length(),
        aSamples->mSamples[0]->mTrackInfo
        ? aSamples->mSamples[0]->mTrackInfo->GetID()
        : 0);
+  DDLOG(DDLogCategory::Log,
+        "audio_demuxed_samples",
+        uint64_t(aSamples->mSamples.Length()));
   mAudio.mDemuxRequest.Complete();
   mAudio.mQueuedSamples.AppendElements(aSamples->mSamples);
   ScheduleUpdate(TrackInfo::kAudioTrack);
 }
 
 void
 MediaFormatReader::NotifyNewOutput(
   TrackType aTrack, const MediaDataDecoder::DecodedData& aResults)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
-  for (auto& sample : aResults) {
-    LOGV("Received new %s sample time:%" PRId64 " duration:%" PRId64,
-         TrackTypeToStr(aTrack), sample->mTime.ToMicroseconds(),
-         sample->mDuration.ToMicroseconds());
-    decoder.mOutput.AppendElement(sample);
-    decoder.mNumSamplesOutput++;
-    decoder.mNumOfConsecutiveError = 0;
-  }
+  if (aResults.IsEmpty()) {
+    DDLOG(DDLogCategory::Log,
+          aTrack == TrackInfo::kAudioTrack ? "decoded_audio" : "decoded_video",
+          "no output samples");
+  } else
+    for (auto& sample : aResults) {
+      if (DecoderDoctorLogger::IsDDLoggingEnabled()) {
+        switch (sample->mType) {
+          case MediaData::AUDIO_DATA:
+            DDLOGPR(DDLogCategory::Log,
+                    aTrack == TrackInfo::kAudioTrack ? "decoded_audio"
+                                                     : "decoded_got_audio!?",
+                    "{\"type\":\"AudioData\", \"offset\":%" PRIi64
+                    ", \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64
+                    ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32
+                    ", \"kf\":%s, \"channels\":%" PRIu32 ", \"rate\":%" PRIu32
+                    ", \"bytes\":%zu}",
+                    sample->mOffset,
+                    sample->mTime.ToMicroseconds(),
+                    sample->mTimecode.ToMicroseconds(),
+                    sample->mDuration.ToMicroseconds(),
+                    sample->mFrames,
+                    sample->mKeyframe ? "true" : "false",
+                    sample->As<AudioData>()->mChannels,
+                    sample->As<AudioData>()->mRate,
+                    sample->As<AudioData>()->mAudioData.Size());
+            break;
+          case MediaData::VIDEO_DATA:
+            DDLOGPR(DDLogCategory::Log,
+                    aTrack == TrackInfo::kVideoTrack ? "decoded_video"
+                                                     : "decoded_got_video!?",
+                    "{\"type\":\"VideoData\", \"offset\":%" PRIi64
+                    ", \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64
+                    ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32
+                    ", \"kf\":%s, \"size\":[%" PRIi32 ",%" PRIi32 "]}",
+                    sample->mOffset,
+                    sample->mTime.ToMicroseconds(),
+                    sample->mTimecode.ToMicroseconds(),
+                    sample->mDuration.ToMicroseconds(),
+                    sample->mFrames,
+                    sample->mKeyframe ? "true" : "false",
+                    sample->As<VideoData>()->mDisplay.width,
+                    sample->As<VideoData>()->mDisplay.height);
+            break;
+          case MediaData::RAW_DATA:
+            DDLOGPR(DDLogCategory::Log,
+                    aTrack == TrackInfo::kAudioTrack
+                      ? "decoded_audio"
+                      : aTrack == TrackInfo::kVideoTrack ? "decoded_video"
+                                                         : "decoded_?",
+                    "{\"type\":\"RawData\", \"offset\":%" PRIi64
+                    " \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64
+                    ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32
+                    ", \"kf\":%s}",
+                    sample->mOffset,
+                    sample->mTime.ToMicroseconds(),
+                    sample->mTimecode.ToMicroseconds(),
+                    sample->mDuration.ToMicroseconds(),
+                    sample->mFrames,
+                    sample->mKeyframe ? "true" : "false");
+            break;
+          case MediaData::NULL_DATA:
+            DDLOGPR(DDLogCategory::Log,
+                    aTrack == TrackInfo::kAudioTrack
+                      ? "decoded_audio"
+                      : aTrack == TrackInfo::kVideoTrack ? "decoded_video"
+                                                         : "decoded_?",
+                    "{\"type\":\"NullData\", \"offset\":%" PRIi64
+                    " \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64
+                    ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32
+                    ", \"kf\":%s}",
+                    sample->mOffset,
+                    sample->mTime.ToMicroseconds(),
+                    sample->mTimecode.ToMicroseconds(),
+                    sample->mDuration.ToMicroseconds(),
+                    sample->mFrames,
+                    sample->mKeyframe ? "true" : "false");
+            break;
+        }
+      }
+      LOGV("Received new %s sample time:%" PRId64 " duration:%" PRId64,
+           TrackTypeToStr(aTrack),
+           sample->mTime.ToMicroseconds(),
+           sample->mDuration.ToMicroseconds());
+      decoder.mOutput.AppendElement(sample);
+      decoder.mNumSamplesOutput++;
+      decoder.mNumOfConsecutiveError = 0;
+    }
   LOG("Done processing new %s samples", TrackTypeToStr(aTrack));
 
   if (!aResults.IsEmpty()) {
     // We have decoded our first frame, we can now starts to skip future errors.
     decoder.mFirstFrameTime.reset();
   }
   ScheduleUpdate(aTrack);
 }
@@ -2099,17 +2318,17 @@ MediaFormatReader::RequestDemuxSamples(T
   if (!decoder.mQueuedSamples.IsEmpty()) {
     // No need to demux new samples.
     return;
   }
 
   if (decoder.mDemuxEOS) {
     // Nothing left to demux.
     // We do not want to attempt to demux while in waiting for data mode
-    // as it would retrigger an unecessary drain.
+    // as it would retrigger an unnecessary drain.
     return;
   }
 
   LOGV("Requesting extra demux %s", TrackTypeToStr(aTrack));
   if (aTrack == TrackInfo::kVideoTrack) {
     DoDemuxVideo();
   } else {
     DoDemuxAudio();
@@ -2119,16 +2338,31 @@ MediaFormatReader::RequestDemuxSamples(T
 void
 MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
                                         MediaRawData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
   RefPtr<MediaFormatReader> self = this;
   decoder.mFlushed = false;
+  DDLOGPR(DDLogCategory::Log,
+          aTrack == TrackInfo::kAudioTrack
+            ? "decode_audio"
+            : aTrack == TrackInfo::kVideoTrack ? "decode_video" : "decode_?",
+          "{\"type\":\"MediaRawData\", \"offset\":%" PRIi64
+          ", \"bytes\":%zu, \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64
+          ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 "%s%s}",
+          aSample->mOffset,
+          aSample->Size(),
+          aSample->mTime.ToMicroseconds(),
+          aSample->mTimecode.ToMicroseconds(),
+          aSample->mDuration.ToMicroseconds(),
+          aSample->mFrames,
+          aSample->mKeyframe ? " kf" : "",
+          aSample->mEOS ? " eos" : "");
   decoder.mDecoder->Decode(aSample)
     ->Then(mTaskQueue, __func__,
            [self, aTrack, &decoder]
            (const MediaDataDecoder::DecodedData& aResults) {
              decoder.mDecodeRequest.Complete();
              self->NotifyNewOutput(aTrack, aResults);
 
              // When we recovered from a GPU crash and get the first decoded
@@ -2250,49 +2484,60 @@ MediaFormatReader::InternalSeek(TrackTyp
   MOZ_ASSERT(OnTaskQueue());
   LOG("%s internal seek to %f",
       TrackTypeToStr(aTrack), aTarget.Time().ToSeconds());
 
   auto& decoder = GetDecoderData(aTrack);
   decoder.Flush();
   decoder.ResetDemuxer();
   decoder.mTimeThreshold = Some(aTarget);
+  DDLOG(DDLogCategory::Log, "seeking", DDNoValue{});
   RefPtr<MediaFormatReader> self = this;
   decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().Time())
-    ->Then(OwnerThread(), __func__,
-           [self, aTrack] (TimeUnit aTime) {
-             auto& decoder = self->GetDecoderData(aTrack);
-             decoder.mSeekRequest.Complete();
-             MOZ_ASSERT(
-               decoder.mTimeThreshold,
-               "Seek promise must be disconnected when timethreshold is reset");
-             decoder.mTimeThreshold.ref().mHasSeeked = true;
-             self->SetVideoDecodeThreshold();
-             self->ScheduleUpdate(aTrack);
-           },
-           [self, aTrack] (const MediaResult& aError) {
-             auto& decoder = self->GetDecoderData(aTrack);
-             decoder.mSeekRequest.Complete();
-             switch (aError.Code()) {
-               case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
-                 self->NotifyWaitingForData(aTrack);
-                 break;
-               case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
-                 decoder.mTimeThreshold.reset();
-                 self->NotifyEndOfStream(aTrack);
-                 break;
-               case NS_ERROR_DOM_MEDIA_CANCELED:
-                 decoder.mTimeThreshold.reset();
-                 break;
-               default:
-                 decoder.mTimeThreshold.reset();
-                 self->NotifyError(aTrack, aError);
-                 break;
-             }
-           })
+    ->Then(
+      OwnerThread(),
+      __func__,
+      [self, aTrack](TimeUnit aTime) {
+        DDLOGEX(self.get(), DDLogCategory::Log, "seeked", DDNoValue{});
+        auto& decoder = self->GetDecoderData(aTrack);
+        decoder.mSeekRequest.Complete();
+        MOZ_ASSERT(
+          decoder.mTimeThreshold,
+          "Seek promise must be disconnected when timethreshold is reset");
+        decoder.mTimeThreshold.ref().mHasSeeked = true;
+        self->SetVideoDecodeThreshold();
+        self->ScheduleUpdate(aTrack);
+      },
+      [self, aTrack](const MediaResult& aError) {
+        auto& decoder = self->GetDecoderData(aTrack);
+        decoder.mSeekRequest.Complete();
+        switch (aError.Code()) {
+          case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
+            DDLOGEX(
+              self.get(), DDLogCategory::Log, "seeking_interrupted", aError);
+            self->NotifyWaitingForData(aTrack);
+            break;
+          case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
+            DDLOGEX(
+              self.get(), DDLogCategory::Log, "seeking_interrupted", aError);
+            decoder.mTimeThreshold.reset();
+            self->NotifyEndOfStream(aTrack);
+            break;
+          case NS_ERROR_DOM_MEDIA_CANCELED:
+            DDLOGEX(
+              self.get(), DDLogCategory::Log, "seeking_interrupted", aError);
+            decoder.mTimeThreshold.reset();
+            break;
+          default:
+            DDLOGEX(self.get(), DDLogCategory::Log, "seeking_error", aError);
+            decoder.mTimeThreshold.reset();
+            self->NotifyError(aTrack, aError);
+            break;
+        }
+      })
     ->Track(decoder.mSeekRequest);
 }
 
 void
 MediaFormatReader::DrainDecoder(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
 
@@ -2307,33 +2552,36 @@ MediaFormatReader::DrainDecoder(TrackTyp
     LOGV("Draining %s with nothing to drain", TrackTypeToStr(aTrack));
     decoder.mDrainState = DrainState::DrainAborted;
     ScheduleUpdate(aTrack);
     return;
   }
 
   decoder.mDrainState = DrainState::Draining;
 
+  DDLOG(DDLogCategory::Log, "draining", DDNoValue{});
   RefPtr<MediaFormatReader> self = this;
   decoder.mDecoder->Drain()
     ->Then(mTaskQueue, __func__,
            [self, aTrack, &decoder]
            (const MediaDataDecoder::DecodedData& aResults) {
              decoder.mDrainRequest.Complete();
+             DDLOGEX(self.get(), DDLogCategory::Log, "drained", DDNoValue{});
              if (aResults.IsEmpty()) {
                decoder.mDrainState = DrainState::DrainCompleted;
              } else {
                self->NotifyNewOutput(aTrack, aResults);
                // Let's see if we have any more data available to drain.
                decoder.mDrainState = DrainState::PartialDrainPending;
              }
              self->ScheduleUpdate(aTrack);
            },
            [self, aTrack, &decoder](const MediaResult& aError) {
              decoder.mDrainRequest.Complete();
+             DDLOGEX(self.get(), DDLogCategory::Log, "draining_error", aError);
              self->NotifyError(aTrack, aError);
            })
     ->Track(decoder.mDrainRequest);
   LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack));
 }
 
 void
 MediaFormatReader::Update(TrackType aTrack)
@@ -2518,16 +2766,17 @@ MediaFormatReader::Update(TrackType aTra
 
   if (decoder.mError && !decoder.HasFatalError()) {
     MOZ_RELEASE_ASSERT(!decoder.HasInternalSeekPending(),
                        "No error can occur while an internal seek is pending");
     bool needsNewDecoder =
       decoder.mError.ref() == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
     if (!needsNewDecoder &&
         ++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) {
+      DDLOG(DDLogCategory::Log, "too_many_decode_errors", decoder.mError.ref());
       NotifyError(aTrack, decoder.mError.ref());
       return;
     }
     decoder.mError.reset();
 
     LOG("%s decoded error count %d", TrackTypeToStr(aTrack),
         decoder.mNumOfConsecutiveError);
 
@@ -2547,16 +2796,17 @@ MediaFormatReader::Update(TrackType aTra
         NS_SUCCEEDED(
           decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe)) &&
         !nextKeyframe.IsInfinite()) {
       SkipVideoDemuxToNextKeyFrame(
         decoder.mLastDecodedSampleTime.refOr(TimeInterval()).Length());
     } else if (aTrack == TrackType::kAudioTrack) {
       decoder.Flush();
     } else {
+      DDLOG(DDLogCategory::Log, "no_keyframe", NS_ERROR_DOM_MEDIA_FATAL_ERR);
       // We can't recover from this error.
       NotifyError(aTrack, NS_ERROR_DOM_MEDIA_FATAL_ERR);
     }
     return;
   }
 
   bool needInput = NeedInput(decoder);
 
@@ -2815,45 +3065,52 @@ MediaFormatReader::VideoSkipReset(uint32
 
 void
 MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Skipping succeeded, skipped %u frames", aSkipped);
   mSkipRequest.Complete();
 
+  DDLOG(DDLogCategory::Log, "video_skipped", DDNoValue());
+
   VideoSkipReset(aSkipped);
 
   ScheduleUpdate(TrackInfo::kVideoTrack);
 }
 
 void
 MediaFormatReader::OnVideoSkipFailed(
   MediaTrackDemuxer::SkipFailureHolder aFailure)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Skipping failed, skipped %u frames", aFailure.mSkipped);
   mSkipRequest.Complete();
 
   switch (aFailure.mFailure.Code()) {
     case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
     case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
+      DDLOG(
+        DDLogCategory::Log, "video_skipping_interruption", aFailure.mFailure);
       // Some frames may have been output by the decoder since we initiated the
       // videoskip process and we know they would be late.
       DropDecodedSamples(TrackInfo::kVideoTrack);
       // We can't complete the skip operation, will just service a video frame
       // normally.
       ScheduleUpdate(TrackInfo::kVideoTrack);
       break;
     case NS_ERROR_DOM_MEDIA_CANCELED:
+      DDLOG(
+        DDLogCategory::Log, "video_skipping_interruption", aFailure.mFailure);
       if (mVideo.HasPromise()) {
         mVideo.RejectPromise(aFailure.mFailure, __func__);
       }
       break;
     default:
+      DDLOG(DDLogCategory::Log, "video_skipping_error", aFailure.mFailure);
       NotifyError(TrackType::kVideoTrack, aFailure.mFailure);
       break;
   }
 }
 
 RefPtr<MediaFormatReader::SeekPromise>
 MediaFormatReader::Seek(const SeekTarget& aTarget)
 {
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -82,17 +82,20 @@ struct MOZ_STACK_CLASS MediaFormatReader
   VideoFrameContainer* mVideoFrameContainer = nullptr;
   FrameStatistics* mFrameStats = nullptr;
   already_AddRefed<layers::KnowsCompositor> mKnowsCompositor;
   already_AddRefed<GMPCrashHelper> mCrashHelper;
   // Used in bug 1393399 for temporary telemetry.
   MediaDecoderOwnerID mMediaDecoderOwnerID = nullptr;
 };
 
+DDLoggedTypeDeclName(MediaFormatReader);
+
 class MediaFormatReader final
+  : public DecoderDoctorLifeLogger<MediaFormatReader>
 {
   static const bool IsExclusive = true;
   typedef TrackInfo::TrackType TrackType;
   typedef MozPromise<bool, MediaResult, IsExclusive> NotifyDataArrivedPromise;
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaFormatReader)
 
 public:
   using TrackSet = EnumSet<TrackInfo::TrackType>;
@@ -378,16 +381,24 @@ private:
       , mNumSamplesOutput(0)
       , mNumSamplesOutputTotal(0)
       , mNumSamplesSkippedTotal(0)
       , mSizeOfQueue(0)
       , mIsHardwareAccelerated(false)
       , mLastStreamSourceID(UINT32_MAX)
       , mIsNullDecode(false)
     {
+      DecoderDoctorLogger::LogConstruction("MediaFormatReader::DecoderData",
+                                           this);
+    }
+
+    ~DecoderData()
+    {
+      DecoderDoctorLogger::LogDestruction("MediaFormatReader::DecoderData",
+                                          this);
     }
 
     MediaFormatReader* mOwner;
     // Disambiguate Audio vs Video.
     MediaData::Type mType;
     RefPtr<MediaTrackDemuxer> mTrackDemuxer;
     // TaskQueue on which decoder can choose to decode.
     // Only non-null up until the decoder is created.
@@ -612,16 +623,27 @@ private:
   {
   public:
     DecoderDataWithPromise(MediaFormatReader* aOwner,
                            MediaData::Type aType,
                            uint32_t aNumOfMaxError)
       : DecoderData(aOwner, aType, aNumOfMaxError)
       , mHasPromise(false)
     {
+      DecoderDoctorLogger::LogConstructionAndBase(
+        "MediaFormatReader::DecoderDataWithPromise",
+        this,
+        "MediaFormatReader::DecoderData",
+        static_cast<const MediaFormatReader::DecoderData*>(this));
+    }
+
+    ~DecoderDataWithPromise()
+    {
+      DecoderDoctorLogger::LogDestruction(
+        "MediaFormatReader::DecoderDataWithPromise", this);
     }
 
     bool HasPromise() const override
     {
       return mHasPromise;
     }
 
     RefPtr<DataPromise<Type>> EnsurePromise(const char* aMethodName)
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -52,16 +52,17 @@ MediaResourceIndex::MediaResourceIndex(M
   , mOffset(0)
   , mCacheBlockSize(aResource->ShouldCacheReads()
                       ? kMediaResourceIndexCacheSize
                       : 0)
   , mCachedOffset(0)
   , mCachedBytes(0)
   , mCachedBlock(MakeUnique<char[]>(mCacheBlockSize))
 {
+  DDLINKCHILD("resource", aResource);
 }
 
 nsresult
 MediaResourceIndex::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   // We purposefuly don't check that we may attempt to read past
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -1,32 +1,35 @@
 /* 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/. */
 
 #if !defined(MediaResource_h_)
 #define MediaResource_h_
 
+#include "DecoderDoctorLogger.h"
 #include "Intervals.h"
 #include "MediaData.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/UniquePtr.h"
 #include "nsISeekableStream.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 // Represents a section of contiguous media, with a start and end offset.
 // Used to denote ranges of data which are cached.
 
 typedef media::Interval<int64_t> MediaByteRange;
 typedef media::IntervalSet<int64_t> MediaByteRangeSet;
 
+DDLoggedTypeDeclName(MediaResource);
+
 /**
  * Provides a thread-safe, seek/read interface to resources
  * loaded from a URI. Uses MediaCache to cache data received over
  * Necko's async channel API, thus resolving the mismatch between clients
  * that need efficient random access to the data and protocols that do not
  * support efficient random access, such as HTTP.
  *
  * Instances of this class must be created on the main thread.
@@ -40,17 +43,17 @@ typedef media::IntervalSet<int64_t> Medi
  *
  * The generic implementation of this class is ChannelMediaResource, which can
  * handle any URI for which Necko supports AsyncOpen.
  * The 'file:' protocol can be implemented efficiently with direct random
  * access, so the FileMediaResource implementation class bypasses the cache.
  * For cross-process blob URL, CloneableWithRangeMediaResource is used.
  * MediaResource::Create automatically chooses the best implementation class.
  */
-class MediaResource
+class MediaResource : public DecoderDoctorLifeLogger<MediaResource>
 {
 public:
   // Our refcounting is threadsafe, and when our refcount drops to zero
   // we dispatch an event to the main thread to delete the MediaResource.
   // Note that this means it's safe for references to this object to be
   // released on a non main thread, but the destructor will always run on
   // the main thread.
   NS_METHOD_(MozExternalRefCountType) AddRef(void);
@@ -142,24 +145,26 @@ class MOZ_RAII AutoPinned {
   operator T*() const { return mResource; }
   T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mResource; }
 
 private:
   T* mResource;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+DDLoggedTypeDeclName(MediaResourceIndex);
+
 /*
  * MediaResourceIndex provides a way to access MediaResource objects.
  * Read, Seek and Tell must only be called on non-main threads.
  * In the case of the Ogg Decoder they are called on the Decode thread for
  * example. You must ensure that no threads are calling these methods once
  * the MediaResource has been Closed.
  */
-class MediaResourceIndex
+class MediaResourceIndex : public DecoderDoctorLifeLogger<MediaResourceIndex>
 {
 public:
   explicit MediaResourceIndex(MediaResource* aResource);
 
   // Read up to aCount bytes from the stream. The buffer must have
   // enough room for at least aCount bytes. Stores the number of
   // actual bytes read in aBytes (0 on end of file).
   // May read less than aCount bytes if the number of
--- a/dom/media/MediaResourceCallback.h
+++ b/dom/media/MediaResourceCallback.h
@@ -2,36 +2,41 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 MediaResourceCallback_h_
 #define MediaResourceCallback_h_
 
+#include "DecoderDoctorLogger.h"
 #include "nsError.h"
 #include "nsISupportsImpl.h"
 #include "MediaResult.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaDecoderOwner;
 class MediaResource;
 
+DDLoggedTypeDeclName(MediaResourceCallback);
+
 /**
  * A callback used by MediaResource (sub-classes like FileMediaResource,
  * RtspMediaResource, and ChannelMediaResource) to notify various events.
  * Currently this is implemented by MediaDecoder only.
  *
  * Since this class has no pure virtual function, it is convenient to write
  * gtests for the readers without using a mock MediaResource when you don't
  * care about the events notified by the MediaResource.
  */
-class MediaResourceCallback {
+class MediaResourceCallback
+  : public DecoderDoctorLifeLogger<MediaResourceCallback>
+{
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaResourceCallback);
 
   // Return an abstract thread on which to run main thread runnables.
   virtual AbstractThread* AbstractMainThread() const { return nullptr; }
 
   // Returns a weak reference to the media decoder owner.
   virtual MediaDecoderOwner* GetMediaOwner() const { return nullptr; }
--- a/dom/media/eme/MediaKeySession.h
+++ b/dom/media/eme/MediaKeySession.h
@@ -19,29 +19,37 @@
 #include "mozilla/DetailedPromise.h"
 #include "mozilla/dom/MediaKeySessionBinding.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozilla/dom/MediaKeyMessageEventBinding.h"
 
 struct JSContext;
 
 namespace mozilla {
+
+namespace dom {
+class MediaKeySession;
+} // namespace dom
+DDLoggedTypeName(dom::MediaKeySession);
+
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class MediaKeyError;
 class MediaKeyStatusMap;
 
 nsCString
 ToCString(MediaKeySessionType aType);
 
 nsString
 ToString(MediaKeySessionType aType);
 
-class MediaKeySession final : public DOMEventTargetHelper
+class MediaKeySession final
+  : public DOMEventTargetHelper
+  , public DecoderDoctorLifeLogger<MediaKeySession>
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaKeySession,
                                            DOMEventTargetHelper)
 public:
   MediaKeySession(JSContext* aCx,
                   nsPIDOMWindowInner* aParent,
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -493,16 +493,17 @@ MediaKeys::CreateSession(JSContext* aCx,
                                                         this,
                                                         mKeySystem,
                                                         aSessionType,
                                                         aRv);
 
   if (aRv.Failed()) {
     return nullptr;
   }
+  DDLINKCHILD("session", session.get());
 
   // Add session to the set of sessions awaiting their sessionId being ready.
   mPendingSessions.Put(session->Token(), session);
 
   return session.forget();
 }
 
 void
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_dom_mediakeys_h__
 #define mozilla_dom_mediakeys_h__
 
+#include "DecoderDoctorLogger.h"
 #include "nsWrapperCache.h"
 #include "nsISupports.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
@@ -22,33 +23,40 @@
 #include "mozilla/DetailedPromise.h"
 #include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 class CDMProxy;
 
 namespace dom {
+class MediaKeys;
+} // namespace dom
+DDLoggedTypeName(dom::MediaKeys);
+
+namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class MediaKeySession;
 struct MediaKeysPolicy;
 class HTMLMediaElement;
 
 typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, dom::DetailedPromise> PromiseHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
 typedef nsDataHashtable<nsUint32HashKey, uint32_t> PendingPromiseIdTokenHashMap;
 typedef uint32_t PromiseId;
 
 // This class is used on the main thread only.
 // Note: its addref/release is not (and can't be) thread safe!
-class MediaKeys final : public nsISupports,
-                        public nsWrapperCache,
-                        public SupportsWeakPtr<MediaKeys>
+class MediaKeys final
+  : public nsISupports
+  , public nsWrapperCache
+  , public SupportsWeakPtr<MediaKeys>
+  , public DecoderDoctorLifeLogger<MediaKeys>
 {
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MediaKeys)
 
--- a/dom/media/flac/FlacDemuxer.cpp
+++ b/dom/media/flac/FlacDemuxer.cpp
@@ -554,23 +554,28 @@ private:
   Frame mNextFrame;
   Frame mFrame;
 };
 
 } // namespace flac
 
 // FlacDemuxer
 
-FlacDemuxer::FlacDemuxer(MediaResource* aSource) : mSource(aSource) { }
+FlacDemuxer::FlacDemuxer(MediaResource* aSource)
+  : mSource(aSource)
+{
+  DDLINKCHILD("source", aSource);
+}
 
 bool
 FlacDemuxer::InitInternal()
 {
   if (!mTrackDemuxer) {
     mTrackDemuxer = new FlacTrackDemuxer(mSource);
+    DDLINKCHILD("track demuxer", mTrackDemuxer.get());
   }
   return mTrackDemuxer->Init();
 }
 
 RefPtr<FlacDemuxer::InitPromise>
 FlacDemuxer::Init()
 {
   if (!InitInternal()) {
@@ -607,16 +612,17 @@ FlacDemuxer::IsSeekable() const
 }
 
 // FlacTrackDemuxer
 FlacTrackDemuxer::FlacTrackDemuxer(MediaResource* aSource)
   : mSource(aSource)
   , mParser(new flac::FrameParser())
   , mTotalFrameLen(0)
 {
+  DDLINKCHILD("source", aSource);
   Reset();
 }
 
 FlacTrackDemuxer::~FlacTrackDemuxer()
 {
 }
 
 bool
--- a/dom/media/flac/FlacDemuxer.h
+++ b/dom/media/flac/FlacDemuxer.h
@@ -13,18 +13,22 @@
 namespace mozilla {
 
 namespace flac {
 class Frame;
 class FrameParser;
 }
 class FlacTrackDemuxer;
 
+DDLoggedTypeDeclNameAndBase(FlacDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(FlacTrackDemuxer, MediaTrackDemuxer);
 
-class FlacDemuxer : public MediaDataDemuxer
+class FlacDemuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<FlacDemuxer>
 {
 public:
   // MediaDataDemuxer interface.
   explicit FlacDemuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
     TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
@@ -35,17 +39,19 @@ public:
 
 private:
   bool InitInternal();
 
   RefPtr<MediaResource> mSource;
   RefPtr<FlacTrackDemuxer> mTrackDemuxer;
 };
 
-class FlacTrackDemuxer : public MediaTrackDemuxer
+class FlacTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<FlacTrackDemuxer>
 {
 public:
   explicit FlacTrackDemuxer(MediaResource* aSource);
 
   // Initializes the track demuxer by reading the first frame for meta data.
   // Returns initialization success state.
   bool Init();
 
--- a/dom/media/gtest/MockMediaResource.h
+++ b/dom/media/gtest/MockMediaResource.h
@@ -4,20 +4,23 @@
 
 #ifndef MOCK_MEDIA_RESOURCE_H_
 #define MOCK_MEDIA_RESOURCE_H_
 
 #include "MediaResource.h"
 #include "nsTArray.h"
 #include "mozilla/Atomics.h"
 
-namespace mozilla
-{
+namespace mozilla {
+
+DDLoggedTypeDeclNameAndBase(MockMediaResource, MediaResource);
 
-class MockMediaResource : public MediaResource
+class MockMediaResource
+  : public MediaResource
+  , public DecoderDoctorLifeLogger<MockMediaResource>
 {
 public:
   explicit MockMediaResource(const char* aFileName);
   nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount,
                   uint32_t* aBytes) override;
   // Data stored in file, caching recommended.
   bool ShouldCacheReads() override { return true; }
   void Pin() override {}
--- a/dom/media/gtest/TestMP3Demuxer.cpp
+++ b/dom/media/gtest/TestMP3Demuxer.cpp
@@ -5,33 +5,45 @@
 
 #include <gtest/gtest.h>
 #include <vector>
 
 #include "MP3Demuxer.h"
 #include "mozilla/ArrayUtils.h"
 #include "MockMediaResource.h"
 
+class MockMP3MediaResource;
+class MockMP3StreamMediaResource;
+namespace mozilla {
+DDLoggedTypeNameAndBase(::MockMP3MediaResource, MockMediaResource);
+DDLoggedTypeNameAndBase(::MockMP3StreamMediaResource, MockMP3MediaResource);
+} // namespace mozilla
+
 using namespace mozilla;
 using media::TimeUnit;
 
-
 // Regular MP3 file mock resource.
-class MockMP3MediaResource : public MockMediaResource {
+class MockMP3MediaResource
+  : public MockMediaResource
+  , public DecoderDoctorLifeLogger<MockMP3MediaResource>
+{
 public:
   explicit MockMP3MediaResource(const char* aFileName)
     : MockMediaResource(aFileName)
   {}
 
 protected:
   virtual ~MockMP3MediaResource() {}
 };
 
 // MP3 stream mock resource.
-class MockMP3StreamMediaResource : public MockMP3MediaResource {
+class MockMP3StreamMediaResource
+  : public MockMP3MediaResource
+  , public DecoderDoctorLifeLogger<MockMP3StreamMediaResource>
+{
 public:
   explicit MockMP3StreamMediaResource(const char* aFileName)
     : MockMP3MediaResource(aFileName)
   {}
 
   int64_t GetLength() override { return -1; }
 
 protected:
--- a/dom/media/gtest/mp4_demuxer/TestParser.cpp
+++ b/dom/media/gtest/mp4_demuxer/TestParser.cpp
@@ -7,21 +7,28 @@
 #include "MediaData.h"
 #include "MediaPrefs.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Preferences.h"
 #include "BufferStream.h"
 #include "MP4Metadata.h"
 #include "MoofParser.h"
 
+class TestStream;
+namespace mozilla {
+DDLoggedTypeNameAndBase(::TestStream, ByteStream);
+} // namespace mozilla
+
 using namespace mozilla;
 
 static const uint32_t E = MP4Metadata::NumberTracksError();
 
-class TestStream : public ByteStream
+class TestStream
+  : public ByteStream
+  , public DecoderDoctorLifeLogger<TestStream>
 {
 public:
   TestStream(const uint8_t* aBuffer, size_t aSize)
     : mHighestSuccessfulEndOffset(0)
     , mBuffer(aBuffer)
     , mSize(aSize)
   {
   }
--- a/dom/media/hls/HLSDemuxer.h
+++ b/dom/media/hls/HLSDemuxer.h
@@ -20,17 +20,22 @@
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaResult;
 class HLSTrackDemuxer;
 
-class HLSDemuxer final : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(HLSDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(HLSTrackDemuxer, MediaTrackDemuxer);
+
+class HLSDemuxer final
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<HLSDemuxer>
 {
   class HLSDemuxerCallbacksSupport;
 public:
   explicit HLSDemuxer(int aPlayerId);
 
   RefPtr<InitPromise> Init() override;
 
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
@@ -63,17 +68,19 @@ private:
 
   MozPromiseHolder<InitPromise> mInitPromise;
   RefPtr<HLSDemuxerCallbacksSupport> mCallbackSupport;
 
   java::GeckoHLSDemuxerWrapper::Callbacks::GlobalRef mJavaCallbacks;
   java::GeckoHLSDemuxerWrapper::GlobalRef mHLSDemuxerWrapper;
 };
 
-class HLSTrackDemuxer : public MediaTrackDemuxer
+class HLSTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<HLSTrackDemuxer>
 {
 public:
   HLSTrackDemuxer(HLSDemuxer* aParent,
                   TrackInfo::TrackType aType,
                   UniquePtr<TrackInfo> aTrackInfo);
   ~HLSTrackDemuxer();
   UniquePtr<TrackInfo> GetInfo() const override;
 
--- a/dom/media/ipc/RemoteVideoDecoder.h
+++ b/dom/media/ipc/RemoteVideoDecoder.h
@@ -7,26 +7,36 @@
 #define include_dom_ipc_RemoteVideoDecoder_h
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/DebugOnly.h"
 #include "MediaData.h"
 #include "PlatformDecoderModule.h"
 
 namespace mozilla {
+
+namespace dom {
+class RemoteVideoDecoder;
+}
+DDLoggedTypeCustomNameAndBase(dom::RemoteVideoDecoder,
+                              RemoteVideoDecoder,
+                              MediaDataDecoder);
+
 namespace dom {
 
 class VideoDecoderChild;
 class RemoteDecoderModule;
 
 // A MediaDataDecoder implementation that proxies through IPDL
 // to a 'real' decoder in the GPU process.
 // All requests get forwarded to a VideoDecoderChild instance that
 // operates solely on the VideoDecoderManagerChild thread.
-class RemoteVideoDecoder : public MediaDataDecoder
+class RemoteVideoDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<RemoteVideoDecoder>
 {
 public:
   friend class RemoteDecoderModule;
 
   // MediaDataDecoder
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -114,17 +114,21 @@ ContainerParser::MediaHeaderRange()
 }
 
 MediaByteRange
 ContainerParser::MediaSegmentRange()
 {
   return mCompleteMediaSegmentRange;
 }
 
-class WebMContainerParser : public ContainerParser
+DDLoggedTypeDeclNameAndBase(WebMContainerParser, ContainerParser);
+
+class WebMContainerParser
+  : public ContainerParser
+  , public DecoderDoctorLifeLogger<WebMContainerParser>
 {
 public:
   explicit WebMContainerParser(const MediaContainerType& aType)
     : ContainerParser(aType)
     , mParser(0)
     , mOffset(0)
   {
   }
@@ -189,16 +193,17 @@ public:
     }
 
     if (initSegment) {
       mOffset = 0;
       mParser = WebMBufferedParser(0);
       mOverlappedMapping.Clear();
       mInitData = new MediaByteBuffer();
       mResource = new SourceBufferResource();
+      DDLINKCHILD("resource", mResource.get());
       mCompleteInitSegmentRange = MediaByteRange();
       mCompleteMediaHeaderRange = MediaByteRange();
       mCompleteMediaSegmentRange = MediaByteRange();
       mGlobalOffset = mTotalParsed;
     }
 
     // XXX if it only adds new mappings, overlapped but not available
     // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
@@ -336,17 +341,21 @@ private:
   WebMBufferedParser mParser;
   nsTArray<WebMTimeDataOffset> mOverlappedMapping;
   int64_t mOffset;
   Maybe<WebMTimeDataOffset> mLastMapping;
 };
 
 #ifdef MOZ_FMP4
 
-class MP4Stream : public ByteStream
+DDLoggedTypeDeclNameAndBase(MP4Stream, ByteStream);
+
+class MP4Stream
+  : public ByteStream
+  , public DecoderDoctorLifeLogger<MP4Stream>
 {
 public:
   explicit MP4Stream(SourceBufferResource* aResource);
   virtual ~MP4Stream();
   bool ReadAt(int64_t aOffset,
               void* aBuffer,
               size_t aCount,
               size_t* aBytesRead) override;
@@ -360,16 +369,17 @@ private:
   RefPtr<SourceBufferResource> mResource;
 };
 
 MP4Stream::MP4Stream(SourceBufferResource* aResource)
   : mResource(aResource)
 {
   MOZ_COUNT_CTOR(MP4Stream);
   MOZ_ASSERT(aResource);
+  DDLINKCHILD("resource", aResource);
 }
 
 MP4Stream::~MP4Stream()
 {
   MOZ_COUNT_DTOR(MP4Stream);
 }
 
 bool
@@ -401,17 +411,21 @@ bool
 MP4Stream::Length(int64_t* aSize)
 {
   if (mResource->GetLength() < 0)
     return false;
   *aSize = mResource->GetLength();
   return true;
 }
 
-class MP4ContainerParser : public ContainerParser
+DDLoggedTypeDeclNameAndBase(MP4ContainerParser, ContainerParser);
+
+class MP4ContainerParser
+  : public ContainerParser
+  , public DecoderDoctorLifeLogger<MP4ContainerParser>
 {
 public:
   explicit MP4ContainerParser(const MediaContainerType& aType)
     : ContainerParser(aType)
   {
   }
 
   MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override
@@ -570,22 +584,24 @@ private:
 public:
   MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                          int64_t& aStart,
                                          int64_t& aEnd) override
   {
     bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
     if (initSegment) {
       mResource = new SourceBufferResource();
+      DDLINKCHILD("resource", mResource.get());
       mStream = new MP4Stream(mResource);
       // We use a timestampOffset of 0 for ContainerParser, and require
       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
       // manually. This allows the ContainerParser to be shared across different
       // timestampOffsets.
       mParser = new MoofParser(mStream, 0, /* aIsAudio = */ false);
+      DDLINKCHILD("parser", mParser.get());
       mInitData = new MediaByteBuffer();
       mCompleteInitSegmentRange = MediaByteRange();
       mCompleteMediaHeaderRange = MediaByteRange();
       mCompleteMediaSegmentRange = MediaByteRange();
       mGlobalOffset = mTotalParsed;
     } else if (!mStream || !mParser) {
       mTotalParsed += aData->Length();
       return NS_ERROR_NOT_AVAILABLE;
@@ -652,17 +668,21 @@ public:
 
 private:
   RefPtr<MP4Stream> mStream;
   nsAutoPtr<MoofParser> mParser;
 };
 #endif // MOZ_FMP4
 
 #ifdef MOZ_FMP4
-class ADTSContainerParser : public ContainerParser
+DDLoggedTypeDeclNameAndBase(ADTSContainerParser, ContainerParser);
+
+class ADTSContainerParser
+  : public ContainerParser
+  , public DecoderDoctorLifeLogger<ADTSContainerParser>
 {
 public:
   explicit ADTSContainerParser(const MediaContainerType& aType)
     : ContainerParser(aType)
   {
   }
 
   typedef struct
--- a/dom/media/mediasource/ContainerParser.h
+++ b/dom/media/mediasource/ContainerParser.h
@@ -12,17 +12,19 @@
 #include "MediaResource.h"
 #include "MediaResult.h"
 
 namespace mozilla {
 
 class MediaByteBuffer;
 class SourceBufferResource;
 
-class ContainerParser
+DDLoggedTypeDeclName(ContainerParser);
+
+class ContainerParser : public DecoderDoctorLifeLogger<ContainerParser>
 {
 public:
   explicit ContainerParser(const MediaContainerType& aType);
   virtual ~ContainerParser();
 
   // Return true if aData starts with an initialization segment.
   // The base implementation exists only for debug logging and is expected
   // to be called first from the overriding implementation.
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -255,16 +255,17 @@ MediaSource::AddSourceBuffer(const nsASt
     return nullptr;
   }
   RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, *containerType);
   if (!sourceBuffer) {
     aRv.Throw(NS_ERROR_FAILURE); // XXX need a better error here
     return nullptr;
   }
   mSourceBuffers->Append(sourceBuffer);
+  DDLINKCHILD("sourcebuffer[]", sourceBuffer.get());
   MSE_DEBUG("sourceBuffer=%p", sourceBuffer.get());
   return sourceBuffer.forget();
 }
 
 RefPtr<MediaSource::ActiveCompletionPromise>
 MediaSource::SourceBufferIsActive(SourceBuffer* aSourceBuffer)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -330,16 +331,17 @@ MediaSource::RemoveSourceBuffer(SourceBu
   //     remove sourceBuffer video, audio, text Tracks from MediaElement tracks
   //     remove sourceBuffer video, audio, text Tracks and fire "removetrack" at affected lists
   //     fire "removetrack" at modified MediaElement track lists
   // If removed enabled/selected, fire "change" at affected MediaElement list.
   if (mActiveSourceBuffers->Contains(sourceBuffer)) {
     mActiveSourceBuffers->Remove(sourceBuffer);
   }
   mSourceBuffers->Remove(sourceBuffer);
+  DDUNLINKCHILD(sourceBuffer);
   // TODO: Free all resources associated with sourceBuffer
 }
 
 void
 MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("EndOfStream(aError=%d)",
--- a/dom/media/mediasource/MediaSource.h
+++ b/dom/media/mediasource/MediaSource.h
@@ -29,27 +29,34 @@ class nsPIDOMWindowInner;
 namespace mozilla {
 
 class AbstractThread;
 class ErrorResult;
 template <typename T> class AsyncEventRunner;
 class MediaResult;
 
 namespace dom {
+class MediaSource;
+} // namespace dom
+DDLoggedTypeName(dom::MediaSource);
+
+namespace dom {
 
 class GlobalObject;
 class SourceBuffer;
 class SourceBufferList;
 template <typename T> class Optional;
 
 #define MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID \
   { 0x3839d699, 0x22c5, 0x439f, \
   { 0x94, 0xca, 0x0e, 0x0b, 0x26, 0xf9, 0xca, 0xbf } }
 
-class MediaSource final : public DOMEventTargetHelper
+class MediaSource final
+  : public DOMEventTargetHelper
+  , public DecoderDoctorLifeLogger<MediaSource>
 {
 public:
   /** WebIDL Methods. */
   static already_AddRefed<MediaSource>
   Constructor(const GlobalObject& aGlobal,
               ErrorResult& aRv);
 
   SourceBufferList* SourceBuffers();
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -179,22 +179,24 @@ MediaSourceDecoder::Shutdown()
   MediaDecoder::Shutdown();
 }
 
 void
 MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource)
 {
   MOZ_ASSERT(!mMediaSource && !GetStateMachine() && NS_IsMainThread());
   mMediaSource = aMediaSource;
+  DDLINKCHILD("mediasource", aMediaSource);
 }
 
 void
 MediaSourceDecoder::DetachMediaSource()
 {
   MOZ_ASSERT(mMediaSource && NS_IsMainThread());
+  DDUNLINKCHILD(mMediaSource);
   mMediaSource = nullptr;
 }
 
 void
 MediaSourceDecoder::Ended(bool aEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
   AbstractThread::AutoEnter context(AbstractMainThread());
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -16,17 +16,21 @@ class MediaDecoderStateMachine;
 class MediaSourceDemuxer;
 
 namespace dom {
 
 class MediaSource;
 
 } // namespace dom
 
-class MediaSourceDecoder : public MediaDecoder
+DDLoggedTypeDeclNameAndBase(MediaSourceDecoder, MediaDecoder);
+
+class MediaSourceDecoder
+  : public MediaDecoder
+  , public DecoderDoctorLifeLogger<MediaSourceDecoder>
 {
 public:
   explicit MediaSourceDecoder(MediaDecoderInit& aInit);
 
   nsresult Load(nsIPrincipal* aPrincipal);
   media::TimeIntervals GetSeekable() override;
   media::TimeIntervals GetBuffered() override;
 
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -139,16 +139,17 @@ already_AddRefed<MediaTrackDemuxer>
 MediaSourceDemuxer::GetTrackDemuxer(TrackType aType, uint32_t aTrackNumber)
 {
   RefPtr<TrackBuffersManager> manager = GetManager(aType);
   if (!manager) {
     return nullptr;
   }
   RefPtr<MediaSourceTrackDemuxer> e =
     new MediaSourceTrackDemuxer(this, aType, manager);
+  DDLINKCHILD("track demuxer", e.get());
   mDemuxers.AppendElement(e);
   return e.forget();
 }
 
 bool
 MediaSourceDemuxer::IsSeekable() const
 {
   return true;
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -18,17 +18,22 @@
 #include "TrackBuffersManager.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaResult;
 class MediaSourceTrackDemuxer;
 
-class MediaSourceDemuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(MediaSourceDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(MediaSourceTrackDemuxer, MediaTrackDemuxer);
+
+class MediaSourceDemuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<MediaSourceDemuxer>
 {
 public:
   explicit MediaSourceDemuxer(AbstractThread* aAbstractMainThread);
 
   RefPtr<InitPromise> Init() override;
 
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
@@ -82,17 +87,19 @@ private:
 
   // Monitor to protect members below across multiple threads.
   mutable Monitor mMonitor;
   RefPtr<TrackBuffersManager> mAudioTrack;
   RefPtr<TrackBuffersManager> mVideoTrack;
   MediaInfo mInfo;
 };
 
-class MediaSourceTrackDemuxer : public MediaTrackDemuxer
+class MediaSourceTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<MediaSourceTrackDemuxer>
 {
 public:
   MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
                           TrackInfo::TrackType aType,
                           TrackBuffersManager* aManager);
 
   UniquePtr<TrackInfo> GetInfo() const override;
 
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -132,16 +132,17 @@ SourceBuffer::GetTimeIntervals()
   return mTrackBuffersManager->Buffered();
 }
 
 void
 SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetAppendWindowStart(aAppendWindowStart=%f)", aAppendWindowStart);
+  DDLOG(DDLogCategory::API, "SetAppendWindowStart", aAppendWindowStart);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (aAppendWindowStart < 0 ||
       aAppendWindowStart >= mCurrentAttributes.GetAppendWindowEnd()) {
     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     return;
@@ -149,16 +150,17 @@ SourceBuffer::SetAppendWindowStart(doubl
   mCurrentAttributes.SetAppendWindowStart(aAppendWindowStart);
 }
 
 void
 SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetAppendWindowEnd(aAppendWindowEnd=%f)", aAppendWindowEnd);
+  DDLOG(DDLogCategory::API, "SetAppendWindowEnd", aAppendWindowEnd);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (IsNaN(aAppendWindowEnd) ||
       aAppendWindowEnd <= mCurrentAttributes.GetAppendWindowStart()) {
     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     return;
@@ -167,45 +169,51 @@ SourceBuffer::SetAppendWindowEnd(double 
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("AppendBuffer(ArrayBuffer)");
   aData.ComputeLengthAndData();
+  DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("AppendBuffer(ArrayBufferView)");
   aData.ComputeLengthAndData();
+  DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::Abort(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("Abort()");
   if (!IsAttached()) {
+    DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
+    DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mPendingRemoval.Exists()) {
+    DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
+  DDLOG(DDLogCategory::API, "Abort", NS_OK);
   AbortBufferAppend();
   ResetParserState();
   mCurrentAttributes.SetAppendWindowStart(0);
   mCurrentAttributes.SetAppendWindowEnd(PositiveInfinity<double>());
 }
 
 void
 SourceBuffer::AbortBufferAppend()
@@ -226,16 +234,18 @@ SourceBuffer::ResetParserState()
   mTrackBuffersManager->ResetParserState(mCurrentAttributes);
 }
 
 void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd);
+  DDLOG(DDLogCategory::API, "Remove-from", aStart);
+  DDLOG(DDLogCategory::API, "Remove-until", aEnd);
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
@@ -308,16 +318,17 @@ SourceBuffer::SourceBuffer(MediaSource* 
   , mActive(false)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
 
   mTrackBuffersManager =
     new TrackBuffersManager(aMediaSource->GetDecoder(), aType);
+  DDLINKCHILD("track buffers manager", mTrackBuffersManager.get());
 
   MSE_DEBUG("Create mTrackBuffersManager=%p",
             mTrackBuffersManager.get());
 
   ErrorResult dummy;
   if (mCurrentAttributes.mGenerateTimestamps) {
     SetMode(SourceBufferAppendMode::Sequence, dummy);
   } else {
@@ -424,16 +435,17 @@ SourceBuffer::AppendData(const uint8_t* 
     ->Track(mPendingAppend);
 }
 
 void
 SourceBuffer::AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult)
 {
   MOZ_ASSERT(mUpdating);
   mPendingAppend.Complete();
+  DDLOG(DDLogCategory::API, "AppendBuffer-completed", NS_OK);
 
   if (aResult.first()) {
     if (!mActive) {
       mActive = true;
       MSE_DEBUG("Init segment received");
       RefPtr<SourceBuffer> self = this;
       mMediaSource->SourceBufferIsActive(this)
         ->Then(mAbstractMainThread, __func__,
@@ -460,16 +472,17 @@ SourceBuffer::AppendDataCompletedWithSuc
   }
 }
 
 void
 SourceBuffer::AppendDataErrored(const MediaResult& aError)
 {
   MOZ_ASSERT(mUpdating);
   mPendingAppend.Complete();
+  DDLOG(DDLogCategory::API, "AppendBuffer-error", aError);
 
   switch (aError.Code()) {
     case NS_ERROR_DOM_MEDIA_CANCELED:
       // Nothing further to do as the trackbuffer has been shutdown.
       // or append was aborted and abort() has handled all the events.
       break;
     default:
       AppendError(aError);
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -32,21 +32,25 @@ struct JSContext;
 
 namespace mozilla {
 
 class AbstractThread;
 class ErrorResult;
 class MediaByteBuffer;
 template <typename T> class AsyncEventRunner;
 
+DDLoggedTypeName(dom::SourceBuffer);
+
 namespace dom {
 
 class TimeRanges;
 
-class SourceBuffer final : public DOMEventTargetHelper
+class SourceBuffer final
+  : public DOMEventTargetHelper
+  , public DecoderDoctorLifeLogger<SourceBuffer>
 {
 public:
   /** WebIDL Methods. */
   SourceBufferAppendMode Mode() const
   {
     return mCurrentAttributes.GetAppendMode();
   }
 
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -21,18 +21,22 @@ class MediaByteBuffer;
 class TaskQueue;
 
 namespace dom {
 
 class SourceBuffer;
 
 } // namespace dom
 
+DDLoggedTypeDeclNameAndBase(SourceBufferResource, MediaResource);
+
 // SourceBufferResource is not thread safe.
-class SourceBufferResource final : public MediaResource
+class SourceBufferResource final
+  : public MediaResource
+  , public DecoderDoctorLifeLogger<SourceBufferResource>
 {
 public:
   SourceBufferResource();
   nsresult Close() override;
   nsresult ReadAt(int64_t aOffset,
                   char* aBuffer,
                   uint32_t aCount,
                   uint32_t* aBytes) override;
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -109,16 +109,17 @@ TrackBuffersManager::TrackBuffersManager
   , mAudioEvictionThreshold(
       Preferences::GetUint("media.mediasource.eviction_threshold.audio",
                            20 * 1024 * 1024))
   , mEvictionState(EvictionState::NO_EVICTION_NEEDED)
   , mMutex("TrackBuffersManager")
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
+  DDLINKCHILD("parser", mParser.get());
 }
 
 TrackBuffersManager::~TrackBuffersManager()
 {
   ShutdownDemuxers();
 }
 
 RefPtr<TrackBuffersManager::AppendPromise>
@@ -874,23 +875,25 @@ TrackBuffersManager::ShutdownDemuxers()
 void
 TrackBuffersManager::CreateDemuxerforMIMEType()
 {
   ShutdownDemuxers();
 
   if (mType.Type() == MEDIAMIMETYPE("video/webm") ||
       mType.Type() == MEDIAMIMETYPE("audio/webm")) {
     mInputDemuxer = new WebMDemuxer(mCurrentInputBuffer, true /* IsMediaSource*/ );
+    DDLINKCHILD("demuxer", mInputDemuxer.get());
     return;
   }
 
 #ifdef MOZ_FMP4
   if (mType.Type() == MEDIAMIMETYPE("video/mp4") ||
       mType.Type() == MEDIAMIMETYPE("audio/mp4")) {
     mInputDemuxer = new MP4Demuxer(mCurrentInputBuffer);
+    DDLINKCHILD("demuxer", mInputDemuxer.get());
     return;
   }
 #endif
   NS_WARNING("Not supported (yet)");
 }
 
 // We reset the demuxer by creating a new one and initializing it.
 void
@@ -945,24 +948,26 @@ TrackBuffersManager::OnDemuxerResetDone(
 
   // Recreate track demuxers.
   uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
   if (numVideos) {
     // We currently only handle the first video track.
     mVideoTracks.mDemuxer =
       mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
     MOZ_ASSERT(mVideoTracks.mDemuxer);
+    DDLINKCHILD("video demuxer", mVideoTracks.mDemuxer.get());
   }
 
   uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
   if (numAudios) {
     // We currently only handle the first audio track.
     mAudioTracks.mDemuxer =
       mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
     MOZ_ASSERT(mAudioTracks.mDemuxer);
+    DDLINKCHILD("audio demuxer", mAudioTracks.mDemuxer.get());
   }
 
   if (mPendingInputBuffer) {
     // We had a partial media segment header stashed aside.
     // Reparse its content so we can continue parsing the current input buffer.
     int64_t start, end;
     mParser->ParseStartAndEndTimestamps(mPendingInputBuffer, start, end);
     mProcessedInput += mPendingInputBuffer->Length();
@@ -1038,26 +1043,28 @@ TrackBuffersManager::OnDemuxerInitDone(c
   MediaInfo info;
 
   uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
   if (numVideos) {
     // We currently only handle the first video track.
     mVideoTracks.mDemuxer =
       mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
     MOZ_ASSERT(mVideoTracks.mDemuxer);
+    DDLINKCHILD("video demuxer", mVideoTracks.mDemuxer.get());
     info.mVideo = *mVideoTracks.mDemuxer->GetInfo()->GetAsVideoInfo();
     info.mVideo.mTrackId = 2;
   }
 
   uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
   if (numAudios) {
     // We currently only handle the first audio track.
     mAudioTracks.mDemuxer =
       mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
     MOZ_ASSERT(mAudioTracks.mDemuxer);
+    DDLINKCHILD("audio demuxer", mAudioTracks.mDemuxer.get());
     info.mAudio = *mAudioTracks.mDemuxer->GetInfo()->GetAsAudioInfo();
     info.mAudio.mTrackId = 1;
   }
 
   int64_t videoDuration = numVideos ? info.mVideo.mDuration.ToMicroseconds() : 0;
   int64_t audioDuration = numAudios ? info.mAudio.mDuration.ToMicroseconds() : 0;
 
   int64_t duration = std::max(videoDuration, audioDuration);
@@ -2044,17 +2051,21 @@ TrackBuffersManager::RemoveFrames(const 
 void
 TrackBuffersManager::RecreateParser(bool aReuseInitData)
 {
   MOZ_ASSERT(OnTaskQueue());
   // Recreate our parser for only the data remaining. This is required
   // as it has parsed the entire InputBuffer provided.
   // Once the old TrackBuffer/MediaSource implementation is removed
   // we can optimize this part. TODO
+  if (mParser) {
+    DDUNLINKCHILD(mParser.get());
+  }
   mParser = ContainerParser::CreateForMIMEType(mType);
+  DDLINKCHILD("parser", mParser.get());
   if (aReuseInitData && mInitData) {
     int64_t start, end;
     mParser->ParseStartAndEndTimestamps(mInitData, start, end);
     mProcessedInput = mInitData->Length();
   } else {
     mProcessedInput = 0;
   }
 }
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -60,17 +60,19 @@ public:
   nsTArray<SourceBufferTask>::size_type Length() const
   {
     return mQueue.Length();
   }
 private:
   nsTArray<RefPtr<SourceBufferTask>> mQueue;
 };
 
-class TrackBuffersManager
+DDLoggedTypeDeclName(TrackBuffersManager);
+
+class TrackBuffersManager : public DecoderDoctorLifeLogger<TrackBuffersManager>
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffersManager);
 
   enum class EvictDataResult : int8_t
   {
     NO_DATA_EVICTED,
     CANT_EVICT,
--- a/dom/media/mp3/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3Demuxer.cpp
@@ -25,23 +25,28 @@ using mozilla::media::TimeUnit;
 using mozilla::media::TimeInterval;
 using mozilla::media::TimeIntervals;
 using mozilla::BufferReader;
 
 namespace mozilla {
 
 // MP3Demuxer
 
-MP3Demuxer::MP3Demuxer(MediaResource* aSource) : mSource(aSource) { }
+MP3Demuxer::MP3Demuxer(MediaResource* aSource)
+  : mSource(aSource)
+{
+  DDLINKCHILD("source", aSource);
+}
 
 bool
 MP3Demuxer::InitInternal()
 {
   if (!mTrackDemuxer) {
     mTrackDemuxer = new MP3TrackDemuxer(mSource);
+    DDLINKCHILD("track demuxer", mTrackDemuxer.get());
   }
   return mTrackDemuxer->Init();
 }
 
 RefPtr<MP3Demuxer::InitPromise>
 MP3Demuxer::Init()
 {
   if (!InitInternal()) {
@@ -102,16 +107,17 @@ MP3TrackDemuxer::MP3TrackDemuxer(MediaRe
   , mFirstFrameOffset(0)
   , mNumParsedFrames(0)
   , mFrameIndex(0)
   , mTotalFrameLen(0)
   , mSamplesPerFrame(0)
   , mSamplesPerSecond(0)
   , mChannels(0)
 {
+  DDLINKCHILD("source", aSource);
   Reset();
 }
 
 bool
 MP3TrackDemuxer::Init()
 {
   Reset();
   FastSeek(TimeUnit());
--- a/dom/media/mp3/MP3Demuxer.h
+++ b/dom/media/mp3/MP3Demuxer.h
@@ -8,17 +8,22 @@
 #include "MediaDataDemuxer.h"
 #include "MediaResource.h"
 #include "MP3FrameParser.h"
 
 namespace mozilla {
 
 class MP3TrackDemuxer;
 
-class MP3Demuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(MP3Demuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(MP3TrackDemuxer, MediaTrackDemuxer);
+
+class MP3Demuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<MP3Demuxer>
 {
 public:
   // MediaDataDemuxer interface.
   explicit MP3Demuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
       TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
@@ -31,17 +36,19 @@ private:
   bool InitInternal();
 
   RefPtr<MediaResource> mSource;
   RefPtr<MP3TrackDemuxer> mTrackDemuxer;
 };
 
 // The MP3 demuxer used to extract MPEG frames and side information out of
 // MPEG streams.
-class MP3TrackDemuxer : public MediaTrackDemuxer
+class MP3TrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<MP3TrackDemuxer>
 {
 public:
   // Constructor, expecting a valid media resource.
   explicit MP3TrackDemuxer(MediaResource* aSource);
 
   // Initializes the track demuxer by reading the first frame for meta data.
   // Returns initialization success state.
   bool Init();
--- a/dom/media/mp4/BufferStream.h
+++ b/dom/media/mp4/BufferStream.h
@@ -7,17 +7,21 @@
 
 #include "ByteStream.h"
 #include "nsTArray.h"
 #include "MediaResource.h"
 
 namespace mozilla {
 class MediaByteBuffer;
 
-class BufferStream : public ByteStream
+DDLoggedTypeDeclNameAndBase(BufferStream, ByteStream);
+
+class BufferStream
+  : public ByteStream
+  , public mozilla::DecoderDoctorLifeLogger<BufferStream>
 {
 public:
   /* BufferStream does not take ownership of aData nor does it make a copy.
    * Therefore BufferStream shouldn't get used after aData is destroyed.
    */
   BufferStream();
   explicit BufferStream(mozilla::MediaByteBuffer* aBuffer);
 
--- a/dom/media/mp4/ByteStream.h
+++ b/dom/media/mp4/ByteStream.h
@@ -1,21 +1,23 @@
 /* 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 STREAM_H_
 #define STREAM_H_
 
+#include "DecoderDoctorLogger.h"
 #include "nsISupportsImpl.h"
 
-namespace mozilla
-{
+namespace mozilla {
 
-class ByteStream
+DDLoggedTypeDeclName(ByteStream);
+
+class ByteStream : public DecoderDoctorLifeLogger<ByteStream>
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ByteStream);
 
   virtual bool ReadAt(int64_t offset, void* data, size_t size,
                       size_t* bytes_read) = 0;
   virtual bool CachedReadAt(int64_t offset, void* data, size_t size,
                             size_t* bytes_read) = 0;
--- a/dom/media/mp4/MP4Demuxer.cpp
+++ b/dom/media/mp4/MP4Demuxer.cpp
@@ -28,17 +28,21 @@ mozilla::LogModule* GetDemuxerLog()
 {
   return gMediaDemuxerLog;
 }
 
 #define LOG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("MP4Demuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
-class MP4TrackDemuxer : public MediaTrackDemuxer
+DDLoggedTypeDeclNameAndBase(MP4TrackDemuxer, MediaTrackDemuxer);
+
+class MP4TrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<MP4TrackDemuxer>
 {
 public:
   MP4TrackDemuxer(MP4Demuxer* aParent,
                   UniquePtr<TrackInfo>&& aInfo,
                   const IndiceWrapper& aIndices);
 
   UniquePtr<TrackInfo> GetInfo() const override;
 
@@ -115,16 +119,18 @@ AccumulateSPSTelemetry(const MediaByteBu
 
   return true;
 }
 
 MP4Demuxer::MP4Demuxer(MediaResource* aResource)
   : mResource(aResource)
   , mStream(new ResourceStream(aResource))
 {
+  DDLINKCHILD("resource", aResource);
+  DDLINKCHILD("stream", mStream.get());
 }
 
 RefPtr<MP4Demuxer::InitPromise>
 MP4Demuxer::Init()
 {
   AutoPinned<ResourceStream> stream(mStream);
 
   // 'result' will capture the first warning, if any.
@@ -142,16 +148,17 @@ MP4Demuxer::Init()
   } else if (NS_FAILED(initData.Result()) && result == NS_OK) {
     result = Move(initData.Result());
   }
 
   RefPtr<BufferStream> bufferstream =
     new BufferStream(initData.Ref());
 
   MP4Metadata metadata{bufferstream};
+  DDLINKCHILD("metadata", &metadata);
   nsresult rv = metadata.Parse();
   if (NS_FAILED(rv)) {
     return InitPromise::CreateAndReject(
       MediaResult(rv, RESULT_DETAIL("Parse MP4 metadata failed")), __func__);
   }
 
   auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
   if (audioTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
@@ -217,18 +224,20 @@ MP4Demuxer::Init()
       MP4Metadata::ResultAndIndice indices =
         metadata.GetTrackIndice(info.Ref()->mTrackId);
       if (!indices.Ref()) {
         if (NS_FAILED(info.Result()) && result == NS_OK) {
           result = Move(indices.Result());
         }
         continue;
       }
-      mAudioDemuxers.AppendElement(
-        new MP4TrackDemuxer(this, Move(info.Ref()), *indices.Ref().get()));
+      RefPtr<MP4TrackDemuxer> demuxer =
+        new MP4TrackDemuxer(this, Move(info.Ref()), *indices.Ref().get());
+      DDLINKCHILD("audio demuxer", demuxer.get());
+      mAudioDemuxers.AppendElement(Move(demuxer));
     }
   }
 
   if (videoTrackCount.Ref() != 0) {
     for (size_t i = 0; i < videoTrackCount.Ref(); i++) {
       MP4Metadata::ResultAndTrackInfo info =
         metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
       if (!info.Ref()) {
@@ -251,18 +260,20 @@ MP4Demuxer::Init()
       MP4Metadata::ResultAndIndice indices =
         metadata.GetTrackIndice(info.Ref()->mTrackId);
       if (!indices.Ref()) {
         if (NS_FAILED(info.Result()) && result == NS_OK) {
           result = Move(indices.Result());
         }
         continue;
       }
-      mVideoDemuxers.AppendElement(
-        new MP4TrackDemuxer(this, Move(info.Ref()), *indices.Ref().get()));
+      RefPtr<MP4TrackDemuxer> demuxer =
+        new MP4TrackDemuxer(this, Move(info.Ref()), *indices.Ref().get());
+      DDLINKCHILD("video demuxer", demuxer.get());
+      mVideoDemuxers.AppendElement(Move(demuxer));
     }
   }
 
   MP4Metadata::ResultAndCryptoFile cryptoFile =
     metadata.Crypto();
   if (NS_FAILED(cryptoFile.Result()) && result == NS_OK) {
     result = Move(cryptoFile.Result());
   }
--- a/dom/media/mp4/MP4Demuxer.h
+++ b/dom/media/mp4/MP4Demuxer.h
@@ -11,17 +11,21 @@
 #include "mozilla/Monitor.h"
 #include "MediaDataDemuxer.h"
 #include "MediaResource.h"
 
 namespace mozilla {
 class MP4TrackDemuxer;
 class ResourceStream;
 
-class MP4Demuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(MP4Demuxer, MediaDataDemuxer);
+
+class MP4Demuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<MP4Demuxer>
 {
 public:
   explicit MP4Demuxer(MediaResource* aResource);
 
   RefPtr<InitPromise> Init() override;
 
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
--- a/dom/media/mp4/MP4Metadata.cpp
+++ b/dom/media/mp4/MP4Metadata.cpp
@@ -98,16 +98,18 @@ read_source(uint8_t* buffer, uintptr_t s
   }
   return bytes_read;
 }
 
 MP4Metadata::MP4Metadata(ByteStream* aSource)
   : mSource(aSource)
   , mSourceAdaptor(aSource)
 {
+  DDLINKCHILD("source", aSource);
+
   Mp4parseIo io = { read_source, &mSourceAdaptor };
   mParser.reset(mp4parse_new(&io));
   MOZ_ASSERT(mParser);
 
   if (MOZ_LOG_TEST(gMP4MetadataLog, LogLevel::Debug)) {
     mp4parse_log(true);
   }
 }
--- a/dom/media/mp4/MP4Metadata.h
+++ b/dom/media/mp4/MP4Metadata.h
@@ -12,16 +12,18 @@
 #include "MediaData.h"
 #include "MediaInfo.h"
 #include "MediaResult.h"
 #include "ByteStream.h"
 #include "mp4parse.h"
 
 namespace mozilla {
 
+DDLoggedTypeDeclName(MP4Metadata);
+
 // The memory owner in mIndice.indices is rust mp4 parser, so lifetime of this
 // class SHOULD NOT longer than rust parser.
 class IndiceWrapper
 {
 public:
   size_t Length() const;
 
   bool GetIndice(size_t aIndex, Index::Indice& aIndice) const;
@@ -47,17 +49,17 @@ public:
 
   bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read);
 
 private:
   ByteStream* mSource;
   CheckedInt<size_t> mOffset;
 };
 
-class MP4Metadata
+class MP4Metadata : public DecoderDoctorLifeLogger<MP4Metadata>
 {
 public:
   explicit MP4Metadata(ByteStream* aSource);
   ~MP4Metadata();
 
   // Simple template class containing a MediaResult and another type.
   template <typename T>
   class ResultAndType
--- a/dom/media/mp4/MoofParser.cpp
+++ b/dom/media/mp4/MoofParser.cpp
@@ -106,20 +106,26 @@ MoofParser::FirstCompleteMediaSegment()
   for (uint32_t i = 0 ; i < mMediaRanges.Length(); i++) {
     if (mMediaRanges[i].Contains(Moofs()[i].mMdatRange)) {
       return mMediaRanges[i];
     }
   }
   return MediaByteRange();
 }
 
-class BlockingStream : public ByteStream {
+DDLoggedTypeDeclNameAndBase(BlockingStream, ByteStream);
+
+class BlockingStream
+  : public ByteStream
+  , public DecoderDoctorLifeLogger<BlockingStream>
+{
 public:
   explicit BlockingStream(ByteStream* aStream) : mStream(aStream)
   {
+    DDLINKCHILD("stream", aStream);
   }
 
   bool ReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read)
     override
   {
     return mStream->ReadAt(offset, data, size, bytes_read);
   }
 
--- a/dom/media/mp4/MoofParser.h
+++ b/dom/media/mp4/MoofParser.h
@@ -9,16 +9,17 @@
 #include "Atom.h"
 #include "AtomType.h"
 #include "SinfParser.h"
 #include "ByteStream.h"
 #include "MP4Interval.h"
 #include "MediaResource.h"
 
 namespace mozilla {
+
 typedef int64_t Microseconds;
 
 class Box;
 class BoxContext;
 class BoxReader;
 class Moof;
 
 class Mvhd : public Atom
@@ -278,28 +279,31 @@ private:
   // aDecodeTime is updated to the end of the parsed TRUN on return.
   Result<Ok, nsresult> ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio);
   void ParseSaiz(Box& aBox);
   void ParseSaio(Box& aBox);
   bool ProcessCenc();
   uint64_t mMaxRoundingError;
 };
 
-class MoofParser
+DDLoggedTypeDeclName(MoofParser);
+
+class MoofParser : public DecoderDoctorLifeLogger<MoofParser>
 {
 public:
   MoofParser(ByteStream* aSource, uint32_t aTrackId, bool aIsAudio)
     : mSource(aSource)
     , mOffset(0)
     , mTrex(aTrackId)
     , mIsAudio(aIsAudio)
     , mLastDecodeTime(0)
   {
     // Setting the mTrex.mTrackId to 0 is a nasty work around for calculating
     // the composition range for MSE. We need an array of tracks.
+    DDLINKCHILD("source", aSource);
   }
   bool RebuildFragmentedIndex(
     const mozilla::MediaByteRangeSet& aByteRanges);
   // If *aCanEvict is set to true. then will remove all moofs already parsed
   // from index then rebuild the index. *aCanEvict is set to true upon return if
   // some moofs were removed.
   bool RebuildFragmentedIndex(
     const mozilla::MediaByteRangeSet& aByteRanges, bool* aCanEvict);
--- a/dom/media/mp4/ResourceStream.cpp
+++ b/dom/media/mp4/ResourceStream.cpp
@@ -8,16 +8,17 @@
 
 namespace mozilla {
 
 ResourceStream::ResourceStream(mozilla::MediaResource* aResource)
   : mResource(aResource)
   , mPinCount(0)
 {
   MOZ_ASSERT(aResource);
+  DDLINKCHILD("resource", &mResource);
 }
 
 ResourceStream::~ResourceStream()
 {
   MOZ_ASSERT(mPinCount == 0);
 }
 
 bool
--- a/dom/media/mp4/ResourceStream.h
+++ b/dom/media/mp4/ResourceStream.h
@@ -7,17 +7,21 @@
 
 #include "MediaResource.h"
 #include "ByteStream.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla
 {
 
-class ResourceStream : public ByteStream
+DDLoggedTypeDeclNameAndBase(ResourceStream, ByteStream);
+
+class ResourceStream
+  : public ByteStream
+  , public DecoderDoctorLifeLogger<ResourceStream>
 {
 public:
   explicit ResourceStream(mozilla::MediaResource* aResource);
 
   virtual bool ReadAt(int64_t offset, void* aBuffer, size_t aCount,
                       size_t* aBytesRead) override;
   virtual bool CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
                             size_t* aBytesRead) override;
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -111,16 +111,18 @@ OggDemuxer::OggDemuxer(MediaResource* aR
   , mSkeletonState(nullptr)
   , mAudioOggState(aResource)
   , mVideoOggState(aResource)
   , mIsChained(false)
   , mTimedMetadataEvent(nullptr)
   , mOnSeekableEvent(nullptr)
 {
   MOZ_COUNT_CTOR(OggDemuxer);
+  // aResource is referenced through inner m{Audio,Video}OffState members.
+  DDLINKCHILD("resource", aResource);
 }
 
 OggDemuxer::~OggDemuxer()
 {
   MOZ_COUNT_DTOR(OggDemuxer);
   Reset(TrackInfo::kAudioTrack);
   Reset(TrackInfo::kVideoTrack);
   if (HasAudio() || HasVideo()) {
@@ -279,16 +281,17 @@ OggDemuxer::GetTrackInfo(TrackInfo::Trac
 
 already_AddRefed<MediaTrackDemuxer>
 OggDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
 {
   if (GetNumberTracks(aType) <= aTrackNumber) {
     return nullptr;
   }
   RefPtr<OggTrackDemuxer> e = new OggTrackDemuxer(this, aType, aTrackNumber);
+  DDLINKCHILD("track demuxer", e.get());
   mDemuxers.AppendElement(e);
 
   return e.forget();
 }
 
 nsresult
 OggDemuxer::Reset(TrackInfo::TrackType aType)
 {
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -11,17 +11,22 @@
 #include "OggCodecState.h"
 #include "OggCodecStore.h"
 #include "MediaMetadataManager.h"
 
 namespace mozilla {
 
 class OggTrackDemuxer;
 
-class OggDemuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(OggDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(OggTrackDemuxer, MediaTrackDemuxer);
+
+class OggDemuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<OggDemuxer>
 {
 public:
   explicit OggDemuxer(MediaResource* aResource);
 
   RefPtr<InitPromise> Init() override;
 
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
 
@@ -324,17 +329,19 @@ private:
   // It is updated once a chained ogg is encountered.
   // As Ogg chaining is only supported for audio, we only need an audio track
   // info.
   RefPtr<TrackInfoSharedPtr> mSharedAudioTrackInfo;
 
   friend class OggTrackDemuxer;
 };
 
-class OggTrackDemuxer : public MediaTrackDemuxer
+class OggTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<OggTrackDemuxer>
 {
 public:
   OggTrackDemuxer(OggDemuxer* aParent,
                   TrackInfo::TrackType aType,
                   uint32_t aTrackNumber);
 
   UniquePtr<TrackInfo> GetInfo() const override;
 
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #if !defined(PlatformDecoderModule_h_)
 #define PlatformDecoderModule_h_
 
+#include "DecoderDoctorLogger.h"
 #include "GMPCrashHelper.h"
 #include "MediaEventSource.h"
 #include "MediaInfo.h"
 #include "MediaResult.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TaskQueue.h"
@@ -221,16 +222,18 @@ protected:
   // On Windows the task queue's threads in have MSCOM initialized with
   // COINIT_MULTITHREADED.
   // It is safe to store a reference to aConfig.
   // This is called on the decode task queue.
   virtual already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) = 0;
 };
 
+DDLoggedTypeDeclName(MediaDataDecoder);
+
 // MediaDataDecoder is the interface exposed by decoders created by the
 // PlatformDecoderModule's Create*Decoder() functions. The type of
 // media data that the decoder accepts as valid input and produces as
 // output is determined when the MediaDataDecoder is created.
 //
 // Unless otherwise noted, all functions are only called on the decode task
 // queue.  An exception is the MediaDataDecoder in
 // MediaFormatReader::IsVideoAccelerated() for which all calls (Init(),
@@ -238,17 +241,17 @@ protected:
 //
 // Don't block inside these functions, unless it's explicitly noted that you
 // should (like in Flush()).
 //
 // Decoding is done asynchronously. Any async work can be done on the
 // TaskQueue passed into the PlatformDecoderModules's Create*Decoder()
 // function. This may not be necessary for platforms with async APIs
 // for decoding.
-class MediaDataDecoder
+class MediaDataDecoder : public DecoderDoctorLifeLogger<MediaDataDecoder>
 {
 protected:
   virtual ~MediaDataDecoder() { }
 
 public:
   typedef TrackInfo::TrackType TrackType;
   typedef nsTArray<RefPtr<MediaData>> DecodedData;
   typedef MozPromise<TrackType, MediaResult, /* IsExclusive = */ true>
--- a/dom/media/platforms/agnostic/AOMDecoder.h
+++ b/dom/media/platforms/agnostic/AOMDecoder.h
@@ -9,17 +9,21 @@
 #include "PlatformDecoderModule.h"
 #include "mozilla/Span.h"
 
 #include <stdint.h>
 #include "aom/aom_decoder.h"
 
 namespace mozilla {
 
-class AOMDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(AOMDecoder, MediaDataDecoder);
+
+class AOMDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<AOMDecoder>
 {
 public:
   explicit AOMDecoder(const CreateDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
--- a/dom/media/platforms/agnostic/DummyMediaDataDecoder.h
+++ b/dom/media/platforms/agnostic/DummyMediaDataDecoder.h
@@ -18,19 +18,23 @@ class MediaRawData;
 
 class DummyDataCreator
 {
 public:
   virtual ~DummyDataCreator();
   virtual already_AddRefed<MediaData> Create(MediaRawData* aSample) = 0;
 };
 
+DDLoggedTypeDeclNameAndBase(DummyMediaDataDecoder, MediaDataDecoder);
+
 // Decoder that uses a passed in object's Create function to create Null
 // MediaData objects.
-class DummyMediaDataDecoder : public MediaDataDecoder
+class DummyMediaDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<DummyMediaDataDecoder>
 {
 public:
   DummyMediaDataDecoder(UniquePtr<DummyDataCreator>&& aCreator,
                         const nsACString& aDescription,
                         const CreateDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
 
--- a/dom/media/platforms/agnostic/OpusDecoder.h
+++ b/dom/media/platforms/agnostic/OpusDecoder.h
@@ -12,17 +12,21 @@
 #include "nsAutoPtr.h"
 
 struct OpusMSDecoder;
 
 namespace mozilla {
 
 class OpusParser;
 
-class OpusDataDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(OpusDataDecoder, MediaDataDecoder);
+
+class OpusDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<OpusDataDecoder>
 {
 public:
   explicit OpusDataDecoder(const CreateDecoderParams& aParams);
   ~OpusDataDecoder();
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
--- a/dom/media/platforms/agnostic/TheoraDecoder.h
+++ b/dom/media/platforms/agnostic/TheoraDecoder.h
@@ -8,17 +8,21 @@
 
 #include "PlatformDecoderModule.h"
 #include "ogg/ogg.h"
 #include "theora/theoradec.h"
 #include <stdint.h>
 
 namespace mozilla {
 
-class TheoraDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(TheoraDecoder, MediaDataDecoder);
+
+class TheoraDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<TheoraDecoder>
 {
 public:
   explicit TheoraDecoder(const CreateDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
--- a/dom/media/platforms/agnostic/VPXDecoder.h
+++ b/dom/media/platforms/agnostic/VPXDecoder.h
@@ -12,17 +12,21 @@
 #include <stdint.h>
 #define VPX_DONT_DEFINE_STDINT_TYPES
 #include "vpx/vp8dx.h"
 #include "vpx/vpx_codec.h"
 #include "vpx/vpx_decoder.h"
 
 namespace mozilla {
 
-class VPXDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(VPXDecoder, MediaDataDecoder);
+
+class VPXDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<VPXDecoder>
 {
 public:
   explicit VPXDecoder(const CreateDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
--- a/dom/media/platforms/agnostic/VorbisDecoder.h
+++ b/dom/media/platforms/agnostic/VorbisDecoder.h
@@ -13,17 +13,21 @@
 #ifdef MOZ_TREMOR
 #include "tremor/ivorbiscodec.h"
 #else
 #include "vorbis/codec.h"
 #endif
 
 namespace mozilla {
 
-class VorbisDataDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(VorbisDataDecoder, MediaDataDecoder);
+
+class VorbisDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<VorbisDataDecoder>
 {
 public:
   explicit VorbisDataDecoder(const CreateDecoderParams& aParams);
   ~VorbisDataDecoder();
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
--- a/dom/media/platforms/agnostic/WAVDecoder.h
+++ b/dom/media/platforms/agnostic/WAVDecoder.h
@@ -6,17 +6,21 @@
 
 #if !defined(WaveDecoder_h_)
 #define WaveDecoder_h_
 
 #include "PlatformDecoderModule.h"
 
 namespace mozilla {
 
-class WaveDataDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(WaveDataDecoder, MediaDataDecoder);
+
+class WaveDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<WaveDataDecoder>
 {
 public:
   explicit WaveDataDecoder(const CreateDecoderParams& aParams);
 
   // Return true if mimetype is Wave
   static bool IsWave(const nsACString& aMimeType);
 
   RefPtr<InitPromise> Init() override;
--- a/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
+++ b/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
@@ -10,17 +10,21 @@
 #include "PlatformDecoderModule.h"
 #include "ChromiumCDMParent.h"
 
 namespace mozilla {
 
 class CDMProxy;
 struct GMPVideoDecoderParams;
 
-class ChromiumCDMVideoDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(ChromiumCDMVideoDecoder, MediaDataDecoder);
+
+class ChromiumCDMVideoDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<ChromiumCDMVideoDecoder>
 {
 public:
   ChromiumCDMVideoDecoder(const GMPVideoDecoderParams& aParams,
                           CDMProxy* aCDMProxy);
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<FlushPromise> Flush() override;
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -20,30 +20,35 @@
 #include "DecryptThroughputLimit.h"
 #include "ChromiumCDMVideoDecoder.h"
 
 namespace mozilla {
 
 typedef MozPromiseRequestHolder<DecryptPromise> DecryptPromiseRequestHolder;
 extern already_AddRefed<PlatformDecoderModule> CreateBlankDecoderModule();
 
-class EMEDecryptor : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(EMEDecryptor, MediaDataDecoder);
+
+class EMEDecryptor
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<EMEDecryptor>
 {
 public:
   EMEDecryptor(MediaDataDecoder* aDecoder, CDMProxy* aProxy,
                TaskQueue* aDecodeTaskQueue, TrackInfo::TrackType aType,
                MediaEventProducer<TrackInfo::TrackType>* aOnWaitingForKey)
     : mDecoder(aDecoder)
     , mTaskQueue(aDecodeTaskQueue)
     , mProxy(aProxy)
     , mSamplesWaitingForKey(
         new SamplesWaitingForKey(mProxy, aType, aOnWaitingForKey))
     , mThroughputLimiter(aDecodeTaskQueue)
     , mIsShutdown(false)
   {
+    DDLINKCHILD("decoder", mDecoder.get());
   }
 
   RefPtr<InitPromise> Init() override
   {
     MOZ_ASSERT(!mIsShutdown);
     return mDecoder->Init();
   }
 
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
@@ -37,17 +37,21 @@ protected:
 
 private:
   virtual ~EMEDecoderModule();
   RefPtr<CDMProxy> mProxy;
   // Will be null if CDM has decoding capability.
   RefPtr<PDMFactory> mPDM;
 };
 
-class EMEMediaDataDecoderProxy : public MediaDataDecoderProxy
+DDLoggedTypeDeclNameAndBase(EMEMediaDataDecoderProxy, MediaDataDecoderProxy);
+
+class EMEMediaDataDecoderProxy
+  : public MediaDataDecoderProxy
+  , public DecoderDoctorLifeLogger<EMEMediaDataDecoderProxy>
 {
 public:
   EMEMediaDataDecoderProxy(
     already_AddRefed<AbstractThread> aProxyThread, CDMProxy* aProxy,
     const CreateDecoderParams& aParams);
   EMEMediaDataDecoderProxy(const CreateDecoderParams& aParams,
                            already_AddRefed<MediaDataDecoder> aProxyDecoder,
                            CDMProxy* aProxy);
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -22,18 +22,22 @@ struct GMPVideoDecoderParams
 
   const VideoInfo& mConfig;
   TaskQueue* mTaskQueue;
   layers::ImageContainer* mImageContainer;
   layers::LayersBackend mLayersBackend;
   RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
-class GMPVideoDecoder : public MediaDataDecoder,
-                        public GMPVideoDecoderCallbackProxy
+DDLoggedTypeDeclNameAndBase(GMPVideoDecoder, MediaDataDecoder);
+
+class GMPVideoDecoder
+  : public MediaDataDecoder
+  , public GMPVideoDecoderCallbackProxy
+  , public DecoderDoctorLifeLogger<GMPVideoDecoder>
 {
 public:
   explicit GMPVideoDecoder(const GMPVideoDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -9,17 +9,21 @@
 #include "FennecJNIWrappers.h"
 #include "SurfaceTexture.h"
 #include "TimeUnits.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 
-class RemoteDataDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(RemoteDataDecoder, MediaDataDecoder);
+
+class RemoteDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<RemoteDataDecoder>
 {
 public:
   static already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams,
                      const nsString& aDrmStubId,
                      CDMProxy* aProxy);
 
   static already_AddRefed<MediaDataDecoder>
--- a/dom/media/platforms/apple/AppleATDecoder.h
+++ b/dom/media/platforms/apple/AppleATDecoder.h
@@ -12,17 +12,21 @@
 #include "mozilla/Vector.h"
 #include "nsIThread.h"
 #include "AudioConverter.h"
 
 namespace mozilla {
 
 class TaskQueue;
 
-class AppleATDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(AppleATDecoder, MediaDataDecoder);
+
+class AppleATDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<AppleATDecoder>
 {
 public:
   AppleATDecoder(const AudioInfo& aConfig,
                  TaskQueue* aTaskQueue);
   ~AppleATDecoder();
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
--- a/dom/media/platforms/apple/AppleVTDecoder.h
+++ b/dom/media/platforms/apple/AppleVTDecoder.h
@@ -12,17 +12,21 @@
 #include "nsIThread.h"
 #include "ReorderQueue.h"
 #include "TimeUnits.h"
 
 #include "VideoToolbox/VideoToolbox.h"
 
 namespace mozilla {
 
-class AppleVTDecoder : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(AppleVTDecoder, MediaDataDecoder);
+
+class AppleVTDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<AppleVTDecoder>
 {
 public:
   AppleVTDecoder(const VideoInfo& aConfig,
                  TaskQueue* aTaskQueue,
                  layers::ImageContainer* aImageContainer);
 
   class AppleFrameRef {
   public:
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -11,18 +11,25 @@
 #include "FFmpegLibWrapper.h"
 
 namespace mozilla {
 
 template <int V> class FFmpegAudioDecoder
 {
 };
 
-template <>
-class FFmpegAudioDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
+template<>
+class FFmpegAudioDecoder<LIBAV_VER>;
+DDLoggedTypeNameAndBase(FFmpegAudioDecoder<LIBAV_VER>,
+                        FFmpegDataDecoder<LIBAV_VER>);
+
+template<>
+class FFmpegAudioDecoder<LIBAV_VER>
+  : public FFmpegDataDecoder<LIBAV_VER>
+  , public DecoderDoctorLifeLogger<FFmpegAudioDecoder<LIBAV_VER>>
 {
 public:
   FFmpegAudioDecoder(FFmpegLibWrapper* aLib, TaskQueue* aTaskQueue,
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   RefPtr<InitPromise> Init() override;
   void InitCodecContext() override;
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -14,18 +14,24 @@
 
 namespace mozilla {
 
 template <int V>
 class FFmpegDataDecoder : public MediaDataDecoder
 {
 };
 
-template <>
-class FFmpegDataDecoder<LIBAV_VER> : public MediaDataDecoder
+template<>
+class FFmpegDataDecoder<LIBAV_VER>;
+DDLoggedTypeNameAndBase(FFmpegDataDecoder<LIBAV_VER>, MediaDataDecoder);
+
+template<>
+class FFmpegDataDecoder<LIBAV_VER>
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<FFmpegDataDecoder<LIBAV_VER>>
 {
 public:
   FFmpegDataDecoder(FFmpegLibWrapper* aLib, TaskQueue* aTaskQueue,
                     AVCodecID aCodecID);
   virtual ~FFmpegDataDecoder();
 
   static bool Link();
 
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
@@ -14,18 +14,25 @@
 namespace mozilla
 {
 
 template <int V>
 class FFmpegVideoDecoder : public FFmpegDataDecoder<V>
 {
 };
 
-template <>
-class FFmpegVideoDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
+template<>
+class FFmpegVideoDecoder<LIBAV_VER>;
+DDLoggedTypeNameAndBase(FFmpegVideoDecoder<LIBAV_VER>,
+                        FFmpegDataDecoder<LIBAV_VER>);
+
+template<>
+class FFmpegVideoDecoder<LIBAV_VER>
+  : public FFmpegDataDecoder<LIBAV_VER>
+  , public DecoderDoctorLifeLogger<FFmpegVideoDecoder<LIBAV_VER>>
 {
   typedef mozilla::layers::Image Image;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::KnowsCompositor KnowsCompositor;
   typedef SimpleMap<int64_t> DurationMap;
 
 public:
   FFmpegVideoDecoder(FFmpegLibWrapper* aLib, TaskQueue* aTaskQueue,
--- a/dom/media/platforms/omx/OmxDataDecoder.h
+++ b/dom/media/platforms/omx/OmxDataDecoder.h
@@ -25,16 +25,18 @@ class MediaDataHelper;
 
 typedef OmxPromiseLayer::OmxCommandPromise OmxCommandPromise;
 typedef OmxPromiseLayer::OmxBufferPromise OmxBufferPromise;
 typedef OmxPromiseLayer::OmxBufferFailureHolder OmxBufferFailureHolder;
 typedef OmxPromiseLayer::OmxCommandFailureHolder OmxCommandFailureHolder;
 typedef OmxPromiseLayer::BufferData BufferData;
 typedef OmxPromiseLayer::BUFFERLIST BUFFERLIST;
 
+DDLoggedTypeDeclNameAndBase(OmxDataDecoder, MediaDataDecoder);
+
 /* OmxDataDecoder is the major class which performs followings:
  *   1. Translate PDM function into OMX commands.
  *   2. Keeping the buffers between client and component.
  *   3. Manage the OMX state.
  *
  * From the definition in OpenMax spec. "2.2.1", there are 3 major roles in
  * OpenMax IL.
  *
@@ -51,17 +53,19 @@ typedef OmxPromiseLayer::BUFFERLIST BUFF
  *   OmxPromiseLayer acts as the OpenMAX IL component.
  *
  * OpenMAX IL core:
  *   "Platform-specific code that has the functionality necessary to locate and
  *   then load an OpenMAX IL component into main memory."
  *
  *   OmxPlatformLayer acts as the OpenMAX IL core.
  */
-class OmxDataDecoder : public MediaDataDecoder
+class OmxDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<OmxDataDecoder>
 {
 protected:
   virtual ~OmxDataDecoder();
 
 public:
   OmxDataDecoder(const TrackInfo& aTrackInfo,
                  layers::ImageContainer* aImageContainer);
 
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.h
@@ -74,22 +74,26 @@ public:
 
 protected:
   // IMFTransform wrapper that performs the decoding.
   RefPtr<MFTDecoder> mDecoder;
 
   Maybe<media::TimeUnit> mSeekTargetThreshold;
 };
 
+DDLoggedTypeDeclNameAndBase(WMFMediaDataDecoder, MediaDataDecoder);
+
 // Decodes audio and video using Windows Media Foundation. Samples are decoded
 // using the MFTDecoder created by the MFTManager. This class implements
 // the higher-level logic that drives mapping the MFT to the async
 // MediaDataDecoder interface. The specifics of decoding the exact stream
 // type are handled by MFTManager and the MFTDecoder it creates.
-class WMFMediaDataDecoder : public MediaDataDecoder
+class WMFMediaDataDecoder
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<WMFMediaDataDecoder>
 {
 public:
   WMFMediaDataDecoder(MFTManager* aOutputSource, TaskQueue* aTaskQueue);
   ~WMFMediaDataDecoder();
 
   RefPtr<MediaDataDecoder::InitPromise> Init() override;
 
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -289,16 +289,18 @@ H264Converter::CreateDecoder(const Video
       // The decoder supports CreateDecoderParam::mError, returns the value.
       return error;
     } else {
       return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                          RESULT_DETAIL("Unable to create H264 decoder"));
     }
   }
 
+  DDLINKCHILD("decoder", mDecoder.get());
+
   mNeedKeyframe = true;
 
   return NS_OK;
 }
 
 MediaResult
 H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
 {
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -9,24 +9,28 @@
 
 #include "PlatformDecoderModule.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.
 // The H264Converter allows playback of content where the SPS NAL may not be
 // provided in the init segment (e.g. AVC3 or Annex B)
 // H264Converter will monitor the input data, and will delay creation of the
 // MediaDataDecoder until a SPS and PPS NALs have been extracted.
 
-class H264Converter : public MediaDataDecoder
+class H264Converter
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<H264Converter>
 {
 public:
 
   H264Converter(PlatformDecoderModule* aPDM,
                 const CreateDecoderParams& aParams);
   virtual ~H264Converter();
 
   RefPtr<InitPromise> Init() override;
--- a/dom/media/platforms/wrappers/MediaDataDecoderProxy.h
+++ b/dom/media/platforms/wrappers/MediaDataDecoderProxy.h
@@ -10,17 +10,21 @@
 #include "PlatformDecoderModule.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 #include "nscore.h"
 
 namespace mozilla {
 
-class MediaDataDecoderProxy : public MediaDataDecoder
+DDLoggedTypeDeclNameAndBase(MediaDataDecoderProxy, MediaDataDecoder);
+
+class MediaDataDecoderProxy
+  : public MediaDataDecoder
+  , public DecoderDoctorLifeLogger<MediaDataDecoderProxy>
 {
 public:
   explicit MediaDataDecoderProxy(already_AddRefed<AbstractThread> aProxyThread)
     : mProxyThread(aProxyThread)
 #if defined(DEBUG)
     , mIsShutdown(false)
 #endif
   {
@@ -28,22 +32,24 @@ public:
 
   explicit MediaDataDecoderProxy(
     already_AddRefed<MediaDataDecoder> aProxyDecoder)
     : mProxyDecoder(aProxyDecoder)
 #if defined(DEBUG)
     , mIsShutdown(false)
 #endif
   {
+    DDLINKCHILD("proxy decoder", mProxyDecoder.get());
   }
 
   void SetProxyTarget(MediaDataDecoder* aProxyDecoder)
   {
     MOZ_ASSERT(aProxyDecoder);
     mProxyDecoder = aProxyDecoder;
+    DDLINKCHILD("proxy decoder", aProxyDecoder);
   }
 
   RefPtr<InitPromise> Init() override;
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
   RefPtr<DecodePromise> Drain() override;
   RefPtr<FlushPromise> Flush() override;
   RefPtr<ShutdownPromise> Shutdown() override;
   nsCString GetDescriptionName() const override;
--- a/dom/media/wave/WaveDemuxer.cpp
+++ b/dom/media/wave/WaveDemuxer.cpp
@@ -21,23 +21,25 @@ using mozilla::media::TimeIntervals;
 
 namespace mozilla {
 
 // WAVDemuxer
 
 WAVDemuxer::WAVDemuxer(MediaResource* aSource)
   : mSource(aSource)
 {
+  DDLINKCHILD("source", aSource);
 }
 
 bool
 WAVDemuxer::InitInternal()
 {
   if (!mTrackDemuxer) {
     mTrackDemuxer = new WAVTrackDemuxer(mSource.GetResource());
+    DDLINKCHILD("track demuxer", mTrackDemuxer.get());
   }
   return mTrackDemuxer->Init();
 }
 
 RefPtr<WAVDemuxer::InitPromise>
 WAVDemuxer::Init()
 {
   if (!InitInternal()) {
@@ -76,16 +78,17 @@ WAVTrackDemuxer::WAVTrackDemuxer(MediaRe
   , mFirstChunkOffset(0)
   , mNumParsedChunks(0)
   , mChunkIndex(0)
   , mTotalChunkLen(0)
   , mSamplesPerChunk(0)
   , mSamplesPerSecond(0)
   , mChannels(0)
 {
+  DDLINKCHILD("source", aSource);
   Reset();
 }
 
 bool
 WAVTrackDemuxer::Init()
 {
   Reset();
   FastSeek(TimeUnit());
--- a/dom/media/wave/WaveDemuxer.h
+++ b/dom/media/wave/WaveDemuxer.h
@@ -21,17 +21,22 @@ static const uint8_t WAVE[4] = {'W', 'A'
 
 static const uint16_t RIFF_CHUNK_SIZE = 12;
 static const uint16_t CHUNK_HEAD_SIZE = 8;
 static const uint16_t FMT_CHUNK_MIN_SIZE = 16;
 static const uint16_t DATA_CHUNK_SIZE = 768;
 
 class WAVTrackDemuxer;
 
-class WAVDemuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(WAVDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(WAVTrackDemuxer, MediaTrackDemuxer);
+
+class WAVDemuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<WAVDemuxer>
 {
 public:
   // MediaDataDemuxer interface.
   explicit WAVDemuxer(MediaResource* aSource);
   RefPtr<InitPromise> Init() override;
   uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
     TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
@@ -171,17 +176,19 @@ private:
     void Reset();
   private:
     int mPos; // To Check Alignment
   };
 
   DataChunk mChunk;
 };
 
-class WAVTrackDemuxer : public MediaTrackDemuxer
+class WAVTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<WAVTrackDemuxer>
 {
 public:
   explicit WAVTrackDemuxer(MediaResource* aSource);
 
   bool Init();
 
   int64_t StreamLength() const;
 
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -173,16 +173,20 @@ WebMDemuxer::WebMDemuxer(MediaResource* 
   , mAudioCodec(-1)
   , mVideoCodec(-1)
   , mHasVideo(false)
   , mHasAudio(false)
   , mNeedReIndex(true)
   , mLastWebMBlockOffset(-1)
   , mIsMediaSource(aIsMediaSource)
 {
+  DDLINKCHILD("resource", aResource);
+  // Audio/video contexts hold a MediaResourceIndex.
+  DDLINKCHILD("video context", mVideoContext.GetResource());
+  DDLINKCHILD("audio context", mAudioContext.GetResource());
 }
 
 WebMDemuxer::~WebMDemuxer()
 {
   Reset(TrackInfo::kVideoTrack);
   Reset(TrackInfo::kAudioTrack);
 }
 
@@ -242,16 +246,17 @@ WebMDemuxer::GetTrackInfo(TrackInfo::Tra
 already_AddRefed<MediaTrackDemuxer>
 WebMDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
 {
   if (GetNumberTracks(aType) <= aTrackNumber) {
     return nullptr;
   }
   RefPtr<WebMTrackDemuxer> e =
     new WebMTrackDemuxer(this, aType, aTrackNumber);
+  DDLINKCHILD("track demuxer", e.get());
   mDemuxers.AppendElement(e);
 
   return e.forget();
 }
 
 nsresult
 WebMDemuxer::Reset(TrackInfo::TrackType aType)
 {
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -117,17 +117,22 @@ class MediaRawDataQueue
   }
 
 private:
   ContainerType mQueue;
 };
 
 class WebMTrackDemuxer;
 
-class WebMDemuxer : public MediaDataDemuxer
+DDLoggedTypeDeclNameAndBase(WebMDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(WebMTrackDemuxer, MediaTrackDemuxer);
+
+class WebMDemuxer
+  : public MediaDataDemuxer
+  , public DecoderDoctorLifeLogger<WebMDemuxer>
 {
 public:
   explicit WebMDemuxer(MediaResource* aResource);
   // Indicate if the WebMDemuxer is to be used with MediaSource. In which
   // case the demuxer will stop reads to the last known complete block.
   WebMDemuxer(MediaResource* aResource, bool aIsMediaSource);
 
   RefPtr<InitPromise> Init() override;
@@ -285,17 +290,19 @@ private:
   Maybe<gfx::IntSize> mLastSeenFrameSize;
   // This will be populated only if a resolution change occurs, otherwise it
   // will be left as null so the original metadata is used
   RefPtr<TrackInfoSharedPtr> mSharedVideoTrackInfo;
 
   EncryptionInfo mCrypto;
 };
 
-class WebMTrackDemuxer : public MediaTrackDemuxer
+class WebMTrackDemuxer
+  : public MediaTrackDemuxer
+  , public DecoderDoctorLifeLogger<WebMTrackDemuxer>
 {
 public:
   WebMTrackDemuxer(WebMDemuxer* aParent,
                   TrackInfo::TrackType aType,
                   uint32_t aTrackNumber);
 
   UniquePtr<TrackInfo> GetInfo() const override;