Bug 1108950 part.3 - Add WorkerMonitor related functions into MediaStreamTrack. sr?smaug, r?roc draft
authorctai <ctai@mozilla.com>
Tue, 01 Sep 2015 10:19:05 +0800
changeset 289366 0c32eaa61e28c3ffa1fede73aaf21a74d8c63f76
parent 289365 40083bf155d1b8f8897df9b1736f3b78af37bcbe
child 289367 a498cb75e5f2742d0a5d80674d45560c3219a8f5
push id4976
push userctai@mozilla.com
push dateTue, 01 Sep 2015 02:32:48 +0000
reviewerssmaug, roc
bugs1108950
milestone43.0a1
Bug 1108950 part.3 - Add WorkerMonitor related functions into MediaStreamTrack. sr?smaug, r?roc
dom/bindings/Bindings.conf
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/media/MediaStreamGraph.h
dom/media/MediaStreamTrack.h
dom/media/TrackUnionStream.cpp
dom/media/TrackUnionStream.h
dom/media/VideoProcessEvent.cpp
dom/media/VideoProcessEvent.h
dom/media/VideoStreamTrack.cpp
dom/media/VideoStreamTrack.h
dom/webidl/MediaStreamTrack.webidl
dom/webidl/VideoProcessEvent.webidl
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -716,17 +716,18 @@ DOMInterfaces = {
     'binaryNames': { 'stream': 'DOMStream' }
 },
 
 'MediaStreamList': {
     'headerFile': 'MediaStreamList.h',
 },
 
 'MediaStreamTrack': {
-    'concrete': False
+    'concrete': False,
+    'implicitJSContext': ['addVideoMonitor', 'removeVideoMonitor'],
 },
 
 'MediaRecorder': {
     'headerFile': 'MediaRecorder.h',
 },
 
 'MessagePort': {
     'nativeType': 'mozilla::dom::MessagePortBase',
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -10,20 +10,21 @@
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/LocalMediaStreamBinding.h"
 #include "mozilla/dom/AudioNode.h"
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
+#include "AudioStreamTrack.h"
+#include "Layers.h"
 #include "MediaStreamGraph.h"
-#include "AudioStreamTrack.h"
+#include "TrackUnionStream.h"
 #include "VideoStreamTrack.h"
-#include "Layers.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 const TrackID TRACK_VIDEO_PRIMARY = 1;
 
 class DOMMediaStream::StreamListener : public MediaStreamListener {
@@ -629,16 +630,36 @@ DOMMediaStream::NotifyMediaStreamTrackEn
 
   nsAutoString id;
   aTrack->GetId(id);
   for (uint32_t i = 0; i < mMediaTrackListListeners.Length(); ++i) {
     mMediaTrackListListeners[i].NotifyMediaTrackEnded(id);
   }
 }
 
+void
+DOMMediaStream::AddVideoMonitor(Promise* aPromise,
+                                dom::workers::WorkerPrivate* aWorker,
+                                TrackID aTrackID)
+{
+  TrackUnionStream* unionStream = mStream->AsTrackUnionStream();
+  MOZ_ASSERT(unionStream);
+  unionStream->AddVideoMonitor(aPromise, aWorker, aTrackID);
+}
+
+void
+DOMMediaStream::RemoveVideoMonitor(nsRefPtr<Promise> aPromise,
+                                   dom::workers::WorkerPrivate* aWorker,
+                                   TrackID aTrackID)
+{
+  TrackUnionStream* unionStream = mStream->AsTrackUnionStream();
+  MOZ_ASSERT(unionStream);
+  unionStream->RemoveVideoMonitor(Some(aPromise), aWorker, Some(aTrackID));
+}
+
 DOMLocalMediaStream::~DOMLocalMediaStream()
 {
   if (mStream) {
     // Make sure Listeners of this stream know it's going away
     Stop();
   }
 }
 
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -24,16 +24,22 @@
 #undef GetCurrentTime
 #endif
 // X11 has a #define for CurrentTime. Unbelievable :-(.
 // See dom/media/webaudio/AudioContext.h for more fun!
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
+BEGIN_WORKERS_NAMESPACE
+class WorkerPrivate;
+END_WORKERS_NAMESPACE
+
+USING_WORKERS_NAMESPACE
+
 namespace mozilla {
 
 class DOMHwMediaStream;
 class DOMLocalMediaStream;
 class MediaStream;
 class MediaEngineSource;
 class MediaStreamGraph;
 
@@ -43,16 +49,17 @@ class HTMLCanvasElement;
 class MediaStreamTrack;
 class AudioStreamTrack;
 class VideoStreamTrack;
 class AudioTrack;
 class VideoTrack;
 class AudioTrackList;
 class VideoTrackList;
 class MediaTrackListListener;
+class Promise;
 } // namespace dom
 
 namespace layers {
 class ImageContainer;
 class OverlayImage;
 } // namespace layers
 
 class MediaStreamDirectListener;
@@ -255,16 +262,23 @@ public:
    */
   void DisconnectTrackListListeners(const AudioTrackList* aAudioTrackList,
                                     const VideoTrackList* aVideoTrackList);
 
   virtual void NotifyMediaStreamTrackCreated(MediaStreamTrack* aTrack);
 
   virtual void NotifyMediaStreamTrackEnded(MediaStreamTrack* aTrack);
 
+  void AddVideoMonitor(dom::Promise* aPromise,
+                       dom::workers::WorkerPrivate* aWorker,
+                       TrackID aTrackID);
+  void RemoveVideoMonitor(nsRefPtr<dom::Promise> aPromise,
+                          dom::workers::WorkerPrivate* aWorker,
+                          TrackID aTrackID);
+
 protected:
   virtual ~DOMMediaStream();
 
   void Destroy();
   void InitSourceStream(nsIDOMWindow* aWindow,
                         MediaStreamGraph* aGraph = nullptr);
   void InitTrackUnionStream(nsIDOMWindow* aWindow,
                             MediaStreamGraph* aGraph = nullptr);
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -244,16 +244,17 @@ struct AudioNodeSizes
 class MediaStreamGraphImpl;
 class SourceMediaStream;
 class ProcessedMediaStream;
 class MediaInputPort;
 class AudioNodeEngine;
 class AudioNodeExternalInputStream;
 class AudioNodeStream;
 class CameraPreviewMediaStream;
+class TrackUnionStream;
 
 /**
  * A stream of synchronized audio and video data. All (not blocked) streams
  * progress at the same rate --- "real time". Streams cannot seek. The only
  * operation readers can perform on a stream is to read the next data.
  *
  * Consumers of a stream can be reading from it at different offsets, but that
  * should only happen due to the order in which consumers are being run.
@@ -427,16 +428,17 @@ public:
   friend class MediaStreamGraphImpl;
   friend class MediaInputPort;
   friend class AudioNodeExternalInputStream;
 
   virtual SourceMediaStream* AsSourceStream() { return nullptr; }
   virtual ProcessedMediaStream* AsProcessedStream() { return nullptr; }
   virtual AudioNodeStream* AsAudioNodeStream() { return nullptr; }
   virtual CameraPreviewMediaStream* AsCameraPreviewStream() { return nullptr; }
+  virtual TrackUnionStream* AsTrackUnionStream() { return nullptr; }
 
   // These Impl methods perform the core functionality of the control methods
   // above, on the media graph thread.
   /**
    * Stop all stream activity and disconnect it from all inputs and outputs.
    * This must be idempotent.
    */
   virtual void DestroyImpl();
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -2,27 +2,35 @@
 /* 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 MEDIASTREAMTRACK_H_
 #define MEDIASTREAMTRACK_H_
 
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/Promise.h"
 #include "nsID.h"
 #include "StreamBuffer.h"
 
+BEGIN_WORKERS_NAMESPACE
+class WorkerPrivate;
+END_WORKERS_NAMESPACE
+
+USING_WORKERS_NAMESPACE
+
 namespace mozilla {
 
 class DOMMediaStream;
 
 namespace dom {
 
 class AudioStreamTrack;
 class VideoStreamTrack;
+class Promise;
 
 /**
  * Class representing a track in a DOMMediaStream.
  */
 class MediaStreamTrack : public DOMEventTargetHelper {
 public:
   /**
    * aTrackID is the MediaStreamGraph track ID for the track in the
@@ -52,16 +60,35 @@ public:
 
   // Notifications from the MediaStreamGraph
   void NotifyEnded() { mEnded = true; }
 
   // Webrtc allows the remote side to name tracks whatever it wants, and we
   // need to surface this to content.
   void AssignId(const nsAString& aID) { mID = aID; }
 
+  // AddVideoMonitor and RemoveVideoMonitor will be overrode by the functions of
+  // VideoStreamTrack. So those functions should throw an exception when web
+  // developer call them in wrong MediaStreamTrack. For example, call those
+  // functions in the MediaStreamTrack with kind="audio".
+  virtual already_AddRefed<Promise> AddVideoMonitor(JSContext* aCx, WorkerPrivate& aWorker, ErrorResult& aRv) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+    nsRefPtr<Promise> promise;
+    promise = Promise::Create(GetOwnerGlobal(), aRv);
+    promise->MaybeReject(aRv);
+    return promise.forget();
+  }
+  virtual already_AddRefed<Promise> RemoveVideoMonitor(JSContext* aCx, WorkerPrivate& aWorker, ErrorResult& aRv) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
+    nsRefPtr<Promise> promise;
+    promise = Promise::Create(GetOwnerGlobal(), aRv);
+    promise->MaybeReject(aRv);
+    return promise.forget();
+  }
+
 protected:
   virtual ~MediaStreamTrack();
 
   nsRefPtr<DOMMediaStream> mStream;
   TrackID mTrackID;
   nsString mID;
   bool mEnded;
   bool mEnabled;
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #include "MediaStreamGraphImpl.h"
+#include "mozilla/dom/Promise.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
@@ -27,16 +28,19 @@
 #include "webaudio/MediaStreamAudioDestinationNode.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
 #endif
 
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 
 #ifdef STREAM_LOG
 #undef STREAM_LOG
@@ -153,16 +157,309 @@ TrackUnionStream::TrackUnionStream(DOMMe
     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
+    {
+    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);
+
+        // |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;
+          }
+        }
+      }
+    }
+  }
+
+  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));
+      }
+    }
+  }
+
+  // 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);
+
+      class RemoveWorkerInfoRunnable : public nsRunnable
+      {
+      public:
+        RemoveWorkerInfoRunnable(WorkerPrivate* aWorker, TrackUnionStream* aStream)
+          : mWorker(aWorker)
+          , mStream(aStream)
+        {}
+
+        NS_IMETHOD
+        Run(void) override
+        {
+          mStream->RemoveVideoMonitor(Nothing(), mWorker, Nothing(), false);
+          return NS_OK;
+        }
+        WorkerPrivate* mWorker;
+        TrackUnionStream* mStream;
+      };
+
+      nsRefPtr<RemoveWorkerInfoRunnable> runnable = new
+          RemoveWorkerInfoRunnable(mWorker, mStream);
+      NS_DispatchToMainThread(runnable);
+    }
+    return true;
+  }
+
   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);
--- a/dom/media/TrackUnionStream.h
+++ b/dom/media/TrackUnionStream.h
@@ -2,35 +2,99 @@
 /* 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_TRACKUNIONSTREAM_H_
 #define MOZILLA_TRACKUNIONSTREAM_H_
 
 #include "MediaStreamGraph.h"
+
 #include <algorithm>
+#include "mozilla/dom/workers/bindings/WorkerFeature.h"
+#include "mozilla/Maybe.h"
+
+BEGIN_WORKERS_NAMESPACE
+class WorkerPrivate;
+END_WORKERS_NAMESPACE
+
+USING_WORKERS_NAMESPACE
 
 namespace mozilla {
 
+namespace dom {
+  class Promise;
+}
+
 /**
  * See MediaStreamGraph::CreateTrackUnionStream.
  */
 class TrackUnionStream : public ProcessedMediaStream {
 public:
   explicit TrackUnionStream(DOMMediaStream* aWrapper);
 
   virtual void RemoveInput(MediaInputPort* aPort) override;
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
   // Forward SetTrackEnabled(output_track_id, enabled) to the Source MediaStream,
   // translating the output track ID into the correct ID in the source.
   virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) override;
 
+  virtual TrackUnionStream* AsTrackUnionStream() override { return this; };
+
+  void AddVideoMonitor(dom::Promise* aPromise, WorkerPrivate* aWorker, TrackID aTrackID);
+
+  // |RemoveVideoMonitor| can be called in |VideoWorkerFeature::Notify|. There
+  // is no Promise in such case. So use maybe to handle it.
+  void RemoveVideoMonitor(Maybe<nsRefPtr<dom::Promise>> aPromise, WorkerPrivate* aWorker, Maybe<TrackID> aTrackID, bool aNeedRemoveFeature = true);
+
 protected:
+  virtual ~TrackUnionStream();
+
+  struct WorkerInformation; // Forward declaration for VideoWorkerFeature.
+
+  class VideoWorkerFeature final : public WorkerFeature
+  {
+  public:
+    NS_INLINE_DECL_REFCOUNTING(VideoWorkerFeature)
+
+    VideoWorkerFeature(WorkerPrivate* aWorker,
+                       TrackUnionStream* aStream)
+      : mWorker(aWorker)
+      , mStream(aStream)
+    {}
+
+    // Remove the WorkerInformation of the worker in TrackUnionStream::mTrackMap
+    // to avoid dispatching event to a closed worker.
+    bool Notify(JSContext* aCx, Status aStatus) override;
+
+  private:
+    virtual ~VideoWorkerFeature() {}
+    friend class TrackUnionStream;
+    WorkerPrivate* mWorker;
+    TrackUnionStream* mStream;
+  };
+
+  // The worker tuple keep the information of each worker.
+  struct WorkerInformation {
+    bool operator==(const WorkerInformation& aOther) const {
+      return mWorker == aOther.mWorker;
+    }
+    bool operator!=(const WorkerInformation& aOther) const {
+      return !(*this == aOther);
+    }
+
+    WorkerPrivate* mWorker;
+    nsRefPtr<VideoWorkerFeature> mWorkerFeature;
+    // Memorized dispatched image. Because the complexity in each worker might be
+    // different, keep the latest dispatched image to determine whether dispatch
+    // the new event once the worker finished the job.
+    nsRefPtr<VideoFrame::Image> mDispatchedImage;
+  };
+
   // Only non-ended tracks are allowed to persist in this map.
   struct TrackMapEntry {
     // mEndOfConsumedInputTicks is the end of the input ticks that we've consumed.
     // 0 if we haven't consumed any yet.
     StreamTime mEndOfConsumedInputTicks;
     // mEndOfLastInputIntervalInInputStream is the timestamp for the end of the
     // previous interval which was unblocked for both the input and output
     // stream, in the input stream's timeline, or -1 if there wasn't one.
@@ -43,16 +107,22 @@ protected:
     // We keep track IDs instead of track pointers because
     // tracks can be removed without us being notified (e.g.
     // when a finished track is forgotten.) When we need a Track*,
     // we call StreamBuffer::FindTrack, which will return null if
     // the track has been deleted.
     TrackID mInputTrackID;
     TrackID mOutputTrackID;
     nsAutoPtr<MediaSegment> mSegment;
+    // TrackUnionStream will hold TrackMapEntrys. Each TrackMapEntry has
+    // multiple WorkerInformation. The WorkerInformation contains
+    // VideoWorkerFeature object. The WorkerInformation is added into
+    // mWorkerMonitorInfos when the AddWorkerMonitor is called. And it is
+    // removed by RemoveWorkerMonitor.
+    nsTArray<WorkerInformation> mWorkerMonitorInfos;
   };
 
   // Add the track to this stream, retaining its TrackID if it has never
   // been previously used in this stream, allocating a new TrackID otherwise.
   uint32_t AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack,
                     GraphTime aFrom);
   void EndTrack(uint32_t aIndex);
   void CopyTrackData(StreamBuffer::Track* aInputTrack,
@@ -62,13 +132,18 @@ protected:
   nsTArray<TrackMapEntry> mTrackMap;
 
   // The next available TrackID, starting at 1 and progressing upwards.
   // All TrackIDs in [1, mNextAvailableTrackID) have implicitly been used.
   TrackID mNextAvailableTrackID;
 
   // Sorted array of used TrackIDs that require manual tracking.
   nsTArray<TrackID> mUsedTracks;
+
+private:
+  // MSG thread only methods
+  void AddVideoMonitorImpl(WorkerPrivate* aWorker, TrackID aTrackID);
+  void RemoveVideoMonitorImpl(WorkerPrivate* aWorker, Maybe<TrackID> aTrackID, bool aNeedRemoveFeature);
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */
--- a/dom/media/VideoProcessEvent.cpp
+++ b/dom/media/VideoProcessEvent.cpp
@@ -39,16 +39,29 @@ VideoMonitorEvent::VideoMonitorEvent(Eve
 }
 
 JSObject*
 VideoMonitorEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VideoMonitorEventBinding::Wrap(aCx, this, aGivenProto);
 }
 
+already_AddRefed<VideoMonitorEvent>
+VideoMonitorEvent::Constructor(const GlobalObject& aGlobal,
+                               const nsAString& aTrackId,
+                               double aPlaybackTime,
+                               ImageBitmap& aInputImageBitmap,
+                               ErrorResult& aRv)
+{
+  nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
+  nsRefPtr<VideoMonitorEvent> videoMonitorEvent = new VideoMonitorEvent(target, nullptr, nullptr);
+  aRv = videoMonitorEvent->InitEvent(aPlaybackTime, aTrackId, aInputImageBitmap);
+  return videoMonitorEvent.forget();
+}
+
 double
 VideoMonitorEvent::PlaybackTime() const
 {
   return mPlaybackTime;
 }
 
 void
 VideoMonitorEvent::GetTrackId(nsString& aRetVal) const
@@ -58,31 +71,40 @@ VideoMonitorEvent::GetTrackId(nsString& 
 
 already_AddRefed<ImageBitmap>
 VideoMonitorEvent::InputImageBitmap() const
 {
   nsRefPtr<ImageBitmap> bitmap = mInputImageBitmap;
   return bitmap.forget();
 }
 
+
 NS_IMETHODIMP
 VideoMonitorEvent::InitEvent(double aPlaybackTime,
                              const nsAString& aTrackId,
-                             layers::Image* aImage)
+                             ImageBitmap& aInputImageBitmap)
 {
-  MOZ_ASSERT(aImage);
   nsresult rv = Event::InitEvent(NS_LITERAL_STRING("videoprocess"),
                                  false /* non-bubbling */,
                                  true /* cancelable */);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mPlaybackTime = aPlaybackTime;
   mTrackId = aTrackId;
-  ErrorResult er;
-  mInputImageBitmap = ImageBitmap::Create(mOwner, aImage);
-  MOZ_ASSERT(mInputImageBitmap);
+  mInputImageBitmap = &aInputImageBitmap;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+VideoMonitorEvent::InitEvent(double aPlaybackTime,
+                             const nsAString& aTrackId,
+                             layers::Image* aImage)
+{
+  MOZ_ASSERT(aImage);
+  nsRefPtr<ImageBitmap> imageBitmap = ImageBitmap::Create(mOwner, aImage);
+  MOZ_ASSERT(imageBitmap);
+  return InitEvent(aPlaybackTime, aTrackId, *imageBitmap);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/VideoProcessEvent.h
+++ b/dom/media/VideoProcessEvent.h
@@ -40,26 +40,37 @@ public:
                     WidgetEvent* aEvent);
 
 protected:
   ~VideoMonitorEvent() {}
 
 public:
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
+  static already_AddRefed<VideoMonitorEvent>
+  Constructor(const GlobalObject& aGlobal,
+              const nsAString& aTrackId,
+              double aPlaybackTime,
+              ImageBitmap& aInputImageBitmap,
+              ErrorResult& aRv);
+
   void GetTrackId(nsString& aRetVal) const;
 
   double PlaybackTime() const;
 
   already_AddRefed<ImageBitmap> InputImageBitmap() const;
 
   bool IsTrusted() const { return true; };
 
   nsresult InitEvent(double aPlaybackTime,
                      const nsAString& aTrackId,
+                     ImageBitmap& aInputImageBitmap);
+
+  nsresult InitEvent(double aPlaybackTime,
+                     const nsAString& aTrackId,
                      layers::Image* aImage);
 
 private:
   nsRefPtr<ImageBitmap> mInputImageBitmap;
   nsString mTrackId;
   double mPlaybackTime;
 };
 
--- a/dom/media/VideoStreamTrack.cpp
+++ b/dom/media/VideoStreamTrack.cpp
@@ -1,20 +1,52 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #include "VideoStreamTrack.h"
 
 #include "mozilla/dom/VideoStreamTrackBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 JSObject*
 VideoStreamTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VideoStreamTrackBinding::Wrap(aCx, this, aGivenProto);
 }
 
