Files
palemoon27/dom/media/mediasource/MediaSourceDecoder.cpp
T
roytam1 fdb63ff9b9 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1163201 - Part 1: Remove instances of #ifdef PR_LOGGING in dom/. r=froydnj (9979c0e74)
-  Bug 1183972 - No sync-dispatch of new GMPParent - r=cpearce (93339b530)
- Bug 1142935 - reset transports with NuwaAddConstructor(). r=tlee (277406812)
- Bug 1121676 - Use a lock to protect the list of top-level actors (r=bent) (3d0be2f87)
-  Bug 1121676 - Use static mutex to protect top-level protocols (r=bent) (4491dd318)
- Bug 1163201 - Part 2: Wrap expensive calls in PR_LOG_TEST. r=froydnj (7de4b9a97)
- Bug 1163201 - Part 3: Remove mSamples in |MediaEngineWebRTCAudioSource|. r=cpeterson (452442773)
- Bug 1163201 - Part 4: Fix b2g build. r=bustage (a824ea36d)
- Bug 1165518 - Part 1: Add Logging.h. r=froydnj (09d68aaa6)
- Bug 1162850 - Don't stop looking for style sheet load finishes after the FontFaceSet gets a DOMContentLoaded. r=jdaggett (c29fbffa0)
- Bug 1056479 p0 - rename ambiguous GetFontList method in Android fontlist. r=m_kato (76239d7a0)
- Bug 1056479 p1 - add language to FindFamily parameters. r=jfkthame (2271bd7d0)
- Bug 1056479 p1a - use lang as part of pref font fallback. r=karlt (5f5fd66c5)
- cleanup GetTableFromFontData() to match gecko code again (78076fc26)
- Bug 1056479 p2 - implement platform fontlist based on fontconfig. r=karlt (6a7631e44)
- Bug 1056479 p3 - fixup various reftests for Linux. r=jfkthame (b25360708)
- Bug 1056479 p4 - fix accessibility api for font-weight. r=jfkthame (efa8f5080)
- Bug 1056479 p5 - fixup printpreview test. r=jfkthame (3fe2ddc0b)
- Bug 1056479 p6 - handle font updates. r=jfkthame (eb78b2c54)
- Bug 1056479 p7 - fixup assertion for non-italic fallback. r=m_kato (f5e9f539e)
- Bug 1056479 p8 - switch gfxFontConfig to gfxFontconfig. r=karlt (4da146b50)
- Bug 1056479 p9 - fix build bustage. r=birtles (28f246c2b)
- Bug 1056479 p10 - activate bundled fonts. r=m_kato (d7627c3fa)
- Bug 1056479 p10 - activate bundled fonts. r=m_kato (251c02315)
- Bug 1056479 followup: Annotate gfxPlatformGtk::CreatePlatformFontList() as 'override'. rs=ehsan (993e65d6e)
2020-05-30 12:49:03 +08:00

373 lines
11 KiB
C++

