Bug 1288329: [ogg] P1. Add support for metadata chaining in OggDemuxer. r=gerald,jwwang
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 28 Jul 2016 15:32:11 +1000
changeset 347502 725b6c6ff7f6eb46b19efc2136e29f2b329ba294
parent 347501 786521deead2bb8607d806ed77562baf4783b318
child 347503 b1f32a906af2f3aaaf653d804f83ddf0f5884e31
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald, jwwang
bugs1288329
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1288329: [ogg] P1. Add support for metadata chaining in OggDemuxer. r=gerald,jwwang This is not the cleanest approach, but ensures identical behavior with the OggReader when it comes to firing loadedmetadata event and handling the change of seekability. A more universal solution could be considered involving the MediaFormatReader and changing the MediaDataDemuxer API, of interest would be adding support for a new event fired whenever we have a change of content or metadata (useful with MSE or recorded webm of a WebRTC session MozReview-Commit-ID: BojB2r1CtA3
dom/media/MediaDecoderReader.h
dom/media/ogg/OggDecoder.cpp
dom/media/ogg/OggDemuxer.cpp
dom/media/ogg/OggDemuxer.h
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -286,16 +286,26 @@ public:
   TimedMetadataEventSource& TimedMetadataEvent()
   {
     return mTimedMetadataEvent;
   }
 
   // Notified by the OggReader during playback when chained ogg is detected.
   MediaEventSource<void>& OnMediaNotSeekable() { return mOnMediaNotSeekable; }
 
+  TimedMetadataEventProducer& TimedMetadataProducer()
+  {
+    return mTimedMetadataEvent;
+  }
+
+  MediaEventProducer<void>& MediaNotSeekableProducer()
+  {
+    return mOnMediaNotSeekable;
+  }
+
   bool IsSuspended() const
   {
     MOZ_ASSERT(OnTaskQueue());
     return mIsSuspended;
   }
 
   void SetIsSuspended(bool aState)
   {
--- a/dom/media/ogg/OggDecoder.cpp
+++ b/dom/media/ogg/OggDecoder.cpp
@@ -12,15 +12,21 @@
 #include "OggDecoder.h"
 
 namespace mozilla {
 
 MediaDecoderStateMachine* OggDecoder::CreateStateMachine()
 {
   bool useFormatDecoder =
     Preferences::GetBool("media.format-reader.ogg", true);
-  RefPtr<MediaDecoderReader> reader = useFormatDecoder ?
-      static_cast<MediaDecoderReader*>(new MediaFormatReader(this, new OggDemuxer(GetResource()), GetVideoFrameContainer())) :
-      new OggReader(this);
+  RefPtr<OggDemuxer> demuxer =
+    useFormatDecoder ? new OggDemuxer(GetResource()) : nullptr;
+  RefPtr<MediaDecoderReader> reader = useFormatDecoder
+    ? static_cast<MediaDecoderReader*>(new MediaFormatReader(this, demuxer, GetVideoFrameContainer()))
+    : new OggReader(this);
+  if (useFormatDecoder) {
+    demuxer->SetChainingEvents(&reader->TimedMetadataProducer(),
+                               &reader->MediaNotSeekableProducer());
+  }
   return new MediaDecoderStateMachine(this, reader);
 }
 
 } // namespace mozilla
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -128,17 +128,18 @@ OggDemuxer::OggDemuxer(MediaResource* aR
   , mSkeletonState(nullptr)
   , mAudioOggState(aResource)
   , mVideoOggState(aResource)
   , mVorbisSerial(0)
   , mOpusSerial(0)
   , mTheoraSerial(0)
   , mOpusPreSkip(0)
   , mIsChained(false)
-  , mDecodedAudioFrames(0)
+  , mTimedMetadataEvent(nullptr)
+  , mOnSeekableEvent(nullptr)
 {
   MOZ_COUNT_CTOR(OggDemuxer);
   PodZero(&mTheoraInfo);
 }
 
 OggDemuxer::~OggDemuxer()
 {
   MOZ_COUNT_DTOR(OggDemuxer);
@@ -151,16 +152,25 @@ OggDemuxer::~OggDemuxer()
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
       OGG_DEBUG("Reporting telemetry MEDIA_OGG_LOADED_IS_CHAINED=%d", isChained);
       Telemetry::Accumulate(Telemetry::ID::MEDIA_OGG_LOADED_IS_CHAINED, isChained);
     });
     AbstractThread::MainThread()->Dispatch(task.forget());
   }
 }
 
+void
+OggDemuxer::SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
+                              MediaEventProducer<void>* aOnSeekableEvent)
+{
+  mTimedMetadataEvent = aMetadataEvent;
+  mOnSeekableEvent = aOnSeekableEvent;
+}
+
+
 bool
 OggDemuxer::HasAudio()
 const
 {
   return mVorbisState || mOpusState;
 }
 
 bool
@@ -710,22 +720,23 @@ OggDemuxer::ReadMetadata()
 void
 OggDemuxer::SetChained() {
   {
     if (mIsChained) {
       return;
     }
     mIsChained = true;
   }
-  // @FIXME how can MediaDataDemuxer / MediaTrackDemuxer notify this has changed?
-  //mOnMediaNotSeekable.Notify();
+  if (mOnSeekableEvent) {
+    mOnSeekableEvent->Notify();
+  }
 }
 
 bool