+already_AddRefed<Promise>
+VideoStreamTrack::AddVideoMonitor(JSContext* aCx, WorkerPrivate& aWorker, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()->GetParentObject());
+  MOZ_ASSERT(global);
+
+  nsRefPtr<Promise> promise;
+  promise = Promise::Create(global, aRv);
+  // We don't need JSContext when the aIncrese is true.
+  aWorker.ModifyBusyCount(nullptr, true);
+  mStream->AddVideoMonitor(promise, &aWorker, mTrackID);
+  return promise.forget();  // We don't need JSContext when the aIncrese is true.
+  aWorker.ModifyBusyCount(nullptr, true);
+  mStream->AddVideoMonitor(promise, &aWorker, mTrackID);
+  return promise.forget();
+ }
+
+already_AddRefed<Promise>
+VideoStreamTrack::RemoveVideoMonitor(JSContext* aCx, WorkerPrivate& aWorker, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()->GetParentObject());
+  MOZ_ASSERT(global);
+
+  nsRefPtr<Promise> promise;
+  promise = Promise::Create(global, aRv);
+  mStream->RemoveVideoMonitor(promise, &aWorker, mTrackID);
+  aWorker.ModifyBusyCount(aCx, false);
+  return promise.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/VideoStreamTrack.h
+++ b/dom/media/VideoStreamTrack.h
@@ -18,14 +18,17 @@ public:
     : MediaStreamTrack(aStream, aTrackID) {}
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual VideoStreamTrack* AsVideoStreamTrack() override { return this; }
 
   // WebIDL
   virtual void GetKind(nsAString& aKind) override { aKind.AssignLiteral("video"); }
