Bug 1179110 - Use a Maybe<> to store start time, rather than using -1 as a sentinel. r=jya, a=ritu
authorBobby Holley <bobbyholley@gmail.com>
Wed, 01 Jul 2015 16:28:47 -0700
changeset 268768 4e6fead2c1f394ae4ced0d2163171689ade74b32
parent 268767 16b1e64fbed3b89cfefb044e7aa93fc3da26fc0d
child 268769 09219bc6153f5ad217f040d35678198e5b37d8eb
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, ritu
bugs1179110
milestone41.0a2
Bug 1179110 - Use a Maybe<> to store start time, rather than using -1 as a sentinel. r=jya, a=ritu This allows us to properly support videos with a negative start time, which is happening here.
dom/media/MediaDecoderReader.cpp
dom/media/MediaDecoderReader.h
dom/media/MediaFormatReader.cpp
dom/media/fmp4/MP4Reader.cpp
dom/media/ogg/OggReader.cpp
dom/media/webm/WebMReader.cpp
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -71,17 +71,16 @@ MediaDecoderReader::MediaDecoderReader(A
                                                        /* aSupportsTailDispatch = */ true))
   , mWatchManager(this, mTaskQueue)
   , mTimer(new MediaTimer())
   , mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
   , mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
   , mThrottleDuration(TimeDuration::FromMilliseconds(500))
   , mLastThrottledNotify(TimeStamp::Now() - mThrottleDuration)
   , mIgnoreAudioOutputFormat(false)
-  , mStartTime(-1)
   , mHitAudioDecodeError(false)
   , mShutdown(false)
   , mTaskQueueIsBorrowed(!!aBorrowedTaskQueue)
   , mAudioDiscontinuity(false)
   , mVideoDiscontinuity(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderReader);
   MOZ_ASSERT(NS_IsMainThread());
@@ -223,17 +222,17 @@ MediaDecoderReader::DoThrottledNotify()
   mThrottledInterval.reset();
   NotifyDataArrived(interval);
 }
 
 media::TimeIntervals
 MediaDecoderReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
-  NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
+  NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
   AutoPinned<MediaResource> stream(mDecoder->GetResource());
 
   if (!mDuration.ReadOnWrongThread().isSome()) {
     return TimeIntervals();
   }
 
   return GetEstimatedBufferedTimeRanges(stream, mDuration.ReadOnWrongThread().ref().ToMicroseconds());
 }
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -301,18 +301,18 @@ public:
 
   void DispatchSetStartTime(int64_t aStartTime)
   {
     nsRefPtr<MediaDecoderReader> self = this;
     nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableFunction([self, aStartTime] () -> void
     {
       MOZ_ASSERT(self->OnTaskQueue());
-      MOZ_ASSERT(self->mStartTime == -1);
-      self->mStartTime = aStartTime;
+      MOZ_ASSERT(!self->HaveStartTime());
+      self->mStartTime.emplace(aStartTime);
       self->UpdateBuffered();
     });
     TaskQueue()->Dispatch(r.forget());
   }
 
   MediaTaskQueue* TaskQueue() {
     return mTaskQueue;
   }
@@ -404,17 +404,19 @@ protected:
   // and then set to a value >= by MediaDecoderStateMachine::SetStartTime(),
   // after which point it never changes (though SetStartTime may be called
   // multiple times with the same value).
   //
   // This is an ugly breach of abstractions - it's currently necessary for the
   // readers to return the correct value of GetBuffered. We should refactor
   // things such that all GetBuffered calls go through the MDSM, which would
   // offset the range accordingly.
-  int64_t mStartTime;
+  Maybe<int64_t> mStartTime;
+  bool HaveStartTime() { MOZ_ASSERT(OnTaskQueue()); return mStartTime.isSome(); }
+  int64_t StartTime() { MOZ_ASSERT(HaveStartTime()); return mStartTime.ref(); }
 
   // This is a quick-and-dirty way for DecodeAudioData implementations to
   // communicate the presence of a decoding error to RequestAudioData. We should
   // replace this with a promise-y mechanism as we make this stuff properly
   // async.
   bool mHitAudioDecodeError;
   bool mShutdown;
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1404,18 +1404,18 @@ MediaFormatReader::GetBuffered()
   media::TimeIntervals intervals;
 
   if (!mInitDone) {
     return intervals;
   }
   int64_t startTime;
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
-    startTime = mStartTime;
+    NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
+    startTime = StartTime();
   }
   // Ensure we have up to date buffered time range.
   if (HasVideo()) {
     UpdateReceivedNewData(TrackType::kVideoTrack);
   }
   if (HasAudio()) {
     UpdateReceivedNewData(TrackType::kAudioTrack);
   }
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -1077,29 +1077,29 @@ MP4Reader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
   MonitorAutoLock mon(mDemuxerMonitor);
   media::TimeIntervals buffered;
   if (!mIndexReady) {
     return buffered;
   }
   UpdateIndex();
