Commit 7fac8df5 authored by Andreas Pehrson's avatar Andreas Pehrson
Browse files

Bug 1172394 - Merge MediaStream and MediaDecoder track sources. r=padenot

This reworks how media element captureStream works by removing the differences
between MediaStream and MediaDecoder capture. MediaDecoder capture will be
refactored so that ownership of MediaStreamTracks lies with the media element
instead of the OutputStreamManager. The internal MediaDecoder parts happen in a
later patch.

The new API for capturing a MediaDecoder involves a boolean on/off toggle, the
output tracks the decoder pipes data to, and the principal that data is tagged
with. If capturing is on but there are no output tracks, playback will not
happen, to ensure that no data gets accidentally skipped in the output tracks
while captured.

This also changes the logic for setting up MediaElementTrackSources in
HTMLMediaElement so it's triggered by the WatchManager and thus run in tail
dispatched runnables.

Differential Revision: https://phabricator.services.mozilla.com/D52040

--HG--
extra : moz-landing-system : lando
parent e324229a
Loading
Loading
Loading
Loading
+368 −258

File changed.

Preview size limit exceeded, changes collapsed.

+54 −33
Original line number Diff line number Diff line
@@ -113,6 +113,26 @@ class HTMLMediaElement : public nsGenericHTMLElement,
  typedef mozilla::MediaDecoderOwner MediaDecoderOwner;
  typedef mozilla::MetadataTags MetadataTags;

  // Helper struct to keep track of the MediaStreams returned by
  // mozCaptureStream(). For each OutputMediaStream, dom::MediaTracks get
  // captured into MediaStreamTracks which get added to
  // OutputMediaStream::mStream.
  struct OutputMediaStream {
    OutputMediaStream(RefPtr<DOMMediaStream> aStream, bool aCapturingAudioOnly,
                      bool aFinishWhenEnded);
    ~OutputMediaStream();

    RefPtr<DOMMediaStream> mStream;
    const bool mCapturingAudioOnly;
    const bool mFinishWhenEnded;
    // If mFinishWhenEnded is true, this is the URI of the first resource
    // mStream got tracks for, if not a MediaStream.
    nsCOMPtr<nsIURI> mFinishWhenEndedLoadingSrc;
    // If mFinishWhenEnded is true, this is the first MediaStream mStream got
    // tracks for, if not a resource.
    RefPtr<DOMMediaStream> mFinishWhenEndedAttrStream;
  };

  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(HTMLMediaElement)
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED

@@ -725,37 +745,17 @@ class HTMLMediaElement : public nsGenericHTMLElement,
  class AudioChannelAgentCallback;
  class ChannelLoader;
  class ErrorSink;
  class MediaElementTrackSource;
  class MediaLoadListener;
  class MediaStreamRenderer;
  class MediaStreamTrackListener;
  class FirstFrameListener;
  class ShutdownObserver;
  class StreamCaptureTrackSource;

  MediaDecoderOwner::NextFrameStatus NextFrameStatus();

  void SetDecoder(MediaDecoder* aDecoder);

  // Holds references to the DOM wrappers for the MediaStreams that we're
  // writing to.
  struct OutputMediaStream {
    OutputMediaStream();
    ~OutputMediaStream();

    RefPtr<DOMMediaStream> mStream;
    // Dummy stream to keep mGraph from shutting down when MediaDecoder shuts
    // down. Shared across all OutputMediaStreams as one stream is enough to
    // keep the graph alive.
    RefPtr<SharedDummyTrack> mGraphKeepAliveDummyStream;
    bool mFinishWhenEnded;
    bool mCapturingAudioOnly;
    bool mCapturingDecoder;
    bool mCapturingMediaStream;

    // The following members are keeping state for a captured MediaStream.
    nsTArray<Pair<nsString, RefPtr<MediaStreamTrackSource>>> mTracks;
  };

  void PlayInternal(bool aHandlingUserInput);

  /** Use this method to change the mReadyState member, so required
@@ -850,28 +850,35 @@ class HTMLMediaElement : public nsGenericHTMLElement,
   */
  void NotifyMediaStreamTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack);

  /**
   * Convenience method to get in a single list all enabled AudioTracks and, if
   * this is a video element, the selected VideoTrack.
   */
  void GetAllEnabledMediaTracks(nsTArray<RefPtr<MediaTrack>>& aTracks);

  /**
   * Enables or disables all tracks forwarded from mSrcStream to all
   * OutputMediaStreams. We do this for muting the tracks when pausing,
   * and unmuting when playing the media element again.
   *
   * If mSrcStream is unset, this does nothing.
   */
  void SetCapturedOutputStreamsEnabled(bool aEnabled);

  /**
   * Create a new MediaStreamTrack for aTrack and add it to the DOMMediaStream
   * in aOutputStream. This automatically sets the output track to enabled or
   * disabled depending on our current playing state.
   * Create a new MediaStreamTrack for the TrackSource corresponding to aTrack
   * and add it to the DOMMediaStream in aOutputStream. This automatically sets
   * the output track to enabled or disabled depending on our current playing
   * state.
   */
  void AddCaptureMediaTrackToOutputStream(dom::MediaTrack* aTrack,
                                          OutputMediaStream& aOutputStream,
                                          bool aAsyncAddtrack = true);
  enum class AddTrackMode { ASYNC, SYNC };
  void AddOutputTrackSourceToOutputStream(
      MediaElementTrackSource* aSource, OutputMediaStream& aOutputStream,
      AddTrackMode aMode = AddTrackMode::ASYNC);

  /**
   * Discard all output streams that are flagged to finish when playback ends.
   * Creates output track sources when this media element is captured, tracks
   * exist, playback is not ended and readyState is >= HAVE_METADATA.
   */
  void DiscardFinishWhenEndedOutputStreams();
  void UpdateOutputTrackSources();

  /**
   * Returns an DOMMediaStream containing the played contents of this
@@ -885,8 +892,8 @@ class HTMLMediaElement : public nsGenericHTMLElement,
   * reaching the stream. No video tracks will be captured in this case.
   */
  already_AddRefed<DOMMediaStream> CaptureStreamInternal(
      StreamCaptureBehavior aBehavior, StreamCaptureType aType,
      MediaTrackGraph* aGraph);
      StreamCaptureBehavior aFinishBehavior,
      StreamCaptureType aStreamCaptureType, MediaTrackGraph* aGraph);

  /**
   * Initialize a decoder as a clone of an existing decoder in another
@@ -1360,6 +1367,12 @@ class HTMLMediaElement : public nsGenericHTMLElement,
  // writing to.
  nsTArray<OutputMediaStream> mOutputStreams;

  // Mapping for output tracks, from dom::MediaTrack ids to the
  // MediaElementTrackSource that represents the source of all corresponding
  // MediaStreamTracks captured from this element.
  nsRefPtrHashtable<nsStringHashKey, MediaElementTrackSource>
      mOutputTrackSources;

  // Holds a reference to the first-frame-getting track listener attached to
  // mSelectedVideoStreamTrack.
  RefPtr<FirstFrameListener> mFirstFrameListener;
@@ -1561,6 +1574,14 @@ class HTMLMediaElement : public nsGenericHTMLElement,
  // True if currently casting this video
  bool mIsCasting = false;

  // Set while there are some OutputMediaStreams this media element's enabled
  // and selected tracks are captured into. When set, all tracks are captured
  // into the graph of this dummy track.
  // NB: This is a SharedDummyTrack to allow non-default graphs (AudioContexts
  // with an explicit sampleRate defined) to capture this element. When
  // cross-graph tracks are supported, this can become a bool.
  Watchable<RefPtr<SharedDummyTrack>> mTracksCaptured;

  // True if the sound is being captured.
  bool mAudioCaptured = false;

+15 −11
Original line number Diff line number Diff line
@@ -231,30 +231,34 @@ RefPtr<GenericPromise> MediaDecoder::SetSink(AudioDeviceInfo* aSink) {
  return GetStateMachine()->InvokeSetSink(aSink);
}

void MediaDecoder::AddOutputStream(DOMMediaStream* aStream,
                                   SharedDummyTrack* aDummyStream) {
void MediaDecoder::SetOutputCaptured(bool aCaptured) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
  AbstractThread::AutoEnter context(AbstractMainThread());
  mDecoderStateMachine->EnsureOutputStreamManager(aDummyStream);
  if (mInfo) {
    mDecoderStateMachine->EnsureOutputStreamManagerHasTracks(*mInfo);
  MOZ_CRASH("Not implemented");
}
  mDecoderStateMachine->AddOutputStream(aStream);

void MediaDecoder::AddOutputTrack(RefPtr<ProcessedMediaTrack> aTrack) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
  AbstractThread::AutoEnter context(AbstractMainThread());
  MOZ_CRASH("Not implemented");
}

void MediaDecoder::RemoveOutputStream(DOMMediaStream* aStream) {
void MediaDecoder::RemoveOutputTrack(
    const RefPtr<ProcessedMediaTrack>& aTrack) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
  AbstractThread::AutoEnter context(AbstractMainThread());
  mDecoderStateMachine->RemoveOutputStream(aStream);
  MOZ_CRASH("Not implemented");
}

void MediaDecoder::SetOutputStreamPrincipal(nsIPrincipal* aPrincipal) {
void MediaDecoder::SetOutputTracksPrincipal(
    const RefPtr<nsIPrincipal>& aPrincipal) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
  AbstractThread::AutoEnter context(AbstractMainThread());
  mDecoderStateMachine->SetOutputStreamPrincipal(aPrincipal);
  MOZ_CRASH("Not implemented");
}

double MediaDecoder::GetDuration() {
+18 −9
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ class MediaMemoryInfo;
class AbstractThread;
class DOMMediaStream;
class DecoderBenchmark;
class ProcessedMediaTrack;
class FrameStatistics;
class VideoFrameContainer;
class MediaFormatReader;
@@ -166,15 +167,23 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
  // replaying after the input as ended. In the latter case, the new source is
  // not connected to streams created by captureStreamUntilEnded.

  // Add an output stream. All decoder output will be sent to the stream.
  // The stream is initially blocked. The decoder is responsible for unblocking
  // it while it is playing back.
  void AddOutputStream(DOMMediaStream* aStream, SharedDummyTrack* aDummyStream);
  // Remove an output stream added with AddOutputStream.
  void RemoveOutputStream(DOMMediaStream* aStream);

  // Update the principal for any output streams and their tracks.
  void SetOutputStreamPrincipal(nsIPrincipal* aPrincipal);
  // Turn output capturing of this decoder on or off. If it is on, the
  // MediaDecoderStateMachine will only create a MediaSink after output tracks
  // have been set. This is to ensure that it doesn't create a regular MediaSink
  // while the owner has intended to capture the full output, thus missing to
  // capture some of it. The owner of the MediaDecoder is responsible for adding
  // output tracks while the output is captured.
  void SetOutputCaptured(bool aCaptured);
  // Add an output track. All decoder output for the track's media type will be
  // sent to the track.
  // Note that only one audio track and one video track is supported by
  // MediaDecoder at this time. Passing in more of one type, or passing in a
  // type that metadata says we are not decoding, is an error.
  void AddOutputTrack(RefPtr<ProcessedMediaTrack> aTrack);
  // Remove an output track added with AddOutputTrack.
  void RemoveOutputTrack(const RefPtr<ProcessedMediaTrack>& aTrack);
  // Update the principal for any output tracks.
  void SetOutputTracksPrincipal(const RefPtr<nsIPrincipal>& aPrincipal);

  // Return the duration of the video in seconds.
  virtual double GetDuration();
+1 −1
Original line number Diff line number Diff line
@@ -308,7 +308,7 @@ class MediaStreamTrackSource : public nsISupports {
  }

  // Principal identifying who may access the contents of this source.
  nsCOMPtr<nsIPrincipal> mPrincipal;
  RefPtr<nsIPrincipal> mPrincipal;

  // Currently registered sinks.
  nsTArray<WeakPtr<Sink>> mSinks;
Loading