diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/media/mediasource/MediaSourceDecoder.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/media/mediasource/MediaSourceDecoder.cpp')
-rw-r--r-- | dom/media/mediasource/MediaSourceDecoder.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp new file mode 100644 index 000000000..2e83bfd23 --- /dev/null +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -0,0 +1,361 @@ +/* -*- mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "MediaSourceDecoder.h" + +#include "mozilla/Logging.h" +#include "mozilla/dom/HTMLMediaElement.h" +#include "MediaDecoderStateMachine.h" +#include "MediaShutdownManager.h" +#include "MediaSource.h" +#include "MediaSourceResource.h" +#include "MediaSourceUtils.h" +#include "VideoUtils.h" +#include "MediaSourceDemuxer.h" +#include "SourceBufferList.h" +#include <algorithm> + +extern mozilla::LogModule* GetMediaSourceLog(); + +#define MSE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) +#define MSE_DEBUGV(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) + +using namespace mozilla::media; + +namespace mozilla { + +MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement) + : MediaDecoder(aElement) + , mMediaSource(nullptr) + , mEnded(false) +{ + mExplicitDuration.Set(Some(UnspecifiedNaN<double>())); +} + +MediaDecoder* +MediaSourceDecoder::Clone(MediaDecoderOwner* aOwner) +{ + // TODO: Sort out cloning. + return nullptr; +} + +MediaDecoderStateMachine* +MediaSourceDecoder::CreateStateMachine() +{ + MOZ_ASSERT(NS_IsMainThread()); + mDemuxer = new MediaSourceDemuxer(); + mReader = new MediaFormatReader(this, mDemuxer, GetVideoFrameContainer()); + return new MediaDecoderStateMachine(this, mReader); +} + +nsresult +MediaSourceDecoder::Load(nsIStreamListener**) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!GetStateMachine()); + + nsresult rv = MediaShutdownManager::Instance().Register(this); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + SetStateMachine(CreateStateMachine()); + if (!GetStateMachine()) { + NS_WARNING("Failed to create state machine!"); + return NS_ERROR_FAILURE; + } + + rv = GetStateMachine()->Init(this); + NS_ENSURE_SUCCESS(rv, rv); + + SetStateMachineParameters(); + return NS_OK; +} + +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; +} + +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; +} + +void +MediaSourceDecoder::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + MSE_DEBUG("Shutdown"); + // Detach first so that TrackBuffers are unused on the main thread when + // shut down on the decode task queue. + if (mMediaSource) { + mMediaSource->Detach(); + } + mDemuxer = nullptr; + + MediaDecoder::Shutdown(); +} + +/*static*/ +already_AddRefed<MediaResource> +MediaSourceDecoder::CreateResource(nsIPrincipal* aPrincipal) +{ + return RefPtr<MediaResource>(new MediaSourceResource(aPrincipal)).forget(); +} + +void +MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource) +{ + MOZ_ASSERT(!mMediaSource && !GetStateMachine() && NS_IsMainThread()); + mMediaSource = aMediaSource; +} + +void +MediaSourceDecoder::DetachMediaSource() +{ + MOZ_ASSERT(mMediaSource && NS_IsMainThread()); + mMediaSource = nullptr; +} + +void +MediaSourceDecoder::Ended(bool aEnded) +{ + MOZ_ASSERT(NS_IsMainThread()); + static_cast<MediaSourceResource*>(GetResource())->SetEnded(aEnded); + if (aEnded) { + // We want the MediaSourceReader to refresh its buffered range as it may + // have been modified (end lined up). + NotifyDataArrived(); + } + mEnded = aEnded; +} + +void +MediaSourceDecoder::AddSizeOfResources(ResourceSizes* aSizes) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (GetDemuxer()) { + GetDemuxer()->AddSizeOfResources(aSizes); + } +} + +void +MediaSourceDecoder::SetInitialDuration(int64_t aDuration) +{ + MOZ_ASSERT(NS_IsMainThread()); + // Only use the decoded duration if one wasn't already + // set. + if (!mMediaSource || !IsNaN(ExplicitDuration())) { + return; + } + double duration = aDuration; + // A duration of -1 is +Infinity. + if (aDuration >= 0) { + duration /= USECS_PER_S; + } + SetMediaSourceDuration(duration); +} + +void +MediaSourceDecoder::SetMediaSourceDuration(double aDuration) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsShutdown()); + if (aDuration >= 0) { + int64_t checkedDuration; + if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) { + // INT64_MAX is used as infinity by the state machine. + // We want a very bigger number, but not infinity. + checkedDuration = INT64_MAX - 1; + } + SetExplicitDuration(aDuration); + } else { + SetExplicitDuration(PositiveInfinity<double>()); + } +} + +void +MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString) +{ + if (mReader && mDemuxer) { + mReader->GetMozDebugReaderData(aString); + mDemuxer->GetMozDebugReaderData(aString); + } +} + +double +MediaSourceDecoder::GetDuration() +{ + MOZ_ASSERT(NS_IsMainThread()); + return ExplicitDuration(); +} + +MediaDecoderOwner::NextFrameStatus +MediaSourceDecoder::NextFrameBufferedStatus() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mMediaSource || + mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) { + return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; + } + + // Next frame hasn't been decoded yet. + // Use the buffered range to consider if we have the next frame available. + TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition()); + TimeIntervals buffered = GetBuffered(); + buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); + TimeInterval interval( + currentPosition, + currentPosition + + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED)); + return buffered.ContainsStrict(ClampIntervalToEnd(interval)) + ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE + : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; +} + +bool +MediaSourceDecoder::CanPlayThrough() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (NextFrameBufferedStatus() == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) { + return false; + } + + if (IsNaN(mMediaSource->Duration())) { + // Don't have any data yet. + return false; + } + TimeUnit duration = TimeUnit::FromSeconds(mMediaSource->Duration()); + TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition()); + if (duration.IsInfinite()) { + // We can't make an informed decision and just assume that it's a live stream + return true; + } else if (duration <= currentPosition) { + return true; + } + // If we have data up to the mediasource's duration or 30s ahead, we can + // assume that we can play without interruption. + TimeIntervals buffered = GetBuffered(); + buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); + TimeUnit timeAhead = + std::min(duration, currentPosition + TimeUnit::FromSeconds(30)); + TimeInterval interval(currentPosition, timeAhead); + return buffered.ContainsStrict(ClampIntervalToEnd(interval)); +} + +void +MediaSourceDecoder::NotifyWaitingForKey() +{ + mWaitingForKeyEvent.Notify(); +} + +MediaEventSource<void>* +MediaSourceDecoder::WaitingForKeyEvent() +{ + return &mWaitingForKeyEvent; +} + +TimeInterval +MediaSourceDecoder::ClampIntervalToEnd(const TimeInterval& aInterval) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mEnded) { + return aInterval; + } + TimeUnit duration = TimeUnit::FromSeconds(GetDuration()); + if (duration < aInterval.mStart) { + return aInterval; + } + return TimeInterval(aInterval.mStart, + std::min(aInterval.mEnd, duration), + aInterval.mFuzz); +} + +#undef MSE_DEBUG +#undef MSE_DEBUGV + +} // namespace mozilla |