Bug 1108950 part.5 - Fix code alignment. r?roc draft
authorctai <ctai@mozilla.com>
Tue, 01 Sep 2015 10:19:32 +0800
changeset 289368 3056e58a5dbabd365b3dfa9b86c13c5f47b6e7c2
parent 289367 a498cb75e5f2742d0a5d80674d45560c3219a8f5
child 289369 449f25d0b8dcc42aed265aaa2d8f40c558a9a969
push id4976
push userctai@mozilla.com
push dateTue, 01 Sep 2015 02:32:48 +0000
reviewersroc
bugs1108950
milestone43.0a1
Bug 1108950 part.5 - Fix code alignment. r?roc
dom/media/TrackUnionStream.cpp
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -59,771 +59,771 @@ PRLogModuleInfo* gTrackUnionStreamLog;
 TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) :
   ProcessedMediaStream(aWrapper), mNextAvailableTrackID(1)
 {
   if (!gTrackUnionStreamLog) {
     gTrackUnionStreamLog = PR_NewLogModule("TrackUnionStream");
   }
 }
 
-  void TrackUnionStream::RemoveInput(MediaInputPort* aPort)
-  {
-    for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
-      if (mTrackMap[i].mInputPort == aPort) {
-        EndTrack(i);
-        mTrackMap.RemoveElementAt(i);
+void TrackUnionStream::RemoveInput(MediaInputPort* aPort)
+{
+  for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
+    if (mTrackMap[i].mInputPort == aPort) {
+      EndTrack(i);
+      mTrackMap.RemoveElementAt(i);
+    }
+  }
+  ProcessedMediaStream::RemoveInput(aPort);
+}
+void TrackUnionStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
+{
+  if (IsFinishedOnGraphThread()) {
+    return;
+  }
+  nsAutoTArray<bool,8> mappedTracksFinished;
+  nsAutoTArray<bool,8> mappedTracksWithMatchingInputTracks;
+  for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
+    mappedTracksFinished.AppendElement(true);
+    mappedTracksWithMatchingInputTracks.AppendElement(false);
+  }
+  bool allFinished = !mInputs.IsEmpty();
+  bool allHaveCurrentData = !mInputs.IsEmpty();
+  for (uint32_t i = 0; i < mInputs.Length(); ++i) {
+    MediaStream* stream = mInputs[i]->GetSource();
+    if (!stream->IsFinishedOnGraphThread()) {
+      // XXX we really should check whether 'stream' has finished within time aTo,
+      // not just that it's finishing when all its queued data eventually runs
+      // out.
+      allFinished = false;
+    }
+    if (!stream->HasCurrentData()) {
+      allHaveCurrentData = false;
+    }
+    bool trackAdded = false;
+    for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer());
+         !tracks.IsEnded(); tracks.Next()) {
+      bool found = false;
+      for (uint32_t j = 0; j < mTrackMap.Length(); ++j) {
+        TrackMapEntry* map = &mTrackMap[j];
+        if (map->mInputPort == mInputs[i] && map->mInputTrackID == tracks->GetID()) {
+          bool trackFinished;
+          StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID);
+          if (!outputTrack || outputTrack->IsEnded()) {
+            trackFinished = true;
+          } else {
+            CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished);
+          }
+          mappedTracksFinished[j] = trackFinished;
+          mappedTracksWithMatchingInputTracks[j] = true;
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        bool trackFinished = false;
+        trackAdded = true;
+        uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom);
+        CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished);
+        mappedTracksFinished.AppendElement(trackFinished);
+        mappedTracksWithMatchingInputTracks.AppendElement(true);
       }
     }
-    ProcessedMediaStream::RemoveInput(aPort);
-  }
-  void TrackUnionStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
-  {
-    if (IsFinishedOnGraphThread()) {
-      return;
-    }
-    nsAutoTArray<bool,8> mappedTracksFinished;
-    nsAutoTArray<bool,8> mappedTracksWithMatchingInputTracks;
-    for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
-      mappedTracksFinished.AppendElement(true);
-      mappedTracksWithMatchingInputTracks.AppendElement(false);
-    }
-    bool allFinished = !mInputs.IsEmpty();
-    bool allHaveCurrentData = !mInputs.IsEmpty();
-    for (uint32_t i = 0; i < mInputs.Length(); ++i) {
-      MediaStream* stream = mInputs[i]->GetSource();
-      if (!stream->IsFinishedOnGraphThread()) {
-        // XXX we really should check whether 'stream' has finished within time aTo,
-        // not just that it's finishing when all its queued data eventually runs
-        // out.
-        allFinished = false;
-      }
-      if (!stream->HasCurrentData()) {
-        allHaveCurrentData = false;
-      }
-      bool trackAdded = false;
-      for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer());
-           !tracks.IsEnded(); tracks.Next()) {
-        bool found = false;
-        for (uint32_t j = 0; j < mTrackMap.Length(); ++j) {
-          TrackMapEntry* map = &mTrackMap[j];
-          if (map->mInputPort == mInputs[i] && map->mInputTrackID == tracks->GetID()) {
-            bool trackFinished;
-            StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID);
-            if (!outputTrack || outputTrack->IsEnded()) {
-              trackFinished = true;
-            } else {
-              CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished);
-            }
-            mappedTracksFinished[j] = trackFinished;
-            mappedTracksWithMatchingInputTracks[j] = true;
-            found = true;
-            break;
-          }
-        }
-        if (!found) {
-          bool trackFinished = false;
-          trackAdded = true;
-          uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom);
-          CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished);
-          mappedTracksFinished.AppendElement(trackFinished);
-          mappedTracksWithMatchingInputTracks.AppendElement(true);
-        }
-      }
-      if (trackAdded) {
-        for (MediaStreamListener* l : mListeners) {
-          l->NotifyFinishedTrackCreation(Graph());
-        }
+    if (trackAdded) {
+      for (MediaStreamListener* l : mListeners) {
+        l->NotifyFinishedTrackCreation(Graph());
       }
     }
-    for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
-      if (mappedTracksFinished[i]) {
-        EndTrack(i);
-      } else {
-        allFinished = false;
-      }
-      if (!mappedTracksWithMatchingInputTracks[i]) {
-        mTrackMap.RemoveElementAt(i);
-      }
+  }
+  for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
+    if (mappedTracksFinished[i]) {
+      EndTrack(i);
+    } else {
+      allFinished = false;
     }
-    if (allFinished && mAutofinish && (aFlags & ALLOW_FINISH)) {
-      // All streams have finished and won't add any more tracks, and
-      // all our tracks have actually finished and been removed from our map,
-      // so we're finished now.
-      FinishOnGraphThread();
-    } else {
-      mBuffer.AdvanceKnownTracksTime(GraphTimeToStreamTime(aTo));
-    }
-    if (allHaveCurrentData) {
-      // We can make progress if we're not blocked
-      mHasCurrentData = true;
+    if (!mappedTracksWithMatchingInputTracks[i]) {
+      mTrackMap.RemoveElementAt(i);
     }
   }