/* -*- 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 "prlog.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "MediaDecoderStateMachine.h"
#include "MediaSource.h"
#include "MediaSourceReader.h"
#include "MediaSourceResource.h"
#include "MediaSourceUtils.h"
#include "SourceBufferDecoder.h"
#include "VideoUtils.h"
#include "MediaFormatReader.h"
#include "MediaSourceDemuxer.h"
#include <algorithm>
extern PRLogModuleInfo* GetMediaSourceLog();
#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define MSE_DEBUGV(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG + 1, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
using namespace mozilla::media;
namespace mozilla {
class SourceBufferDecoder;
MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
: mMediaSource(nullptr)
, mIsUsingFormatReader(Preferences::GetBool("media.mediasource.format-reader", false))
, mEnded(false)
{
SetExplicitDuration(UnspecifiedNaN<double>());
Init(aElement);
}
MediaDecoder*
MediaSourceDecoder::Clone()
{
// TODO: Sort out cloning.
return nullptr;
}
MediaDecoderStateMachine*
MediaSourceDecoder::CreateStateMachine()
{
if (mIsUsingFormatReader) {
mDemuxer = new MediaSourceDemuxer();
mReader = new MediaFormatReader(this, mDemuxer);
} else {
mReader = new MediaSourceReader(this);
}
return new MediaDecoderStateMachine(this, mReader);
}
nsresult
MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
{
MOZ_ASSERT(!GetStateMachine());
SetStateMachine(CreateStateMachine());
if (!GetStateMachine()) {
NS_WARNING("Failed to create state machine!");
return NS_ERROR_FAILURE;
}
nsresult rv = GetStateMachine()->Init(nullptr);
NS_ENSURE_SUCCESS(rv, rv);
SetStateMachineParameters();
return ScheduleStateMachineThread();
}
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 = mReader->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;
}
void
MediaSourceDecoder::Shutdown()
{
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();
// Kick WaitForData out of its slumber.
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mon.NotifyAll();
}
/*static*/
already_AddRefed<MediaResource>
MediaSourceDecoder::CreateResource(nsIPrincipal* aPrincipal)
{
return nsRefPtr<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;
}
already_AddRefed<SourceBufferDecoder>
MediaSourceDecoder::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset)
{
MOZ_ASSERT(mReader && !mIsUsingFormatReader);
return GetReader()->CreateSubDecoder(aType, aTimestampOffset);
}
void
MediaSourceDecoder::AddTrackBuffer(TrackBuffer* aTrackBuffer)
{
MOZ_ASSERT(mReader && !mIsUsingFormatReader);
GetReader()->AddTrackBuffer(aTrackBuffer);
}
void
MediaSourceDecoder::RemoveTrackBuffer(TrackBuffer* aTrackBuffer)
{
MOZ_ASSERT(mReader && !mIsUsingFormatReader);
GetReader()->RemoveTrackBuffer(aTrackBuffer);
}
void
MediaSourceDecoder::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo)
{
MOZ_ASSERT(mReader && !mIsUsingFormatReader);
GetReader()->OnTrackBufferConfigured(aTrackBuffer, aInfo);
}
void
MediaSourceDecoder::Ended(bool aEnded)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
static_cast<MediaSourceResource*>(GetResource())->SetEnded(aEnded);
if (!mIsUsingFormatReader) {
GetReader()->Ended(aEnded);
}
mEnded = true;
mon.NotifyAll();
}
bool
MediaSourceDecoder::IsExpectingMoreData()
{
return !mEnded;
}
void
MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
{
MOZ_ASSERT(NS_IsMainThread());
// Only use the decoded duration if one wasn't already
// set.
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mMediaSource || !IsNaN(ExplicitDuration())) {
return;
}
double duration = aDuration;
// A duration of -1 is +Infinity.
if (aDuration >= 0) {
duration /= USECS_PER_S;
}
SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP);
}
void
MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
double oldDuration = ExplicitDuration();
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>());
}
if (!mIsUsingFormatReader && GetReader()) {
GetReader()->SetMediaSourceDuration(ExplicitDuration());
}
MediaDecoder::DurationChanged(TimeUnit::FromSeconds(ExplicitDuration()));
if (mMediaSource && aAction != MSRangeRemovalAction::SKIP) {
mMediaSource->DurationChange(oldDuration, aDuration);
}
}
double
MediaSourceDecoder::GetMediaSourceDuration()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return ExplicitDuration();
}
void
MediaSourceDecoder::NotifyTimeRangesChanged()
{
MOZ_ASSERT(mReader && !mIsUsingFormatReader);
GetReader()->NotifyTimeRangesChanged();
}
void
MediaSourceDecoder::PrepareReaderInitialization()
{
if (mIsUsingFormatReader) {
return;
}
MOZ_ASSERT(mReader);
GetReader()->PrepareInitialization();
}
void
MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString)
{
if (mIsUsingFormatReader) {
return;
}
GetReader()->GetMozDebugReaderData(aString);
}
bool
MediaSourceDecoder::IsActiveReader(MediaDecoderReader* aReader)
{
return !mIsUsingFormatReader && GetReader()->IsActiveReader(aReader);
}
double
MediaSourceDecoder::GetDuration()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
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());
TimeInterval interval(currentPosition,
currentPosition + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED),
MediaSourceDemuxer::EOS_FUZZ);
return GetBuffered().Contains(interval)
? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
: MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
}
bool
MediaSourceDecoder::CanPlayThrough()
{
MOZ_ASSERT(NS_IsMainThread());
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 <= 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.
TimeUnit timeAhead =
std::min(duration, currentPosition + TimeUnit::FromSeconds(30));
TimeInterval interval(currentPosition,
timeAhead,
MediaSourceDemuxer::EOS_FUZZ);
return GetBuffered().Contains(interval);
}
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;
}
#undef MSE_DEBUG
#undef MSE_DEBUGV
} // namespace mozilla