-  NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
+  NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> ranges;
   nsresult rv = resource->GetCachedRanges(ranges);
 
   if (NS_SUCCEEDED(rv)) {
     nsTArray<Interval<Microseconds>> timeRanges;
     mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
     for (size_t i = 0; i < timeRanges.Length(); i++) {
       buffered += media::TimeInterval(
-        media::TimeUnit::FromMicroseconds(timeRanges[i].start - mStartTime),
-        media::TimeUnit::FromMicroseconds(timeRanges[i].end - mStartTime));
+        media::TimeUnit::FromMicroseconds(timeRanges[i].start - StartTime()),
+        media::TimeUnit::FromMicroseconds(timeRanges[i].end - StartTime()));
     }
   }
 
   return buffered;
 }
 
 bool MP4Reader::IsDormantNeeded()
 {
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -1434,28 +1434,29 @@ OggReader::Seek(int64_t aTarget, int64_t
   } else {
     return SeekPromise::CreateAndResolve(aTarget, __func__);
   }
 }
 
 nsresult OggReader::SeekInternal(int64_t aTarget, int64_t aEndTime)
 {
   MOZ_ASSERT(OnTaskQueue());
+  NS_ENSURE_TRUE(HaveStartTime(), NS_ERROR_FAILURE);
   if (mIsChained)
     return NS_ERROR_FAILURE;
   LOG(LogLevel::Debug, ("%p About to seek to %lld", mDecoder, aTarget));
   nsresult res;
   MediaResource* resource = mDecoder->GetResource();
   NS_ENSURE_TRUE(resource != nullptr, NS_ERROR_FAILURE);
   int64_t adjustedTarget = aTarget;
   if (HasAudio() && mOpusState){
-    adjustedTarget = std::max(mStartTime, aTarget - SEEK_OPUS_PREROLL);
+    adjustedTarget = std::max(StartTime(), aTarget - SEEK_OPUS_PREROLL);
   }
 
-  if (adjustedTarget == mStartTime) {
+  if (adjustedTarget == StartTime()) {
     // We've seeked to the media start. Just seek to the offset of the first
     // content page.
     res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
 
     res = ResetDecode(true);
     NS_ENSURE_SUCCESS(res,res);
   } else {
@@ -1468,28 +1469,28 @@ nsresult OggReader::SeekInternal(int64_t
       // No index or other non-fatal index-related failure. Try to seek
       // using a bisection search. Determine the already downloaded data
       // in the media cache, so we can try to seek in the cached data first.
       nsAutoTArray<SeekRange, 16> ranges;
       res = GetSeekRanges(ranges);
       NS_ENSURE_SUCCESS(res,res);
 
       // Figure out if the seek target lies in a buffered range.
-      SeekRange r = SelectSeekRange(ranges, aTarget, mStartTime, aEndTime, true);
+      SeekRange r = SelectSeekRange(ranges, aTarget, StartTime(), aEndTime, true);
 
       if (!r.IsNull()) {
         // We know the buffered range in which the seek target lies, do a
         // bisection search in that buffered range.
-        res = SeekInBufferedRange(aTarget, adjustedTarget, mStartTime, aEndTime, ranges, r);
+        res = SeekInBufferedRange(aTarget, adjustedTarget, StartTime(), aEndTime, ranges, r);
         NS_ENSURE_SUCCESS(res,res);
       } else {
         // The target doesn't lie in a buffered range. Perform a bisection
         // search over the whole media, using the known buffered ranges to
         // reduce the search space.
-        res = SeekInUnbuffered(aTarget, mStartTime, aEndTime, ranges);
+        res = SeekInUnbuffered(aTarget, StartTime(), aEndTime, ranges);
         NS_ENSURE_SUCCESS(res,res);
       }
     }
   }
 
   if (HasVideo()) {
     // Decode forwards until we find the next keyframe. This is required,
     // as although the seek should finish on a page containing a keyframe,
@@ -1848,17 +1849,17 @@ nsresult OggReader::SeekBisection(int64_
   SEEK_LOG(LogLevel::Debug, ("Seek complete in %d bisections.", hops));
 
   return NS_OK;
 }
 
 media::TimeIntervals OggReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
-  NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
+  NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
   {
     mozilla::ReentrantMonitorAutoEnter mon(mMonitor);
     if (mIsChained) {
       return media::TimeIntervals::Invalid();
     }
   }
 #ifdef OGG_ESTIMATE_BUFFERED
   return MediaDecoderReader::GetBuffered();
@@ -1887,17 +1888,17 @@ media::TimeIntervals OggReader::GetBuffe
     // Ensure the offsets are after the header pages.
     int64_t startOffset = ranges[index].mStart;
     int64_t endOffset = ranges[index].mEnd;
 
     // Because the granulepos time is actually the end time of the page,
     // we special-case (startOffset == 0) so that the first
     // buffered range always appears to be buffered from the media start
     // time, rather than from the end-time of the first page.
-    int64_t startTime = (startOffset == 0) ? mStartTime : -1;
+    int64_t startTime = (startOffset == 0) ? StartTime() : -1;
 
     // Find the start time of the range. Read pages until we find one with a
     // granulepos which we can convert into a timestamp to use as the time of
     // the start of the buffered range.
     ogg_sync_reset(&sync.mState);
     while (startTime == -1) {
       ogg_page page;
       int32_t discard;
@@ -1954,18 +1955,18 @@ media::TimeIntervals OggReader::GetBuffe
     }
 
     if (startTime != -1) {
       // We were able to find a start time for that range, see if we can
       // find an end time.
       int64_t endTime = RangeEndTime(startOffset, endOffset, true);
       if (endTime > startTime) {
         buffered += media::TimeInterval(
-           media::TimeUnit::FromMicroseconds(startTime - mStartTime),
-           media::TimeUnit::FromMicroseconds(endTime - mStartTime));
+           media::TimeUnit::FromMicroseconds(startTime - StartTime()),
+           media::TimeUnit::FromMicroseconds(endTime - StartTime()));
       }
     }
   }
 
   return buffered;
 #endif
 }
 
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -730,31 +730,32 @@ WebMReader::Seek(int64_t aTarget, int64_
   } else {
     return SeekPromise::CreateAndResolve(aTarget, __func__);
   }
 }
 
 nsresult WebMReader::SeekInternal(int64_t aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
+  NS_ENSURE_TRUE(HaveStartTime(), NS_ERROR_FAILURE);
   if (mVideoDecoder) {
     nsresult rv = mVideoDecoder->Flush();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   LOG(LogLevel::Debug, ("Reader [%p] for Decoder [%p]: About to seek to %fs",
                      this, mDecoder, double(aTarget) / USECS_PER_S));
   if (NS_FAILED(ResetDecode())) {
     return NS_ERROR_FAILURE;
   }
   uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
   uint64_t target = aTarget * NS_PER_USEC;
 
   if (mSeekPreroll) {
-    target = std::max(uint64_t(mStartTime * NS_PER_USEC),
+    target = std::max(uint64_t(StartTime() * NS_PER_USEC),
                       target - mSeekPreroll);
   }
   int r = nestegg_track_seek(mContext, trackToSeek, target);
   if (r != 0) {
     LOG(LogLevel::Debug, ("Reader [%p]: track_seek for track %u failed, r=%d",
                        this, trackToSeek, r));
 
     // Try seeking directly based on cluster information in memory.
@@ -772,17 +773,17 @@ nsresult WebMReader::SeekInternal(int64_
     }
   }
   return NS_OK;
 }
 
 media::TimeIntervals WebMReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
-  NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
+  NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
 
   media::TimeIntervals buffered;
   // Special case completely cached files.  This also handles local files.
   if (mContext && resource->IsDataCachedToEndOfResource(0)) {
     uint64_t duration = 0;
     if (nestegg_duration(mContext, &duration) == 0) {
       buffered +=
@@ -799,17 +800,17 @@ media::TimeIntervals WebMReader::GetBuff
   NS_ENSURE_SUCCESS(res, media::TimeIntervals::Invalid());
 
   for (uint32_t index = 0; index < ranges.Length(); index++) {
     uint64_t start, end;
     bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
                                                         ranges[index].mEnd,
                                                         &start, &end);
     if (rv) {
-      int64_t startOffset = mStartTime * NS_PER_USEC;
+      int64_t startOffset = StartTime() * NS_PER_USEC;
       NS_ASSERTION(startOffset >= 0 && uint64_t(startOffset) <= start,
                    "startOffset negative or larger than start time");
       if (!(startOffset >= 0 && uint64_t(startOffset) <= start)) {
         startOffset = 0;
       }
       double startTime = (start - startOffset) / NS_PER_S;
       double endTime = (end - startOffset) / NS_PER_S;
       // If this range extends to the end of the file, the true end time