+  if (allFinished && mAutofinish && (aFlags & ALLOW_FINISH)) {
+    // All streams have finished and won't add any more tracks, and
+    // all our tracks have actually finished and been removed from our map,
+    // so we're finished now.
+    FinishOnGraphThread();
+  } else {
+    mBuffer.AdvanceKnownTracksTime(GraphTimeToStreamTime(aTo));
+  }
+  if (allHaveCurrentData) {
+    // We can make progress if we're not blocked
+    mHasCurrentData = true;
+  }
+}
 
-  void TrackUnionStream::UpdateWorkerStatusImpl(WorkerPrivate* aWorker,
-                                                TrackID aTrackID,
-                                                bool aHasOnFlyEvent)
-  {
-    for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
-      TrackMapEntry* entry = &mTrackMap[i];
-      if (entry->mOutputTrackID == aTrackID) {
-        for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
-          WorkerInformation& info = entry->mWorkerMonitorInfos[j];
-          if (aWorker == info.mWorker) {
-            info.mOnFlyStatus = aHasOnFlyEvent;
-            // Check whether the latest image dispatched or not. If the
-            // dispatched is not the latest source frame, dispatch the latest
-            // frame.
-            if (info.mDispatchedImage != entry->mLastImage) {
-              DispatchToVideoWorkerMonitor(entry);
-            }
-            return;
+void TrackUnionStream::UpdateWorkerStatusImpl(WorkerPrivate* aWorker,
+                                              TrackID aTrackID,
+                                              bool aHasOnFlyEvent)
+{
+  for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
+    TrackMapEntry* entry = &mTrackMap[i];
+    if (entry->mOutputTrackID == aTrackID) {
+      for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
+        WorkerInformation& info = entry->mWorkerMonitorInfos[j];
+        if (aWorker == info.mWorker) {
+          info.mOnFlyStatus = aHasOnFlyEvent;
+          // Check whether the latest image dispatched or not. If the
+          // dispatched is not the latest source frame, dispatch the latest
+          // frame.
+          if (info.mDispatchedImage != entry->mLastImage) {
+            DispatchToVideoWorkerMonitor(entry);
           }
+          return;
         }
       }
     }
   }
+}
 
-  // Forward SetTrackEnabled(output_track_id, enabled) to the Source MediaStream,
-  // translating the output track ID into the correct ID in the source.
-  void TrackUnionStream::ForwardTrackEnabled(TrackID aOutputID, bool aEnabled)
+// Forward SetTrackEnabled(output_track_id, enabled) to the Source MediaStream,
+// translating the output track ID into the correct ID in the source.
+void TrackUnionStream::ForwardTrackEnabled(TrackID aOutputID, bool aEnabled)
+{
+  for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
+    if (mTrackMap[i].mOutputTrackID == aOutputID) {
+      mTrackMap[i].mInputPort->GetSource()->
+        SetTrackEnabled(mTrackMap[i].mInputTrackID, aEnabled);
+    }
+  }
+}
+
+class ResolvePromiseAtMainThread : public nsRunnable
+{
+public:
+  ResolvePromiseAtMainThread(nsRefPtr<Promise>& aPromise)
+    : mPromise(nullptr)
+  {
+    // Extend the life cycle.
+    mPromise.swap(aPromise);
+  }
+
+  NS_IMETHOD
+  Run(void) override
+  {
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<Promise> mPromise;
+};
+
+void TrackUnionStream::AddVideoMonitor(Promise* aPromise,
+                                       WorkerPrivate* aWorker,
+                                       TrackID aTrackID)
+{
+  class Message final : public ControlMessage
   {
-    for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
-      if (mTrackMap[i].mOutputTrackID == aOutputID) {
-        mTrackMap[i].mInputPort->GetSource()->
-          SetTrackEnabled(mTrackMap[i].mInputTrackID, aEnabled);
+  public:
+    Message(TrackUnionStream* aStream,
+            Promise* aPromise,
+            WorkerPrivate* aWorker,
+            TrackID aTrackID)
+      : ControlMessage(aStream)
+      , mWorker(aWorker)
+      , mPromise(aPromise)
+      , mTrackID(aTrackID)
+    {}
+    virtual void Run() override
+    {
+      mStream->AsTrackUnionStream()->AddVideoMonitorImpl(mWorker, mTrackID);
+
+      // Resolve the promise in main thread since this promise created from
+      // main thread.
+      nsRefPtr<ResolvePromiseAtMainThread> resolveRunnable =
+        new ResolvePromiseAtMainThread(mPromise);
+      NS_DispatchToMainThread(resolveRunnable);
+    }
+    WorkerPrivate* mWorker;
+    nsRefPtr<Promise> mPromise;
+    TrackID mTrackID;
+  };
+  GraphImpl()->AppendMessage(new Message(this, aPromise, aWorker, aTrackID));
+}
+
+void TrackUnionStream::AddVideoMonitorImpl(WorkerPrivate* aWorker,
+                                           TrackID aTrackID)
+{
+  MOZ_ASSERT(aWorker);
+  for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
+    TrackMapEntry* entry = &mTrackMap[i];
+    if (entry->mOutputTrackID == aTrackID) {
+      for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
+        if (aWorker == entry->mWorkerMonitorInfos[j].mWorker) {
+          // We already add this worker before. No need to add again.
+          return;
+        }
+      }
+      WorkerInformation* info = entry->mWorkerMonitorInfos.AppendElement();
+      info->mWorker = aWorker;
+      info->mDispatchedImage = nullptr;
+      info->mWorkerFeature = new VideoWorkerFeature(aWorker, this);
+      MOZ_ASSERT(info->mWorkerFeature);
+      info->mOnFlyStatus = false;
+      MediaStream* destStream = entry->mInputPort->GetDestination();
+      destStream->QueryDOMTrackID(aTrackID, entry->mDOMTrackID);
+      MOZ_ASSERT(!entry->mDOMTrackID.IsVoid());
+
+      // |WorkerPrivate::AddFeature| could be called in worker thread only.
+      // We need an WorkerRunnable to add the WorkerFeature to the worker.
+      // This runnable is dispatched from MSG thread, not from parent thread,
+      // main thread or worker thread. So the BusyCount balance is controlled
+      // by the runnable itself. Also we already added BusyCount while the
+      // |VideoStreamTrack::AddVideoMonitor| is called. The worker shhould be
+      // kept alive at this point.
+      class AddFeatureRunnable final: public WorkerRunnable
+      {
+      public:
+        AddFeatureRunnable(WorkerPrivate* aWorkerPrivate,
+                           WorkerFeature* aFeature)
+          : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+          , mFeature(aFeature)
+        {
+        }
+      private:
+        virtual bool
+        WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
+          aWorkerPrivate->AssertIsOnWorkerThread();
+          aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
+          return aWorkerPrivate->AddFeature(aCx, mFeature);
+        }
+        void
+        PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
+        {
+          aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
+        }
+
+        // Override |PreDispatch| and |PostDispatch| to avoid triggering
+        // |AssertIsOnParentThread| because of dispatching from MSG thread.
+        bool
+        PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+        {
+          return true;
+        }
+
+        void
+        PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+                     bool aDispatchResult) override
+        {
+        }
+
+        WorkerFeature* mFeature;
+      };
+
+      nsRefPtr<AddFeatureRunnable> runnable =
+        new AddFeatureRunnable(info->mWorker, info->mWorkerFeature.get());
+      runnable->Dispatch(nullptr);
+    }
+  }
+}
+
+// This runnable is dispatched from MSG thread, not from parent thread,
+// main thread or worker thread. So the BusyCount balance is controlled
+// by the runnable itself.
+class RemoveFeatureRunnable final: public WorkerRunnable
+{
+public:
+  RemoveFeatureRunnable(WorkerPrivate* aWorkerPrivate,
+                        WorkerFeature* aFeature)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mFeature(aFeature)
+  {
+  }
+private:
+  virtual bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
+    aWorkerPrivate->RemoveFeature(aCx, mFeature);
+    return true;
+  }
+  void
+  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
+  {
+    aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
+  }
+
+  // Override |PreDispatch| and |PostDispatch| to avoid triggering
+  // |AssertIsOnParentThread| because of dispatching from MSG thread.
+  bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    return true;
+  }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult) override
+  {
+  }
+
+  WorkerFeature* mFeature;
+};
+
+
+void TrackUnionStream::RemoveVideoMonitor(Maybe<nsRefPtr<Promise>> aPromise,
+                                          WorkerPrivate* aWorker,
+                                          Maybe<TrackID> aTrackID,
+                                          bool aNeedRemoveFeature)
+{
+  class Message final : public ControlMessage
+  {
+  public:
+    Message(TrackUnionStream* aStream, Maybe<nsRefPtr<Promise>> aPromise,
+            WorkerPrivate* aWorker, Maybe<TrackID> aTrackID,
+            bool aNeedRemoveFeature)
+      : ControlMessage(aStream)
+      , mWorker(aWorker)
+      , mPromise(aPromise)
+      , mTrackID(aTrackID)
+      , mNeedRemoveFeature(aNeedRemoveFeature)
+    {
+    }
+    virtual void Run() override
+    {
+      mStream->AsTrackUnionStream()->RemoveVideoMonitorImpl(mWorker, mTrackID, mNeedRemoveFeature);
+
+      if (mPromise.isNothing()) {
+        return;
+      }
+      // Resolve the promise in main thread since this promise created from
+      // main thread.
+      nsRefPtr<ResolvePromiseAtMainThread> resolveRunnable =
+        new ResolvePromiseAtMainThread(mPromise.ref());
+      NS_DispatchToMainThread(resolveRunnable);
+    }
+    WorkerPrivate* mWorker;
+    Maybe<nsRefPtr<Promise>> mPromise;
+    Maybe<TrackID> mTrackID;
+    bool mNeedRemoveFeature;
+  };
+  GraphImpl()->AppendMessage(new Message(this, aPromise, aWorker,
+                                         aTrackID, aNeedRemoveFeature));
+}
+
+
+
+void TrackUnionStream::RemoveVideoMonitorImpl(WorkerPrivate* aWorker,
+                                              Maybe<TrackID> aTrackID,
+                                              bool aNeedRemoveFeature)
+{
+  for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
+    TrackMapEntry* entry = &mTrackMap[i];
+    if (aTrackID.isSome()) {
+      if (entry->mOutputTrackID != aTrackID.value()) {
+        continue;
+      }
+    }
+    for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
+      WorkerInformation& info = entry->mWorkerMonitorInfos[j];
+      if (aWorker == info.mWorker) {
+        if (aNeedRemoveFeature) {
+          nsRefPtr<RemoveFeatureRunnable> runnable =
+            new RemoveFeatureRunnable(info.mWorker, info.mWorkerFeature.get());
+          NS_WARN_IF(runnable->Dispatch(nullptr));
+        }
+        entry->mWorkerMonitorInfos.RemoveElementAt(j);
+        // We explicitly point out to remove particular track and worker. So
+        // we can early return when we found the worker.
+        if (aTrackID.isSome()) {
+          return;
+        }
       }
     }
   }