+
+  virtual already_AddRefed<Promise> AddVideoMonitor(JSContext* aCx, WorkerPrivate& aWorker, ErrorResult& aRv) override;
+  virtual already_AddRefed<Promise> RemoveVideoMonitor(JSContext* aCx, WorkerPrivate& aWorker, ErrorResult& aRv) override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* VIDEOSTREAMTRACK_H_ */
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -30,8 +30,18 @@ interface MediaStreamTrack {
 //    void                   setConstraint (DOMString constraintName, any constraintValue, optional boolean mandatory = false);
 //    MediaTrackConstraints? constraints ();
 //    void                   applyConstraints (MediaTrackConstraints constraints);
 //    void                   prependConstraint (DOMString constraintName, any constraintValue);
 //    void                   appendConstraint (DOMString constraintName, any constraintValue);
 //                attribute EventHandler          onoverconstrained;
     void                   stop ();
 };
+
+// For FoxEye project.
+partial interface MediaStreamTrack {
+    // VideoMonitor is for the case of just do the video frames analysis without
+    // any modification.
+    [Pref="foxeye.enabled", Throws]
+    Promise<void> addVideoMonitor(Worker worker);
+    [Pref="foxeye.enabled", Throws]
+    Promise<void> removeVideoMonitor(Worker worker);
+};
--- a/dom/webidl/VideoProcessEvent.webidl
+++ b/dom/webidl/VideoProcessEvent.webidl
@@ -3,13 +3,14 @@
  * 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/.
  *
  * The origin of this IDL file is in below link.
  * http://chiahungtai.github.io/mediacapture-worker/
  *
  */
 
+[Constructor(DOMString trackId, double playbackTime, ImageBitmap inputImageBitmap) ]
 interface VideoMonitorEvent : Event {
   readonly attribute DOMString   trackId;
   readonly attribute double      playbackTime;
   readonly attribute ImageBitmap inputImageBitmap;
 };