Bug 1309516 part 1 - retrieve start time before resolving the metadata promise; r?jya draft
authorKaku Kuo <kaku@mozilla.com>
Fri, 14 Oct 2016 20:01:28 +0800
changeset 428000 57cbc9dc8e72cec6fe22574ee7f89013b9ea235c
parent 425227 1391a2889aeb2bdd61ad6ef838e65826e35aabc2
child 428001 6af01ba877814a43b68bc004b0e28160216e7367
push id33202
push userbmo:kaku@mozilla.com
push dateFri, 21 Oct 2016 11:11:30 +0000
reviewersjya
bugs1309516
milestone52.0a1
Bug 1309516 part 1 - retrieve start time before resolving the metadata promise; r?jya MozReview-Commit-ID: KTC36LkbrwJ
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/MediaInfo.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -347,20 +347,63 @@ MediaFormatReader::OnDemuxerInitDone(nsr
   mInfo.mMediaSeekableOnlyInBufferedRanges =
     mDemuxer->IsSeekableOnlyInBufferedRanges();
 
   if (!videoActive && !audioActive) {
     mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
     return;
   }
 
+  mTags = Move(tags);
   mInitDone = true;
+
+  // Try to get the start time.
+  // For MSE case, the start time of each track is assumed to be 0.
+  // For others, we must demux the first sample to know the start time for each
+  // track.
+  if (ForceZeroStartTime()) {
+    mAudio.mFirstDemuxedSampleTime.emplace(TimeUnit::FromMicroseconds(0));
+    mVideo.mFirstDemuxedSampleTime.emplace(TimeUnit::FromMicroseconds(0));
+  } else {
+    if (HasAudio()) {
+      RequestDemuxSamples(TrackInfo::kAudioTrack);
+    } else {
+      mAudio.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity());
+    }
+
+    if (HasVideo()) {
+      RequestDemuxSamples(TrackInfo::kVideoTrack);
+    } else {
+      mVideo.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity());
+    }
+  }
+
+  MaybeResolveMetadataPromise();
+}
+
+void
+MediaFormatReader::MaybeResolveMetadataPromise()
+{
+  MOZ_ASSERT(OnTaskQueue());
+
+  if (mAudio.mFirstDemuxedSampleTime.isNothing() ||
+      mVideo.mFirstDemuxedSampleTime.isNothing()) {
+    return;
+  }
+
+  TimeUnit startTime = std::min(mAudio.mFirstDemuxedSampleTime.ref(),
+                                mVideo.mFirstDemuxedSampleTime.ref());
+  if (startTime.IsInfinite()) {
+    startTime = TimeUnit::FromMicroseconds(0);
+  }
+
+  mInfo.mStartTime = startTime;
   RefPtr<MetadataHolder> metadata = new MetadataHolder();
   metadata->mInfo = mInfo;
-  metadata->mTags = tags->Count() ? tags.release() : nullptr;
+  metadata->mTags = mTags->Count() ? mTags.release() : nullptr;
   mMetadataPromise.Resolve(metadata, __func__);
 }
 
 bool
 MediaFormatReader::IsEncrypted() const
 {
   return (HasAudio() && mInfo.mAudio.mCrypto.mValid) ||
          (HasVideo() && mInfo.mVideo.mCrypto.mValid);
@@ -562,16 +605,22 @@ MediaFormatReader::RequestVideoData(bool
 void
 MediaFormatReader::OnDemuxFailed(TrackType aTrack, const MediaResult& aError)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Failed to demux %s, failure:%u",
       aTrack == TrackType::kVideoTrack ? "video" : "audio", aError.Code());
   auto& decoder = GetDecoderData(aTrack);
   decoder.mDemuxRequest.Complete();
+
+  if (decoder.mFirstDemuxedSampleTime.isNothing()) {
+    decoder.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity());
+    MaybeResolveMetadataPromise();
+  }
+
   switch (aError.Code()) {
     case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
       if (!decoder.mWaitingForData) {
         decoder.mNeedDraining = true;
       }
       NotifyEndOfStream(aTrack);
       break;
     case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
@@ -740,16 +789,21 @@ MediaFormatReader::NotifyEndOfStream(Tra
   auto& decoder = GetDecoderData(aTrack);
   decoder.mDemuxEOS = true;
   ScheduleUpdate(aTrack);
 }
 
 bool
 MediaFormatReader::NeedInput(DecoderData& aDecoder)
 {
+  // Demux the first sample to get the start time of the treack.
+  if (aDecoder.mFirstDemuxedSampleTime.isNothing()) {
+    return true;
+  }
+
   // To account for H.264 streams which may require a longer
   // run of input than we input, decoders fire an "input exhausted" callback.
   // The decoder will not be fed a new raw sample until InputExhausted
   // has been called.
   return
     (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) &&
     !aDecoder.HasPendingDrain() &&
     !aDecoder.HasFatalError() &&
@@ -908,46 +962,48 @@ MediaFormatReader::DecodeDemuxedSamples(
 }
 
 void
 MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
                                         AbstractMediaDecoder::AutoNotifyDecoded& aA)
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  auto& decoder = GetDecoderData(aTrack);
+
+  if (decoder.mQueuedSamples.IsEmpty()) {
+    return;
+  }
+
+  if (decoder.mFirstDemuxedSampleTime.isNothing()) {
+    decoder.mFirstDemuxedSampleTime.emplace(
+      media::TimeUnit::FromMicroseconds(decoder.mQueuedSamples[0]->mTime));
+    MaybeResolveMetadataPromise();
+    return; // Kaku: should we return here?
+  }
+
   // Don't try to create or initialize decoders
   // (which might allocate hardware resources) when suspended.
   if (IsSuspended()) {
     // Should've deleted decoders when suspended.
     MOZ_DIAGNOSTIC_ASSERT(!mAudio.mDecoder && !mVideo.mDecoder);
     return;
   }
 
-  auto& decoder = GetDecoderData(aTrack);
-
-  if (decoder.mQueuedSamples.IsEmpty()) {
-    return;
-  }
-
   MediaResult rv = EnsureDecoderCreated(aTrack);
   if (NS_FAILED(rv)) {
     NS_WARNING("Error constructing decoders");
     NotifyError(aTrack, rv);
     return;
   }
 
   if (!EnsureDecoderInitialized(aTrack)) {
     return;
   }
 
-  if (!ForceZeroStartTime() && decoder.mFirstDemuxedSampleTime.isNothing()) {
-    decoder.mFirstDemuxedSampleTime.emplace(
-      media::TimeUnit::FromMicroseconds(decoder.mQueuedSamples[0]->mTime));
-  }
-
   LOGV("Giving %s input to decoder", TrackTypeToStr(aTrack));
 
   // Decode all our demuxed frames.
   bool samplesPending = false;
   while (decoder.mQueuedSamples.Length()) {
     RefPtr<MediaRawData> sample = decoder.mQueuedSamples[0];
     RefPtr<SharedTrackInfo> info = sample->mTrackInfo;
 
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -564,13 +564,17 @@ private:
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
   RefPtr<CDMProxy> mCDMProxy;
 
   RefPtr<GMPCrashHelper> mCrashHelper;
 
   void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
+
+  void MaybeResolveMetadataPromise();
+
+  UniquePtr<MetadataTags> mTags;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -498,16 +498,20 @@ public:
 
   // True if the media is seekable (i.e. supports random access).
   bool mMediaSeekable = true;
 
   // True if the media is only seekable within its buffered ranges.
   bool mMediaSeekableOnlyInBufferedRanges = false;
 
   EncryptionInfo mCrypto;
+
+  // The minimum of start times of audio and video tracks.
+  // Used to map the zero time on the media timeline to the first frame.
+  media::TimeUnit mStartTime;
 };
 
 class SharedTrackInfo {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedTrackInfo)
 public:
   SharedTrackInfo(const TrackInfo& aOriginal, uint32_t aStreamID)
     : mInfo(aOriginal.Clone())
     , mStreamSourceID(aStreamID)