+}
 
-  class ResolvePromiseAtMainThread : public nsRunnable
-  {
-  public:
-    ResolvePromiseAtMainThread(nsRefPtr<Promise>& aPromise)
-      : mPromise(nullptr)
-    {
-      // Extend the life cycle.
-      mPromise.swap(aPromise);
+TrackUnionStream::~TrackUnionStream()
+{
+  for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
+    TrackMapEntry* entry = &mTrackMap[i];
+    for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
+      nsRefPtr<RemoveFeatureRunnable> runnable = new
+        RemoveFeatureRunnable(entry->mWorkerMonitorInfos[j].mWorker,
+                              entry->mWorkerMonitorInfos[j].mWorkerFeature.get());
+      NS_WARN_IF(runnable->Dispatch(nullptr));
     }
+  }
+}
 
-    NS_IMETHOD
-    Run(void) override
-    {
-      mPromise->MaybeResolve(JS::UndefinedHandleValue);
-      return NS_OK;
-    }
+// Worker is going to closing or terminating. Remove the feature and the
+// worker record in TrackMapEntry::mWorkerMonitorInfos.
+bool TrackUnionStream::VideoWorkerFeature::Notify(JSContext* aCx, Status aStatus)
+{
+  MOZ_ASSERT(aStatus > workers::Running);
+  if (aStatus >= Closing) {
+    MOZ_ASSERT(mWorker);
+    mWorker->AssertIsOnWorkerThread();
+    // We must call |RemoveFeature| right here. Because the worker is closing,
+    // any WorkerRunnable can not be executed. If the WorkerFeature is not be
+    // removed, that will cause crash.
+    mWorker->RemoveFeature(aCx, this);
 
-  private:
-    nsRefPtr<Promise> mPromise;
-  };
-
-  void TrackUnionStream::AddVideoMonitor(Promise* aPromise,
-                                         WorkerPrivate* aWorker,
-                                         TrackID aTrackID)
-  {
-    class Message final : public ControlMessage
+    class RemoveWorkerInfoRunnable : public nsRunnable
     {
     public:
-      Message(TrackUnionStream* aStream,
-              Promise* aPromise,
-              WorkerPrivate* aWorker,
-              TrackID aTrackID)
-        : ControlMessage(aStream)
-        , mWorker(aWorker)
-        , mPromise(aPromise)
-        , mTrackID(aTrackID)
+      RemoveWorkerInfoRunnable(WorkerPrivate* aWorker, TrackUnionStream* aStream)
+        : mWorker(aWorker)
+        , mStream(aStream)
       {}
-      virtual void Run() override
+
+      NS_IMETHOD
+      Run(void) override
       {
-        mStream->AsTrackUnionStream()->AddVideoMonitorImpl(mWorker, mTrackID);
-
-        // Resolve the promise in main thread since this promise created from
-        // main thread.
-        nsRefPtr<ResolvePromiseAtMainThread> resolveRunnable =
-          new ResolvePromiseAtMainThread(mPromise);
-        NS_DispatchToMainThread(resolveRunnable);
+        mStream->RemoveVideoMonitor(Nothing(), mWorker, Nothing(), false);
+        return NS_OK;
       }
       WorkerPrivate* mWorker;
-      nsRefPtr<Promise> mPromise;
-      TrackID mTrackID;
+      TrackUnionStream* mStream;
     };
-    GraphImpl()->AppendMessage(new Message(this, aPromise, aWorker, aTrackID));
-  }
-
-  void TrackUnionStream::AddVideoMonitorImpl(WorkerPrivate* aWorker,
-                                             TrackID aTrackID)
-  {
-    MOZ_ASSERT(aWorker);
-    for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
-      TrackMapEntry* entry = &mTrackMap[i];
-      if (entry->mOutputTrackID == aTrackID) {
-        for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
-          if (aWorker == entry->mWorkerMonitorInfos[j].mWorker) {
-            // We already add this worker before. No need to add again.
-            return;
-          }
-        }
-        WorkerInformation* info = entry->mWorkerMonitorInfos.AppendElement();
-        info->mWorker = aWorker;
-        info->mDispatchedImage = nullptr;
-        info->mWorkerFeature = new VideoWorkerFeature(aWorker, this);
-        MOZ_ASSERT(info->mWorkerFeature);
-        info->mOnFlyStatus = false;
-        MediaStream* destStream = entry->mInputPort->GetDestination();
-        destStream->QueryDOMTrackID(aTrackID, entry->mDOMTrackID);
-        MOZ_ASSERT(!entry->mDOMTrackID.IsVoid());
 
-        // |WorkerPrivate::AddFeature| could be called in worker thread only.
-        // We need an WorkerRunnable to add the WorkerFeature to the worker.
-        // This runnable is dispatched from MSG thread, not from parent thread,
-        // main thread or worker thread. So the BusyCount balance is controlled
-        // by the runnable itself. Also we already added BusyCount while the
-        // |VideoStreamTrack::AddVideoMonitor| is called. The worker shhould be
-        // kept alive at this point.
-        class AddFeatureRunnable final: public WorkerRunnable
-        {
-        public:
-          AddFeatureRunnable(WorkerPrivate* aWorkerPrivate,
-                             WorkerFeature* aFeature)
-            : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-            , mFeature(aFeature)
-          {
-          }
-        private:
-          virtual bool
-          WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
-            aWorkerPrivate->AssertIsOnWorkerThread();
-            aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
-            return aWorkerPrivate->AddFeature(aCx, mFeature);
-          }
-          void
-          PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
-          {
-            aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
-          }
-
-          // Override |PreDispatch| and |PostDispatch| to avoid triggering
-          // |AssertIsOnParentThread| because of dispatching from MSG thread.
-          bool
-          PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-          {
-            return true;
-          }
-
-          void
-          PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-                       bool aDispatchResult) override
-          {
-          }
-
-          WorkerFeature* mFeature;
-        };
-
-        nsRefPtr<AddFeatureRunnable> runnable =
-          new AddFeatureRunnable(info->mWorker, info->mWorkerFeature.get());
-        runnable->Dispatch(nullptr);
-      }
-    }
+    nsRefPtr<RemoveWorkerInfoRunnable> runnable = new
+        RemoveWorkerInfoRunnable(mWorker, mStream);
+    NS_DispatchToMainThread(runnable);
   }
