Bug 1288329: [ogg] P1. Add support for metadata chaining in OggDemuxer. r=gerald,jwwang
☠☠ backed out by 3f66f98ebf11 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 28 Jul 2016 15:32:11 +1000
changeset 349521 a76b6e5597772de7c8cb9d1a68fee0477a907f0b
parent 349520 9fac9a76954f66b448ee8787b90ec1370ffe4cd3
child 349522 a1c4d9b9de328a0ebb68c9540f473b43bedbb22c
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [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,