-OggDemuxer::ReadOggChain()
+OggDemuxer::ReadOggChain(const media::TimeUnit& aLastEndTime)
 {
   bool chained = false;
   OpusState* newOpusState = nullptr;
   VorbisState* newVorbisState = nullptr;
   nsAutoPtr<MetadataTags> tags;
 
   if (HasVideo() || HasSkeleton() || !HasAudio()) {
     return false;
@@ -809,25 +820,24 @@ OggDemuxer::ReadOggChain()
     mInfo.mAudio.mChannels = newOpusState->mChannels;
 
     chained = true;
     tags = newOpusState->GetTags();
   }
 
   if (chained) {
     SetChained();
+    mInfo.mMediaSeekable = false;
+    mDecodedAudioDuration += aLastEndTime;
+    if (mTimedMetadataEvent)
     {
-      // @FIXME notify this!
-      /*
-      auto t = mDecodedAudioFrames * USECS_PER_S / mInfo.mAudio.mRate;
-      mTimedMetadataEvent.Notify(
-        TimedMetadata(TimeUnit::FromMicroseconds(t),
+      mTimedMetadataEvent->Notify(
+        TimedMetadata(mDecodedAudioDuration,
                       Move(tags),
                       nsAutoPtr<MediaInfo>(new MediaInfo(mInfo))));
-      */
     }
     return true;
   }
 
   return false;
 }
 
 ogg_sync_state*
@@ -1419,17 +1429,17 @@ OggTrackDemuxer::NextSample()
   }
   // Check the eos state in case we need to look for chained streams.
   bool eos = packet->e_o_s;
   OggCodecState* state = mParent->GetTrackCodecState(mType);
   RefPtr<MediaRawData> data = state->PacketOutAsMediaRawData();;
   if (eos) {
     // We've encountered an end of bitstream packet; check for a chained
     // bitstream following this one.
-    mParent->ReadOggChain();
+    mParent->ReadOggChain(TimeUnit::FromMicroseconds(data->GetEndTime()));
   }
   return data;
 }
 
 RefPtr<OggTrackDemuxer::SamplesPromise>
 OggTrackDemuxer::GetSamples(int32_t aNumSamples)
 {
   RefPtr<SamplesHolder> samples = new SamplesHolder;
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(OggDemuxer_h_)
 #define OggDemuxer_h_
 
 #include "nsTArray.h"
 #include "MediaDataDemuxer.h"
 #include "OggCodecState.h"
 #include "OggCodecStore.h"
+#include "MediaMetadataManager.h"
 
 namespace mozilla {
 
 class OggTrackDemuxer;
 class OggHeaders;
 
 class OggDemuxer : public MediaDataDemuxer
 {
@@ -29,16 +30,19 @@ public:
 
   already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(TrackInfo::TrackType aType,
                                                       uint32_t aTrackNumber) override;
 
   bool IsSeekable() const override;
 
   UniquePtr<EncryptionInfo> GetCrypto() override;
 
+  // Set the events to notify when chaining is encountered.
+  void SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
+                         MediaEventProducer<void>* aOnSeekableEvent);
 
 private:
 
   // helpers for friend OggTrackDemuxer
   UniquePtr<TrackInfo> GetTrackInfo(TrackInfo::TrackType aType, size_t aTrackNumber) const;
 
   struct nsAutoOggSyncState {
     nsAutoOggSyncState() {
@@ -194,17 +198,17 @@ private:
 
   // Reads and decodes header packets for aState, until either header decode
   // fails, or is complete. Initializes the codec state before returning.
   // Returns true if reading headers and initializtion of the stream
   // succeeds.
   bool ReadHeaders(TrackInfo::TrackType aType, OggCodecState* aState, OggHeaders& aHeaders);
 
   // Reads the next link in the chain.
-  bool ReadOggChain();
+  bool ReadOggChain(const media::TimeUnit& aLastEndTime);
 
   // Set this media as being a chain and notifies the state machine that the
   // media is no longer seekable.
   void SetChained();
 
   // Fills aTracks with the serial numbers of each active stream, for use by
   // various SkeletonState functions.
   void BuildSerialList(nsTArray<uint32_t>& aTracks);
@@ -311,18 +315,22 @@ private:
 
   // The picture region inside Theora frame to be displayed, if we have
   // a Theora video track.
   nsIntRect mPicture;
 
   // True if we are decoding a chained ogg.
   bool mIsChained;
 
-  // Number of audio frames decoded so far.
-  int64_t mDecodedAudioFrames;
+  // Total audio duration played so far.
+  media::TimeUnit mDecodedAudioDuration;
+
+  // Events manager
+  TimedMetadataEventProducer* mTimedMetadataEvent;
+  MediaEventProducer<void>* mOnSeekableEvent;
 
   friend class OggTrackDemuxer;
 };
 
 class OggTrackDemuxer : public MediaTrackDemuxer
 {
 public:
   OggTrackDemuxer(OggDemuxer* aParent,