+  return true;
+}
 
-  // This runnable is dispatched from MSG thread, not from parent thread,
-  // main thread or worker thread. So the BusyCount balance is controlled
-  // by the runnable itself.
-  class RemoveFeatureRunnable final: public WorkerRunnable
-  {
-  public:
-    RemoveFeatureRunnable(WorkerPrivate* aWorkerPrivate,
-                          WorkerFeature* aFeature)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-      , mFeature(aFeature)
-    {
-    }
-  private:
-    virtual bool
-    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
-      aWorkerPrivate->AssertIsOnWorkerThread();
-      aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
-      aWorkerPrivate->RemoveFeature(aCx, mFeature);
-      return true;
-    }
-    void
-    PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
-    {
-      aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
-    }
-
-    // Override |PreDispatch| and |PostDispatch| to avoid triggering
-    // |AssertIsOnParentThread| because of dispatching from MSG thread.
-    bool
-    PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-    {
-      return true;
-    }
-
-    void
-    PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-                 bool aDispatchResult) override
-    {
-    }
-
-    WorkerFeature* mFeature;
-  };
-
+uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack,
+                  GraphTime aFrom)
+{
+  TrackID id = aTrack->GetID();
+  if (id > mNextAvailableTrackID &&
+     mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) {
+    // Input id available. Mark it used in mUsedTracks.
+    mUsedTracks.InsertElementSorted(id);
+  } else {
+    // Input id taken, allocate a new one.
+    id = mNextAvailableTrackID;
 
-  void TrackUnionStream::RemoveVideoMonitor(Maybe<nsRefPtr<Promise>> aPromise,
-                                            WorkerPrivate* aWorker,
-                                            Maybe<TrackID> aTrackID,
-                                            bool aNeedRemoveFeature)
-  {
-    class Message final : public ControlMessage
-    {
-    public:
-      Message(TrackUnionStream* aStream, Maybe<nsRefPtr<Promise>> aPromise,
-              WorkerPrivate* aWorker, Maybe<TrackID> aTrackID,
-              bool aNeedRemoveFeature)
-        : ControlMessage(aStream)
-        , mWorker(aWorker)
-        , mPromise(aPromise)
-        , mTrackID(aTrackID)
-        , mNeedRemoveFeature(aNeedRemoveFeature)
-      {
-      }
-      virtual void Run() override
-      {
-        mStream->AsTrackUnionStream()->RemoveVideoMonitorImpl(mWorker, mTrackID, mNeedRemoveFeature);
-
-        if (mPromise.isNothing()) {
-          return;
-        }
-        // Resolve the promise in main thread since this promise created from
-        // main thread.
-        nsRefPtr<ResolvePromiseAtMainThread> resolveRunnable =
-          new ResolvePromiseAtMainThread(mPromise.ref());
-        NS_DispatchToMainThread(resolveRunnable);
-      }
-      WorkerPrivate* mWorker;
-      Maybe<nsRefPtr<Promise>> mPromise;
-      Maybe<TrackID> mTrackID;
-      bool mNeedRemoveFeature;
-    };
-    GraphImpl()->AppendMessage(new Message(this, aPromise, aWorker,
-                                           aTrackID, aNeedRemoveFeature));
-  }
-
-
-
-  void TrackUnionStream::RemoveVideoMonitorImpl(WorkerPrivate* aWorker,
-                                                Maybe<TrackID> aTrackID,
-                                                bool aNeedRemoveFeature)
-  {
-    for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
-      TrackMapEntry* entry = &mTrackMap[i];
-      if (aTrackID.isSome()) {
-        if (entry->mOutputTrackID != aTrackID.value()) {
-          continue;
-        }
-      }
-      for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
-        WorkerInformation& info = entry->mWorkerMonitorInfos[j];
-        if (aWorker == info.mWorker) {
-          if (aNeedRemoveFeature) {
-            nsRefPtr<RemoveFeatureRunnable> runnable =
-              new RemoveFeatureRunnable(info.mWorker, info.mWorkerFeature.get());
-            NS_WARN_IF(runnable->Dispatch(nullptr));
-          }
-          entry->mWorkerMonitorInfos.RemoveElementAt(j);
-          // We explicitly point out to remove particular track and worker. So
-          // we can early return when we found the worker.
-          if (aTrackID.isSome()) {
-            return;
-          }
-        }
+    // Update mNextAvailableTrackID and prune any mUsedTracks members it now
+    // covers.
+    while (1) {
+      if (!mUsedTracks.RemoveElementSorted(++mNextAvailableTrackID)) {
+        // Not in use. We're done.
+        break;
       }
     }
   }
 
-  TrackUnionStream::~TrackUnionStream()
+  // Round up the track start time so the track, if anything, starts a
+  // little later than the true time. This means we'll have enough
+  // samples in our input stream to go just beyond the destination time.
+  StreamTime outputStart = GraphTimeToStreamTime(aFrom);
+
+  nsAutoPtr<MediaSegment> segment;
+  segment = aTrack->GetSegment()->CreateEmptyClone();
+  for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+    MediaStreamListener* l = mListeners[j];
+    l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
+                                MediaStreamListener::TRACK_EVENT_CREATED,
+                                *segment);
+  }
+  segment->AppendNullData(outputStart);
+  StreamBuffer::Track* track =
+    &mBuffer.AddTrack(id, outputStart, segment.forget());
+  STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld",
+                            this, id, aPort->GetSource(), aTrack->GetID(),
+                            (long long)outputStart));
+  TrackMapEntry* map = mTrackMap.AppendElement();
+  map->mDOMTrackID.SetIsVoid(true);
+  map->mLastImage = nullptr;
+  map->mLastImageStreamTime = 0;
+  map->mEndOfConsumedInputTicks = 0;
+  map->mEndOfLastInputIntervalInInputStream = -1;
+  map->mEndOfLastInputIntervalInOutputStream = -1;
+  map->mInputPort = aPort;
+  map->mInputTrackID = aTrack->GetID();
+  map->mOutputTrackID = track->GetID();
+  map->mSegment = aTrack->GetSegment()->CreateEmptyClone();
+  return mTrackMap.Length() - 1;
+}
+
+void TrackUnionStream::EndTrack(uint32_t aIndex)
+{
+  StreamBuffer::Track* outputTrack = mBuffer.FindTrack(mTrackMap[aIndex].mOutputTrackID);
+  if (!outputTrack || outputTrack->IsEnded())
+    return;
+  for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+    MediaStreamListener* l = mListeners[j];
+    StreamTime offset = outputTrack->GetSegment()->GetDuration();
+    nsAutoPtr<MediaSegment> segment;
+    segment = outputTrack->GetSegment()->CreateEmptyClone();
+    l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
+                                MediaStreamListener::TRACK_EVENT_ENDED,
+                                *segment);
+  }
+  outputTrack->SetEnded();
+}
+
+void TrackUnionStream::UpdateWorkerStatus(WorkerPrivate* aWorker,
+                                          TrackID aTrackID,
+                                          bool aHasOnFlyEvent)
+{
+  class Message final : public ControlMessage
   {
-    for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
-      TrackMapEntry* entry = &mTrackMap[i];
-      for (uint32_t j = 0; j < entry->mWorkerMonitorInfos.Length(); ++j) {
-        nsRefPtr<RemoveFeatureRunnable> runnable = new
-          RemoveFeatureRunnable(entry->mWorkerMonitorInfos[j].mWorker,
-                                entry->mWorkerMonitorInfos[j].mWorkerFeature.get());
-        NS_WARN_IF(runnable->Dispatch(nullptr));
-      }
+  public:
+    Message(TrackUnionStream* aStream,
+            WorkerPrivate* aWorker,
+            TrackID aTrackID,
+            bool aHasOnFlyEvent)
+      : ControlMessage(aStream)
+      , mWorker(aWorker)
+      , mTrackID(aTrackID)
+      , mHasOnFlyEvent(aHasOnFlyEvent)
+    {}
+    virtual void Run() override
+    {
+      mStream->AsTrackUnionStream()->UpdateWorkerStatusImpl(mWorker, mTrackID, mHasOnFlyEvent);
     }
+    WorkerPrivate* mWorker;
+    TrackID mTrackID;
+    bool mHasOnFlyEvent;
+  };
+  // The MediaStream might be shutting down. If the Stream is destroyed, we
+  // don't need to append message to the graph.
+  if (this->IsDestroyed()) {
+    return;
+  }
+  GraphImpl()->AppendMessage(new Message(this, aWorker, aTrackID, aHasOnFlyEvent));
+}
+
+// This runnable is dispatched from MSG thread, not from parent thread,
+// main thread or worker thread. So the BusyCount balance is controlled
+// by the runnable itself.
+class VideoMonitorEventRunnable final : public WorkerRunnable {
+public:
+  VideoMonitorEventRunnable(WorkerPrivate* aWorkerPrivate,
+                            TargetAndBusyBehavior aBehavior,
+                            TrackUnionStream* aStream,
+                            TrackID aTrackID,
+                            double aPlaybackTime,
+                            const nsString& aID,
+                            layers::Image* aImage)
+  :WorkerRunnable(aWorkerPrivate, aBehavior),
+   mStream(aStream),
+   mImage(aImage),
+   mTrackID(aTrackID),
+   mPlaybackTime(aPlaybackTime),
+   mID(aID)
+  {
   }
 
-  // Worker is going to closing or terminating. Remove the feature and the
-  // worker record in TrackMapEntry::mWorkerMonitorInfos.
-  bool TrackUnionStream::VideoWorkerFeature::Notify(JSContext* aCx, Status aStatus)
+private:
+  virtual bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-    MOZ_ASSERT(aStatus > workers::Running);
-    if (aStatus >= Closing) {
-      MOZ_ASSERT(mWorker);
-      mWorker->AssertIsOnWorkerThread();
-      // We must call |RemoveFeature| right here. Because the worker is closing,
-      // any WorkerRunnable can not be executed. If the WorkerFeature is not be
-      // removed, that will cause crash.
-      mWorker->RemoveFeature(aCx, this);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
 
-      class RemoveWorkerInfoRunnable : public nsRunnable
+    // The callback is returned. Set the mOnFlyStatus flag to false which
+    // allowing following event flow.
+    class SetOnFlyEventStatusFalseRunnable : public nsRunnable
+    {
+    public:
+      SetOnFlyEventStatusFalseRunnable(TrackUnionStream* aStream,
+                                       WorkerPrivate* aWorker,
+                                       TrackID aTrackID)
+        : mStream(aStream)
+        , mWorker(aWorker)
+        , mTrackID(aTrackID)
+      {
+        MOZ_ASSERT(mStream);
+        MOZ_ASSERT(mWorker);
+      }
+
+      NS_IMETHOD
+      Run(void) override
       {
-      public:
-        RemoveWorkerInfoRunnable(WorkerPrivate* aWorker, TrackUnionStream* aStream)
-          : mWorker(aWorker)
-          , mStream(aStream)
-        {}
+        // Set the mOnFlyStatus to false;
+        mStream->UpdateWorkerStatus(mWorker, mTrackID, false);
+        return NS_OK;
+      }
+      TrackUnionStream* mStream;
+      WorkerPrivate* mWorker;
+      TrackID mTrackID;
+    };
+    // We need to recover the onFlyEvent status whether the event is executed
+    // successfully or not.
+    nsRefPtr<SetOnFlyEventStatusFalseRunnable> runnable = new
+      SetOnFlyEventStatusFalseRunnable(mStream, aWorkerPrivate, mTrackID);
+    MOZ_ASSERT(runnable);
 
-        NS_IMETHOD
-        Run(void) override
-        {
-          mStream->RemoveVideoMonitor(Nothing(), mWorker, Nothing(), false);
-          return NS_OK;
-        }
-        WorkerPrivate* mWorker;
-        TrackUnionStream* mStream;
-      };
+    DOMEventTargetHelper* target = aWorkerPrivate->GlobalScope();
+    nsRefPtr<VideoMonitorEvent> event =
+      new VideoMonitorEvent(target, nullptr, nullptr);
+    nsresult rv = event->InitEvent(mPlaybackTime, mID, mImage);
+    if (NS_FAILED(rv)) {
+      xpc::Throw(aCx, rv);
+      // The ControlMessage is only allowed to send from main thread.
+      NS_DispatchToMainThread(runnable);
+      return false;
+    }
+    event->SetTrusted(true);
+
+    nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
 
-      nsRefPtr<RemoveWorkerInfoRunnable> runnable = new
-          RemoveWorkerInfoRunnable(mWorker, mStream);
-      NS_DispatchToMainThread(runnable);
+    nsEventStatus dummy = nsEventStatus_eIgnore;
+    // Update the memory usage information to JS context before dispathing
+    // event.
+    if (aCx) {
+      int32_t width = mImage->GetSize().width;
+      int32_t height = mImage->GetSize().height;
+      // We don't know exactly format of mImage. So just set the maximum
+      // possible size.
+      JS_updateMallocCounter(aCx, width * height * 4);
+    }
+    rv = target->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
+    NS_WARN_IF(NS_FAILED(rv));
+
+    // The ControlMessage is only allowed to send from main thread.
+    rv = NS_DispatchToMainThread(runnable);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
     }
     return true;
   }
 
