summaryrefslogtreecommitdiffstats
path: root/dom/media/mediasource/MediaSourceDecoder.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/media/mediasource/MediaSourceDecoder.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-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.cpp361
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