Bug 654787 - part5: Add the looping-offset time to audio data; r=jwwang draft
authorChun-Min Chang <chun.m.chang@gmail.com>
Fri, 10 Nov 2017 13:50:38 +0800
changeset 702945 dd1ef89167825da69369a1c9c8d90241c00304aa
parent 702944 844fe51cb6add26233c19cd2bb8f0954b177f154
child 702946 c077338eb810dad0bcbe27db2b6663bd0b05fac2
push id90643
push userbmo:cchang@mozilla.com
push dateFri, 24 Nov 2017 02:29:17 +0000
reviewersjwwang
bugs654787
milestone59.0a1
Bug 654787 - part5: Add the looping-offset time to audio data; r=jwwang MozReview-Commit-ID: LUNF9x6foEA
dom/media/ReaderProxy.cpp
dom/media/ReaderProxy.h
--- a/dom/media/ReaderProxy.cpp
+++ b/dom/media/ReaderProxy.cpp
@@ -53,39 +53,47 @@ ReaderProxy::ReadMetadata()
            &ReaderProxy::OnMetadataNotRead);
 }
 
 RefPtr<ReaderProxy::AudioDataPromise>
 ReaderProxy::OnAudioDataRequestCompleted(RefPtr<AudioData> aAudio)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 
-  int64_t startTime = StartTime().ToMicroseconds();
-  aAudio->AdjustForStartTime(startTime);
+  // Subtract the start time and add the looping-offset time.
+  int64_t offset =
+    StartTime().ToMicroseconds() - mLoopingOffset.ToMicroseconds();
+  aAudio->AdjustForStartTime(offset);
+  mLastAudioEndTime = aAudio->mTime;
   return AudioDataPromise::CreateAndResolve(aAudio.forget(), __func__);
 }
 
 RefPtr<ReaderProxy::AudioDataPromise>
 ReaderProxy::OnAudioDataRequestFailed(const MediaResult& aError)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 
   if (mSeamlessLoopingBlocked || !mSeamlessLoopingEnabled ||
       aError.Code() != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
     return AudioDataPromise::CreateAndReject(aError, __func__);
   }
 
+  // The data time in the audio queue is assumed to be increased linearly,
+  // so we need to add the last ending time as the offset to correct the
+  // audio data time in the next round when seamless looping is enabled.
+  mLoopingOffset = mLastAudioEndTime;
+
   // For seamless looping, the demuxer is sought to the beginning and then
   // keep requesting decoded data in advance, upon receiving EOS.
   // The MDSM will not be aware of the EOS and keep receiving decoded data
   // as usual while looping is on.
   RefPtr<ReaderProxy> self = this;
   RefPtr<MediaFormatReader> reader = mReader;
   ResetDecode(TrackInfo::kAudioTrack);
-  return Seek(SeekTarget(media::TimeUnit::Zero(), SeekTarget::Accurate))
+  return SeekInternal(SeekTarget(media::TimeUnit::Zero(), SeekTarget::Accurate))
     ->Then(mReader->OwnerThread(),
            __func__,
            [reader]() { return reader->RequestAudioData(); },
            [](const SeekRejectValue& aReject) {
              return AudioDataPromise::CreateAndReject(aReject.mError, __func__);
            })
     ->Then(mOwnerThread,
            __func__,
@@ -144,16 +152,26 @@ ReaderProxy::RequestVideoData(const medi
            });
 }
 
 RefPtr<ReaderProxy::SeekPromise>
 ReaderProxy::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   mSeamlessLoopingBlocked = true;
+  // Reset the members for seamless looping if the seek is triggered outside.
+  mLoopingOffset = media::TimeUnit::Zero();
+  mLastAudioEndTime = media::TimeUnit::Zero();
+  return SeekInternal(aTarget);
+}
+
+RefPtr<ReaderProxy::SeekPromise>
+ReaderProxy::SeekInternal(const SeekTarget& aTarget)
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   SeekTarget adjustedTarget = aTarget;
   adjustedTarget.SetTime(adjustedTarget.GetTime() + StartTime());
   return InvokeAsync(mReader->OwnerThread(),
                      mReader.get(),
                      __func__,
                      &MediaFormatReader::Seek,
                      Move(adjustedTarget));
 }
--- a/dom/media/ReaderProxy.h
+++ b/dom/media/ReaderProxy.h
@@ -86,16 +86,17 @@ public:
 
   void SetSeamlessLoopingEnabled(bool aEnabled);
 
 private:
   ~ReaderProxy();
   RefPtr<MetadataPromise> OnMetadataRead(MetadataHolder&& aMetadata);
   RefPtr<MetadataPromise> OnMetadataNotRead(const MediaResult& aError);
   void UpdateDuration();
+  RefPtr<SeekPromise> SeekInternal(const SeekTarget& aTarget);
 
   RefPtr<ReaderProxy::AudioDataPromise> OnAudioDataRequestCompleted(
     RefPtr<AudioData> aAudio);
   RefPtr<ReaderProxy::AudioDataPromise> OnAudioDataRequestFailed(
     const MediaResult& aError);
 
   const RefPtr<AbstractThread> mOwnerThread;
   const RefPtr<MediaFormatReader> mReader;
@@ -104,16 +105,21 @@ private:
   Maybe<media::TimeUnit> mStartTime;
 
   // State-watching manager.
   WatchManager<ReaderProxy> mWatchManager;
 
   // Duration, mirrored from the state machine task queue.
   Mirror<media::NullableTimeUnit> mDuration;
 
+  // The total duration of audio looping in previous rounds.
+  media::TimeUnit mLoopingOffset = media::TimeUnit::Zero();
+  // To keep tracking the latest time of decoded audio data.
+  media::TimeUnit mLastAudioEndTime = media::TimeUnit::Zero();
+
   // To prevent seamless looping while seeking.
   bool mSeamlessLoopingBlocked;
   // Indicates whether we should loop the media.
   bool mSeamlessLoopingEnabled;
 };
 
 } // namespace mozilla