-  uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack,
-                    GraphTime aFrom)
+  void
+  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
+  {
+    aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
+  }
+
+  // Override |PreDispatch| and |PostDispatch| to avoid triggering
+  // |AssertIsOnParentThread| because of dispatching from MSG thread.
+  bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    return true;
+  }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult) override
   {
-    TrackID id = aTrack->GetID();
-    if (id > mNextAvailableTrackID &&
-       mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) {
-      // Input id available. Mark it used in mUsedTracks.
-      mUsedTracks.InsertElementSorted(id);
+  }
+
+  ~VideoMonitorEventRunnable(){
+  }
+
+  TrackUnionStream* mStream;
+  nsRefPtr<Image> mImage;
+  TrackID mTrackID;
+  double mPlaybackTime;
+  nsString mID;
+};
+
+void TrackUnionStream::CopyTrackData(StreamBuffer::Track* aInputTrack,
+                   uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo,
+                   bool* aOutputTrackFinished)
+{
+  TrackMapEntry* map = &mTrackMap[aMapIndex];
+  StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID);
+  MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track");
+
+  MediaSegment* segment = map->mSegment;
+  MediaStream* source = map->mInputPort->GetSource();
+
+  GraphTime next;
+  *aOutputTrackFinished = false;
+  for (GraphTime t = aFrom; t < aTo; t = next) {
+    MediaInputPort::InputInterval interval = map->mInputPort->GetNextInputInterval(t);
+    interval.mEnd = std::min(interval.mEnd, aTo);
+    StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd);
+    StreamTime inputTrackEndPoint = STREAM_TIME_MAX;
+
+    if (aInputTrack->IsEnded() &&
+        aInputTrack->GetEnd() <= inputEnd) {
+      inputTrackEndPoint = aInputTrack->GetEnd();
+      *aOutputTrackFinished = true;
+    }
+
+    if (interval.mStart >= interval.mEnd) {
+      break;
+    }
+    StreamTime ticks = interval.mEnd - interval.mStart;
+    next = interval.mEnd;
+
+    StreamTime outputStart = outputTrack->GetEnd();
+
+    if (interval.mInputIsBlocked) {
+      // Maybe the input track ended?
+      segment->AppendNullData(ticks);
+      STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p appending %lld ticks of null data to track %d",
+                 this, (long long)ticks, outputTrack->GetID()));
+    } else if (InMutedCycle()) {
+      segment->AppendNullData(ticks);
     } else {
-      // Input id taken, allocate a new one.
-      id = mNextAvailableTrackID;
-
-      // Update mNextAvailableTrackID and prune any mUsedTracks members it now
-      // covers.
-      while (1) {
-        if (!mUsedTracks.RemoveElementSorted(++mNextAvailableTrackID)) {
-          // Not in use. We're done.
-          break;
+      if (GraphImpl()->StreamSuspended(source)) {
+        segment->AppendNullData(aTo - aFrom);
+      } else {
+        MOZ_ASSERT(outputTrack->GetEnd() == GraphTimeToStreamTime(interval.mStart),
+                   "Samples missing");
+        StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
+        if (segment->GetType() == MediaSegment::AUDIO){
+          segment->AppendSlice(*aInputTrack->GetSegment(),
+                               std::min(inputTrackEndPoint, inputStart),
+                               std::min(inputTrackEndPoint, inputEnd));
+        } else {
+          nsAutoPtr<MediaSegment> tmpSegment;
+          tmpSegment = aInputTrack->GetSegment()->CreateEmptyClone();
+          tmpSegment->AppendSlice(*aInputTrack->GetSegment(),
+                                  std::min(inputTrackEndPoint, inputStart),
+                                  std::min(inputTrackEndPoint, inputEnd));
+          VideoSegment* videoSegment = static_cast<VideoSegment*>(tmpSegment.get());
+          for (VideoSegment::ChunkIterator i(*videoSegment); !i.IsEnded(); i.Next()) {
+            VideoChunk& chunk = *i;
+            VideoFrame& frame = chunk.mFrame;
+            VideoFrame::Image* image = frame.GetImage();
+            if (image != map->mLastImage) {
+              map->mLastImage = image;
+              map->mLastImageStreamTime = std::min(inputTrackEndPoint, source->GraphTimeToStreamTime(interval.mStart));
+              DispatchToVideoWorkerMonitor(map);
+            }
+          }
+          segment->AppendFrom(videoSegment);
         }
       }
     }
-
-    // Round up the track start time so the track, if anything, starts a
-    // little later than the true time. This means we'll have enough
-    // samples in our input stream to go just beyond the destination time.
-    StreamTime outputStart = GraphTimeToStreamTime(aFrom);
-
-    nsAutoPtr<MediaSegment> segment;
-    segment = aTrack->GetSegment()->CreateEmptyClone();
-    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
-      MediaStreamListener* l = mListeners[j];
-      l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
-                                  MediaStreamListener::TRACK_EVENT_CREATED,
-                                  *segment);
-    }
-    segment->AppendNullData(outputStart);
-    StreamBuffer::Track* track =
-      &mBuffer.AddTrack(id, outputStart, segment.forget());
-    STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld",
-                              this, id, aPort->GetSource(), aTrack->GetID(),
-                              (long long)outputStart));
-    TrackMapEntry* map = mTrackMap.AppendElement();
-    map->mDOMTrackID.SetIsVoid(true);
-    map->mLastImage = nullptr;
-    map->mLastImageStreamTime = 0;
-    map->mEndOfConsumedInputTicks = 0;
-    map->mEndOfLastInputIntervalInInputStream = -1;
-    map->mEndOfLastInputIntervalInOutputStream = -1;
-    map->mInputPort = aPort;
-    map->mInputTrackID = aTrack->GetID();
-    map->mOutputTrackID = track->GetID();
-    map->mSegment = aTrack->GetSegment()->CreateEmptyClone();
-    return mTrackMap.Length() - 1;
-  }
-
-  void TrackUnionStream::EndTrack(uint32_t aIndex)
-  {
-    StreamBuffer::Track* outputTrack = mBuffer.FindTrack(mTrackMap[aIndex].mOutputTrackID);
-    if (!outputTrack || outputTrack->IsEnded())
-      return;
+    ApplyTrackDisabling(outputTrack->GetID(), segment);
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
-      StreamTime offset = outputTrack->GetSegment()->GetDuration();
-      nsAutoPtr<MediaSegment> segment;
-      segment = outputTrack->GetSegment()->CreateEmptyClone();
-      l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
-                                  MediaStreamListener::TRACK_EVENT_ENDED,
-                                  *segment);
+      l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
+                                  outputStart, 0, *segment);
     }
