TimeRanges* SourceBuffer::GetBuffered(ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); // http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps. if (!IsAttached()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } bool rangeChanged = true; media::TimeIntervals intersection = mTrackBuffersManager->Buffered(); MSE_DEBUGV("intersection=%s", DumpTimeRanges(intersection).get()); if (mBuffered) { media::TimeIntervals currentValue(mBuffered); rangeChanged = (intersection != currentValue); MSE_DEBUGV("currentValue=%s", DumpTimeRanges(currentValue).get()); } // 5. If intersection ranges does not contain the exact same range information as the current value of this attribute, then update the current value of this attribute to intersection ranges. if (rangeChanged) { mBuffered = new TimeRanges(ToSupports(this)); intersection.ToTimeRanges(mBuffered); } // 6. Return the current value of this attribute. return mBuffered; }
void MediaSourceReader::GetMozDebugReaderData(nsAString& aString) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); nsAutoCString result; result += nsPrintfCString("Dumping data for reader %p:\n", this); if (mAudioTrack) { result += nsPrintfCString("\tDumping Audio Track Decoders: - mLastAudioTime: %f\n", double(mLastAudioTime) / USECS_PER_S); for (int32_t i = mAudioTrack->Decoders().Length() - 1; i >= 0; --i) { nsRefPtr<MediaDecoderReader> newReader = mAudioTrack->Decoders()[i]->GetReader(); nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); mAudioTrack->Decoders()[i]->GetBuffered(ranges); result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n", i, newReader.get(), DumpTimeRanges(ranges).get(), newReader.get() == GetAudioReader() ? "true" : "false", mAudioTrack->Decoders()[i]->GetResource()->GetSize()); } } if (mVideoTrack) { result += nsPrintfCString("\tDumping Video Track Decoders - mLastVideoTime: %f\n", double(mLastVideoTime) / USECS_PER_S); for (int32_t i = mVideoTrack->Decoders().Length() - 1; i >= 0; --i) { nsRefPtr<MediaDecoderReader> newReader = mVideoTrack->Decoders()[i]->GetReader(); nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); mVideoTrack->Decoders()[i]->GetBuffered(ranges); result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n", i, newReader.get(), DumpTimeRanges(ranges).get(), newReader.get() == GetVideoReader() ? "true" : "false", mVideoTrack->Decoders()[i]->GetResource()->GetSize()); } } aString += NS_ConvertUTF8toUTF16(result); }
media::TimeIntervals MediaSourceDecoder::GetSeekable() { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { NS_WARNING("MediaSource element isn't attached"); return media::TimeIntervals::Invalid(); } media::TimeIntervals seekable; double duration = mMediaSource->Duration(); if (IsNaN(duration)) { // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { media::TimeIntervals buffered = GetBuffered(); if (buffered.Length()) { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), buffered.GetEnd()); } } else { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), media::TimeUnit::FromSeconds(duration)); } MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get()); return seekable; }
already_AddRefed<TimeRanges> SourceBuffer::GetBuffered(ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); if (!IsAttached()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } double highestEndTime = 0; nsRefPtr<TimeRanges> ranges = new TimeRanges(); // TODO: Need to adjust mDecoders so it only tracks active decoders. // Once we have an abstraction for track buffers, this needs to report the // intersection of buffered ranges within those track buffers. for (uint32_t i = 0; i < mDecoders.Length(); ++i) { nsRefPtr<TimeRanges> r = new TimeRanges(); mDecoders[i]->GetBuffered(r); if (r->Length() > 0) { highestEndTime = std::max(highestEndTime, r->GetEndTime()); ranges->Union(r); } } if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { // Set the end time on the last range to highestEndTime by adding a // new range spanning the current end time to highestEndTime, which // Normalize() will then merge with the old last range. ranges->Add(ranges->GetEndTime(), highestEndTime); ranges->Normalize(); } MSE_DEBUGV("SourceBuffer(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(ranges).get()); return ranges.forget(); }
already_AddRefed<SourceBufferDecoder> MediaSourceDecoder::SelectDecoder(int64_t aTarget, int64_t aTolerance, const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders) { MOZ_ASSERT(!mIsUsingFormatReader); ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); media::TimeUnit target{media::TimeUnit::FromMicroseconds(aTarget)}; media::TimeUnit tolerance{media::TimeUnit::FromMicroseconds(aTolerance + aTarget)}; // aTolerance gives a slight bias toward the start of a range only. // Consider decoders in order of newest to oldest, as a newer decoder // providing a given buffered range is expected to replace an older one. for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) { nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i]; media::TimeIntervals ranges = newDecoder->GetBuffered(); for (uint32_t j = 0; j < ranges.Length(); j++) { if (target < ranges.End(j) && tolerance >= ranges.Start(j)) { return newDecoder.forget(); } } MSE_DEBUGV("SelectDecoder(%lld fuzz:%lld) newDecoder=%p (%d/%d) target not in ranges=%s", aTarget, aTolerance, newDecoder.get(), i+1, aTrackDecoders.Length(), DumpTimeRanges(ranges).get()); } return nullptr; }
already_AddRefed<SourceBufferDecoder> MediaSourceDecoder::SelectDecoder(int64_t aTarget, int64_t aTolerance, const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders) { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); // Consider decoders in order of newest to oldest, as a newer decoder // providing a given buffered range is expected to replace an older one. for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) { nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i]; nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); newDecoder->GetBuffered(ranges); if (ranges->Find(double(aTarget) / USECS_PER_S, double(aTolerance) / USECS_PER_S) == dom::TimeRanges::NoIndex) { MSE_DEBUGV("SelectDecoder(%lld fuzz:%lld) newDecoder=%p (%d/%d) target not in ranges=%s", aTarget, aTolerance, newDecoder.get(), i+1, aTrackDecoders.Length(), DumpTimeRanges(ranges).get()); continue; } return newDecoder.forget(); } return nullptr; }
media::TimeIntervals MediaSourceReader::GetBuffered() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); media::TimeIntervals buffered; media::TimeUnit highestEndTime; nsTArray<media::TimeIntervals> activeRanges; // Must set the capacity of the nsTArray first: bug #1164444 activeRanges.SetCapacity(mTrackBuffers.Length()); for (const auto& trackBuffer : mTrackBuffers) { activeRanges.AppendElement(trackBuffer->Buffered()); highestEndTime = std::max(highestEndTime, activeRanges.LastElement().GetEnd()); } buffered += media::TimeInterval(media::TimeUnit::FromMicroseconds(0), highestEndTime); for (auto& range : activeRanges) { if (IsEnded() && range.Length()) { // Set the end time on the last range to highestEndTime by adding a // new range spanning the current end time to highestEndTime, which // Normalize() will then merge with the old last range. range += media::TimeInterval(range.GetEnd(), highestEndTime); } buffered.Intersection(range); } MSE_DEBUG("ranges=%s", DumpTimeRanges(buffered).get()); return buffered; }
media::TimeIntervals MediaSourceDecoder::GetBuffered() { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { NS_WARNING("MediaSource element isn't attached"); return media::TimeIntervals::Invalid(); } dom::SourceBufferList* sourceBuffers = mMediaSource->ActiveSourceBuffers(); if (!sourceBuffers) { // Media source object is shutting down. return TimeIntervals(); } media::TimeUnit highestEndTime; nsTArray<media::TimeIntervals> activeRanges; media::TimeIntervals buffered; for (uint32_t i = 0; i < sourceBuffers->Length(); i++) { bool found; dom::SourceBuffer* sb = sourceBuffers->IndexedGetter(i, found); MOZ_ASSERT(found); activeRanges.AppendElement(sb->GetTimeIntervals()); highestEndTime = std::max(highestEndTime, activeRanges.LastElement().GetEnd()); } buffered += media::TimeInterval(media::TimeUnit::FromMicroseconds(0), highestEndTime); for (auto& range : activeRanges) { if (mEnded && range.Length()) { // Set the end time on the last range to highestEndTime by adding a // new range spanning the current end time to highestEndTime, which // Normalize() will then merge with the old last range. range += media::TimeInterval(range.GetEnd(), highestEndTime); } buffered.Intersection(range); } MSE_DEBUG("ranges=%s", DumpTimeRanges(buffered).get()); return buffered; }
already_AddRefed<TimeRanges> SourceBuffer::GetBuffered(ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); if (!IsAttached()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } nsRefPtr<TimeRanges> ranges = new TimeRanges(); double highestEndTime = mTrackBuffer->Buffered(ranges); if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { // Set the end time on the last range to highestEndTime by adding a // new range spanning the current end time to highestEndTime, which // Normalize() will then merge with the old last range. ranges->Add(ranges->GetEndTime(), highestEndTime); ranges->Normalize(); } MSE_DEBUGV("ranges=%s", DumpTimeRanges(ranges).get()); return ranges.forget(); }
media::TimeIntervals MediaSourceDecoder::GetSeekable() { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { NS_WARNING("MediaSource element isn't attached"); return media::TimeIntervals::Invalid(); } media::TimeIntervals seekable; double duration = mMediaSource->Duration(); if (IsNaN(duration)) { // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { media::TimeIntervals buffered = GetBuffered(); // 1. If live seekable range is not empty: if (mMediaSource->HasLiveSeekableRange()) { // 1. Let union ranges be the union of live seekable range and the // HTMLMediaElement.buffered attribute. media::TimeIntervals unionRanges = buffered + mMediaSource->LiveSeekableRange(); // 2. Return a single range with a start time equal to the earliest start // time in union ranges and an end time equal to the highest end time in // union ranges and abort these steps. seekable += media::TimeInterval(unionRanges.GetStart(), unionRanges.GetEnd()); return seekable; } if (buffered.Length()) { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), buffered.GetEnd()); } } else { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), media::TimeUnit::FromSeconds(duration)); } MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get()); return seekable; }
nsresult MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable) { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { return NS_ERROR_FAILURE; } double duration = mMediaSource->Duration(); if (IsNaN(duration)) { // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges(); mMediaSource->GetBuffered(bufferedRanges); aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime()); } else { aSeekable->Add(0, duration); } MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable ranges=%s", this, DumpTimeRanges(aSeekable).get()); return NS_OK; }
void MediaSource::GetBuffered(TimeRanges* aBuffered) { MOZ_ASSERT(aBuffered->Length() == 0); if (mActiveSourceBuffers->IsEmpty()) { return; } double highestEndTime = 0; nsTArray<nsRefPtr<TimeRanges>> activeRanges; for (uint32_t i = 0; i < mActiveSourceBuffers->Length(); ++i) { bool found; SourceBuffer* sourceBuffer = mActiveSourceBuffers->IndexedGetter(i, found); ErrorResult dummy; *activeRanges.AppendElement() = sourceBuffer->GetBuffered(dummy); highestEndTime = std::max(highestEndTime, activeRanges.LastElement()->GetEndTime()); } TimeRanges* intersectionRanges = aBuffered; intersectionRanges->Add(0, highestEndTime); for (uint32_t i = 0; i < activeRanges.Length(); ++i) { TimeRanges* sourceRanges = activeRanges[i]; if (mReadyState == MediaSourceReadyState::Ended) { // Set the end time on the last range to highestEndTime by adding a // new range spanning the current end time to highestEndTime, which // Normalize() will then merge with the old last range. sourceRanges->Add(sourceRanges->GetEndTime(), highestEndTime); sourceRanges->Normalize(); } intersectionRanges->Intersection(sourceRanges); } MSE_DEBUG("MediaSource(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(intersectionRanges).get()); }
nsresult MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MOZ_ASSERT(aBuffered->Length() == 0); if (mTrackBuffers.IsEmpty()) { return NS_OK; } double highestEndTime = 0; nsTArray<nsRefPtr<TimeRanges>> activeRanges; for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) { nsRefPtr<TimeRanges> r = new TimeRanges(); mTrackBuffers[i]->Buffered(r); activeRanges.AppendElement(r); highestEndTime = std::max(highestEndTime, activeRanges.LastElement()->GetEndTime()); } TimeRanges* intersectionRanges = aBuffered; intersectionRanges->Add(0, highestEndTime); for (uint32_t i = 0; i < activeRanges.Length(); ++i) { TimeRanges* sourceRanges = activeRanges[i]; if (IsEnded()) { // Set the end time on the last range to highestEndTime by adding a // new range spanning the current end time to highestEndTime, which // Normalize() will then merge with the old last range. sourceRanges->Add(sourceRanges->GetEndTime(), highestEndTime); sourceRanges->Normalize(); } intersectionRanges->Intersection(sourceRanges); } MSE_DEBUG("ranges=%s", DumpTimeRanges(intersectionRanges).get()); return NS_OK; }
already_AddRefed<MediaDecoderReader> MediaSourceReader::SelectReader(int64_t aTarget, const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders) { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); // Consider decoders in order of newest to oldest, as a newer decoder // providing a given buffered range is expected to replace an older one. for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) { nsRefPtr<MediaDecoderReader> newReader = aTrackDecoders[i]->GetReader(); nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); aTrackDecoders[i]->GetBuffered(ranges); if (ranges->Find(double(aTarget) / USECS_PER_S) == dom::TimeRanges::NoIndex) { MSE_DEBUGV("MediaSourceReader(%p)::SelectReader(%lld) newReader=%p target not in ranges=%s", this, aTarget, newReader.get(), DumpTimeRanges(ranges).get()); continue; } return newReader.forget(); } return nullptr; }