-    outputTrack->SetEnded();
-  }
-
-  void TrackUnionStream::UpdateWorkerStatus(WorkerPrivate* aWorker,
-                                            TrackID aTrackID,
-                                            bool aHasOnFlyEvent)
-  {
-    class Message final : public ControlMessage
-    {
-    public:
-      Message(TrackUnionStream* aStream,
-              WorkerPrivate* aWorker,
-              TrackID aTrackID,
-              bool aHasOnFlyEvent)
-        : ControlMessage(aStream)
-        , mWorker(aWorker)
-        , mTrackID(aTrackID)
-        , mHasOnFlyEvent(aHasOnFlyEvent)
-      {}
-      virtual void Run() override
-      {
-        mStream->AsTrackUnionStream()->UpdateWorkerStatusImpl(mWorker, mTrackID, mHasOnFlyEvent);
-      }
-      WorkerPrivate* mWorker;
-      TrackID mTrackID;
-      bool mHasOnFlyEvent;
-    };
-    // The MediaStream might be shutting down. If the Stream is destroyed, we
-    // don't need to append message to the graph.
-    if (this->IsDestroyed()) {
-      return;
-    }
-    GraphImpl()->AppendMessage(new Message(this, aWorker, aTrackID, aHasOnFlyEvent));
+    outputTrack->GetSegment()->AppendFrom(segment);
   }
-
-  // This runnable is dispatched from MSG thread, not from parent thread,
-  // main thread or worker thread. So the BusyCount balance is controlled
-  // by the runnable itself.
-  class VideoMonitorEventRunnable final : public WorkerRunnable {
-  public:
-    VideoMonitorEventRunnable(WorkerPrivate* aWorkerPrivate,
-                              TargetAndBusyBehavior aBehavior,
-                              TrackUnionStream* aStream,
-                              TrackID aTrackID,
-                              double aPlaybackTime,
-                              const nsString& aID,
-                              layers::Image* aImage)
-    :WorkerRunnable(aWorkerPrivate, aBehavior),
-     mStream(aStream),
-     mImage(aImage),
-     mTrackID(aTrackID),
-     mPlaybackTime(aPlaybackTime),
-     mID(aID)
-    {
-    }
-
-  private:
-    virtual bool
-    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-    {
-      aWorkerPrivate->AssertIsOnWorkerThread();
-      aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
-
-      // The callback is returned. Set the mOnFlyStatus flag to false which
-      // allowing following event flow.
-      class SetOnFlyEventStatusFalseRunnable : public nsRunnable
-      {
-      public:
-        SetOnFlyEventStatusFalseRunnable(TrackUnionStream* aStream,
-                                         WorkerPrivate* aWorker,
-                                         TrackID aTrackID)
-          : mStream(aStream)
-          , mWorker(aWorker)
-          , mTrackID(aTrackID)
-        {
-          MOZ_ASSERT(mStream);
-          MOZ_ASSERT(mWorker);
-        }
-
-        NS_IMETHOD
-        Run(void) override
-        {
-          // Set the mOnFlyStatus to false;
-          mStream->UpdateWorkerStatus(mWorker, mTrackID, false);
-          return NS_OK;
-        }
-        TrackUnionStream* mStream;
-        WorkerPrivate* mWorker;
-        TrackID mTrackID;
-      };
-      // We need to recover the onFlyEvent status whether the event is executed
-      // successfully or not.
-      nsRefPtr<SetOnFlyEventStatusFalseRunnable> runnable = new
-        SetOnFlyEventStatusFalseRunnable(mStream, aWorkerPrivate, mTrackID);
-      MOZ_ASSERT(runnable);
-
-      DOMEventTargetHelper* target = aWorkerPrivate->GlobalScope();
-      nsRefPtr<VideoMonitorEvent> event =
-        new VideoMonitorEvent(target, nullptr, nullptr);
-      nsresult rv = event->InitEvent(mPlaybackTime, mID, mImage);
-      if (NS_FAILED(rv)) {
-        xpc::Throw(aCx, rv);
-        // The ControlMessage is only allowed to send from main thread.
-        NS_DispatchToMainThread(runnable);
-        return false;
-      }
-      event->SetTrusted(true);
-
-      nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
+}
 
-      nsEventStatus dummy = nsEventStatus_eIgnore;
-      // Update the memory usage information to JS context before dispathing
-      // event.
-      if (aCx) {
-        int32_t width = mImage->GetSize().width;
-        int32_t height = mImage->GetSize().height;
-        // We don't know exactly format of mImage. So just set the maximum
-        // possible size.
-        JS_updateMallocCounter(aCx, width * height * 4);
-      }
-      rv = target->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
-      NS_WARN_IF(NS_FAILED(rv));
-
-      // The ControlMessage is only allowed to send from main thread.
-      rv = NS_DispatchToMainThread(runnable);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
+void TrackUnionStream::DispatchToVideoWorkerMonitor(TrackMapEntry* aMapEntry)
+{
+  if (aMapEntry->mWorkerMonitorInfos.IsEmpty()) {
+    return;
+  }
+  for (uint32_t i = 0; i < aMapEntry->mWorkerMonitorInfos.Length(); ++i) {
+    WorkerInformation& info = aMapEntry->mWorkerMonitorInfos[i];
+    if (!info.mOnFlyStatus) {
+      nsRefPtr<VideoMonitorEventRunnable> runnable =
+        new VideoMonitorEventRunnable(info.mWorker,
+                                      workers::WorkerRunnable::WorkerThreadUnchangedBusyCount,
+                                      this,
+                                      aMapEntry->mOutputTrackID,
+                                      StreamTimeToSeconds(aMapEntry->mLastImageStreamTime),
+                                      aMapEntry->mDOMTrackID,
+                                      aMapEntry->mLastImage);
+      if (NS_WARN_IF(!runnable->Dispatch(nullptr))) {
+        return;
       }
-      return true;
-    }
-
-    void
-    PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
-    {
-      aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false);
-    }
-
-    // Override |PreDispatch| and |PostDispatch| to avoid triggering
-    // |AssertIsOnParentThread| because of dispatching from MSG thread.
-    bool
-    PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-    {
-      return true;
-    }
-
-    void
-    PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-                 bool aDispatchResult) override
-    {
-    }
-
-    ~VideoMonitorEventRunnable(){
-    }
-
-    TrackUnionStream* mStream;
-    nsRefPtr<Image> mImage;
-    TrackID mTrackID;
-    double mPlaybackTime;
-    nsString mID;
-  };
-
-  void TrackUnionStream::CopyTrackData(StreamBuffer::Track* aInputTrack,
-                     uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo,
-                     bool* aOutputTrackFinished)
-  {
-    TrackMapEntry* map = &mTrackMap[aMapIndex];
-    StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID);
-    MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track");
-
-    MediaSegment* segment = map->mSegment;
-    MediaStream* source = map->mInputPort->GetSource();
-
-    GraphTime next;
-    *aOutputTrackFinished = false;
-    for (GraphTime t = aFrom; t < aTo; t = next) {
-      MediaInputPort::InputInterval interval = map->mInputPort->GetNextInputInterval(t);
-      interval.mEnd = std::min(interval.mEnd, aTo);
-      StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd);
-      StreamTime inputTrackEndPoint = STREAM_TIME_MAX;
-
-      if (aInputTrack->IsEnded() &&
-          aInputTrack->GetEnd() <= inputEnd) {
-        inputTrackEndPoint = aInputTrack->GetEnd();
-        *aOutputTrackFinished = true;
-      }
-
-      if (interval.mStart >= interval.mEnd) {
-        break;
-      }
-      StreamTime ticks = interval.mEnd - interval.mStart;
-      next = interval.mEnd;
-
-      StreamTime outputStart = outputTrack->GetEnd();
-
-      if (interval.mInputIsBlocked) {
-        // Maybe the input track ended?
-        segment->AppendNullData(ticks);
-        STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p appending %lld ticks of null data to track %d",
-                   this, (long long)ticks, outputTrack->GetID()));
-      } else if (InMutedCycle()) {
-        segment->AppendNullData(ticks);
-      } else {
-        if (GraphImpl()->StreamSuspended(source)) {
-          segment->AppendNullData(aTo - aFrom);
-        } else {
-          MOZ_ASSERT(outputTrack->GetEnd() == GraphTimeToStreamTime(interval.mStart),
-                     "Samples missing");
-          StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
-          if (segment->GetType() == MediaSegment::AUDIO){
-            segment->AppendSlice(*aInputTrack->GetSegment(),
-                                 std::min(inputTrackEndPoint, inputStart),
-                                 std::min(inputTrackEndPoint, inputEnd));
-          } else {
-            nsAutoPtr<MediaSegment> tmpSegment;
-            tmpSegment = aInputTrack->GetSegment()->CreateEmptyClone();
-            tmpSegment->AppendSlice(*aInputTrack->GetSegment(),
-                                    std::min(inputTrackEndPoint, inputStart),
-                                    std::min(inputTrackEndPoint, inputEnd));
-            VideoSegment* videoSegment = static_cast<VideoSegment*>(tmpSegment.get());
-            for (VideoSegment::ChunkIterator i(*videoSegment); !i.IsEnded(); i.Next()) {
-              VideoChunk& chunk = *i;
-              VideoFrame& frame = chunk.mFrame;
-              VideoFrame::Image* image = frame.GetImage();
-              if (image != map->mLastImage) {
-                map->mLastImage = image;
-                map->mLastImageStreamTime = std::min(inputTrackEndPoint, source->GraphTimeToStreamTime(interval.mStart));
-                DispatchToVideoWorkerMonitor(map);
-              }
-            }
-            segment->AppendFrom(videoSegment);
-          }
-        }
-      }
-      ApplyTrackDisabling(outputTrack->GetID(), segment);
-      for (uint32_t j = 0; j < mListeners.Length(); ++j) {
-        MediaStreamListener* l = mListeners[j];
-        l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
-                                    outputStart, 0, *segment);
-      }
-      outputTrack->GetSegment()->AppendFrom(segment);
+      info.mDispatchedImage = aMapEntry->mLastImage;
+      info.mOnFlyStatus = true;
     }
   }
-
-  void TrackUnionStream::DispatchToVideoWorkerMonitor(TrackMapEntry* aMapEntry)
-  {
-    if (aMapEntry->mWorkerMonitorInfos.IsEmpty()) {
-      return;
-    }
-    for (uint32_t i = 0; i < aMapEntry->mWorkerMonitorInfos.Length(); ++i) {
-      WorkerInformation& info = aMapEntry->mWorkerMonitorInfos[i];
-      if (!info.mOnFlyStatus) {
-        nsRefPtr<VideoMonitorEventRunnable> runnable =
-          new VideoMonitorEventRunnable(info.mWorker,
-                                        workers::WorkerRunnable::WorkerThreadUnchangedBusyCount,
-                                        this,
-                                        aMapEntry->mOutputTrackID,
-                                        StreamTimeToSeconds(aMapEntry->mLastImageStreamTime),
-                                        aMapEntry->mDOMTrackID,
-                                        aMapEntry->mLastImage);
-        if (NS_WARN_IF(!runnable->Dispatch(nullptr))) {
-          return;
-        }
-        info.mDispatchedImage = aMapEntry->mLastImage;
-        info.mOnFlyStatus = true;
-      }
-    }
-  }
+}
 } // namespace mozilla