Bug 1817997 - Replace more double representing seconds by TimeUnit in MediaSource. r=kinetik

Differential Revision: https://phabricator.services.mozilla.com/D176037
This commit is contained in:
Paul Adenot 2023-05-24 13:18:42 +00:00
parent 66362e5e96
commit 0412867c82
12 changed files with 145 additions and 69 deletions

View File

@ -3293,12 +3293,7 @@ double HTMLMediaElement::Duration() const {
}
if (mDecoder) {
// Limit resolution to microsecond.
double duration = mDecoder->GetDuration();
if (IsFinite<double>(duration)) {
return ToMicrosecondResolution(mDecoder->GetDuration());
}
return duration;
return mDecoder->GetDuration();
}
return std::numeric_limits<double>::quiet_NaN();
@ -3307,7 +3302,7 @@ double HTMLMediaElement::Duration() const {
already_AddRefed<TimeRanges> HTMLMediaElement::Seekable() const {
media::TimeIntervals seekable =
mDecoder ? mDecoder->GetSeekable() : media::TimeIntervals();
RefPtr<TimeRanges> ranges = new TimeRanges(ToSupports(OwnerDoc()), seekable.ToMicrosecondResolution());
RefPtr<TimeRanges> ranges = new TimeRanges(ToSupports(OwnerDoc()), seekable);
return ranges.forget();
}

View File

@ -314,7 +314,8 @@ void ChannelMediaDecoder::NotifyDownloadEnded(nsresult aStatus) {
"ChannelMediaDecoder::UpdatePlaybackRate",
[stats = mPlaybackStatistics,
res = RefPtr<BaseMediaResource>(mResource), duration = mDuration]() {
auto rate = ComputePlaybackRate(stats, res, duration);
auto rate = ComputePlaybackRate(stats, res,
duration.match(DurationToTimeUnit()));
UpdatePlaybackRate(rate, res);
});
nsresult rv = GetStateMachine()->OwnerThread()->Dispatch(r.forget());
@ -372,7 +373,8 @@ void ChannelMediaDecoder::DurationChanged() {
"ChannelMediaDecoder::UpdatePlaybackRate",
[stats = mPlaybackStatistics, res = RefPtr<BaseMediaResource>(mResource),
duration = mDuration]() {
auto rate = ComputePlaybackRate(stats, res, duration);
auto rate = ComputePlaybackRate(stats, res,
duration.match(DurationToTimeUnit()));
UpdatePlaybackRate(rate, res);
});
nsresult rv = GetStateMachine()->OwnerThread()->Dispatch(r.forget());
@ -391,7 +393,8 @@ void ChannelMediaDecoder::DownloadProgressed() {
[playbackStats = mPlaybackStatistics,
res = RefPtr<BaseMediaResource>(mResource), duration = mDuration,
pos = mPlaybackPosition]() {
auto rate = ComputePlaybackRate(playbackStats, res, duration);
auto rate = ComputePlaybackRate(
playbackStats, res, duration.match(DurationToTimeUnit()));
UpdatePlaybackRate(rate, res);
MediaStatistics stats = GetStatistics(rate, res, pos);
return StatsPromise::CreateAndResolve(stats, __func__);

View File

@ -242,12 +242,12 @@ void MediaDecoder::SetOutputTracksPrincipal(
double MediaDecoder::GetDuration() {
MOZ_ASSERT(NS_IsMainThread());
return mDuration.IsValid() ? mDuration.ToSeconds() : std::numeric_limits<double>::quiet_NaN();
return ToMicrosecondResolution(mDuration.match(DurationToDouble()));
}
bool MediaDecoder::IsInfinite() const {
MOZ_ASSERT(NS_IsMainThread());
return mDuration.IsInfinite();
return std::isinf(mDuration.match(DurationToDouble()));
}
#define INIT_MIRROR(name, val) \
@ -1022,7 +1022,7 @@ void MediaDecoder::UpdateLogicalPositionInternal() {
TimeUnit currentPosition = CurrentPosition();
if (mPlayState == PLAY_STATE_ENDED) {
currentPosition = std::max(currentPosition, mDuration);
currentPosition = std::max(currentPosition, mDuration.match(DurationToTimeUnit()));
}
const PositionUpdate reason =
@ -1073,31 +1073,40 @@ void MediaDecoder::DurationChanged() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
TimeUnit oldDuration = mDuration;
Variant<TimeUnit, double> oldDuration = mDuration;
// Use the explicit duration if we have one.
// Otherwise use the duration mirrored from MDSM.
if (mExplicitDuration.isSome()) {
mDuration = TimeUnit::FromSeconds(mExplicitDuration.ref());
mDuration.emplace<double>(mExplicitDuration.ref());
} else if (mStateMachineDuration.Ref().isSome()) {
mDuration = mStateMachineDuration.Ref().ref();
MOZ_ASSERT(mStateMachineDuration.Ref().ref().IsValid());
mDuration.emplace<TimeUnit>(mStateMachineDuration.Ref().ref());
}
if (oldDuration.IsValid() && mDuration == oldDuration) {
return;
LOG("New duration to %s", mDuration.match(DurationToTimeUnit()).ToString().get());
if (oldDuration.is<TimeUnit>() && oldDuration.as<TimeUnit>().IsValid()) {
LOG("Duration %s", oldDuration.match(DurationToTimeUnit()).ToString().get());
}
LOG("Duration changed to %f", mDuration.ToSeconds());
if ((oldDuration.is<double>() || oldDuration.as<TimeUnit>().IsValid())) {
if (mDuration.match(DurationToDouble()) ==
oldDuration.match(DurationToDouble())) {
return;
}
}
LOG("Duration changed to %s", mDuration.match(DurationToTimeUnit()).ToString().get());
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
// of whether we should fire durationchange on explicit infinity.
if (mFiredMetadataLoaded &&
(!mDuration.IsInfinite() || mExplicitDuration.isSome())) {
(!std::isinf(mDuration.match(DurationToDouble())) || mExplicitDuration.isSome())) {
GetOwner()->DispatchAsyncEvent(u"durationchange"_ns);
}
if (CurrentPosition() > mDuration) {
Seek(mDuration.ToSeconds(), SeekTarget::Accurate);
if (CurrentPosition().ToSeconds() > mDuration.match(DurationToDouble())) {
Seek(mDuration.match(DurationToDouble()), SeekTarget::Accurate);
}
}
@ -1258,7 +1267,7 @@ media::TimeIntervals MediaDecoder::GetSeekable() {
}
return media::TimeIntervals(media::TimeInterval(
TimeUnit::Zero(), IsInfinite() ? TimeUnit::FromInfinity()
: mDuration));
: mDuration.match(DurationToTimeUnit()).ToBase(1000000)));
}
void MediaDecoder::SetFragmentEndTime(double aTime) {

View File

@ -51,6 +51,31 @@ class MediaDecoderStateMachineBase;
struct MediaPlaybackEvent;
struct SharedDummyTrack;
struct DurationToDouble {
double operator()(double aDouble) { return aDouble; }
double operator()(const media::TimeUnit& aTimeUnit) {
if (aTimeUnit.IsValid()) {
if (aTimeUnit.IsPosInf()) {
return std::numeric_limits<double>::infinity();
}
if (aTimeUnit.IsNegInf()) {
return -std::numeric_limits<double>::infinity();
}
return aTimeUnit.ToSeconds();
}
return std::numeric_limits<double>::quiet_NaN();
}
};
struct DurationToTimeUnit {
media::TimeUnit operator()(double aDouble) {
return media::TimeUnit::FromSeconds(aDouble);
}
media::TimeUnit operator()(const media::TimeUnit& aTimeUnit) {
return aTimeUnit;
}
};
struct MOZ_STACK_CLASS MediaDecoderInit {
MediaDecoderOwner* const mOwner;
TelemetryProbesReporterOwner* const mReporterOwner;
@ -477,7 +502,10 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
already_AddRefed<layers::KnowsCompositor> GetCompositor();
// Official duration of the media resource as observed by script.
media::TimeUnit mDuration;
// This can be a TimeUnit representing the exact duration found by demuxing,
// as a TimeUnit. This can also be a duration set explicitly by script, as a
// double.
Variant<media::TimeUnit, double> mDuration;
/******
* The following member variables can be accessed from any thread.

View File

@ -588,12 +588,13 @@ void MediaSource::QueueAsyncSimpleEvent(const char* aName) {
mAbstractMainThread->Dispatch(event.forget());
}
void MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv) {
void MediaSource::DurationChange(const media::TimeUnit& aNewDuration,
ErrorResult& aRv) {
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("DurationChange(aNewDuration=%f)", aNewDuration);
MSE_DEBUG("DurationChange(aNewDuration=%s)", aNewDuration.ToString().get());
// 1. If the current value of duration is equal to new duration, then return.
if (mDecoder->GetDuration() == aNewDuration) {
if (mDecoder->GetDuration() == aNewDuration.ToSeconds()) {
return;
}
@ -607,14 +608,43 @@ void MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv) {
// 3. Let highest end time be the largest track buffer ranges end time across
// all the track buffers across all SourceBuffer objects in sourceBuffers.
double highestEndTime = mSourceBuffers->HighestEndTime();
media::TimeUnit highestEndTime = mSourceBuffers->HighestEndTime();
// 4. If new duration is less than highest end time, then
// 4.1 Update new duration to equal highest end time.
aNewDuration = std::max(aNewDuration, highestEndTime);
media::TimeUnit newDuration = std::max(aNewDuration, highestEndTime);
// 5. Update the media duration to new duration and run the HTMLMediaElement
// duration change algorithm.
mDecoder->SetMediaSourceDuration(media::TimeUnit::FromSeconds(aNewDuration));
mDecoder->SetMediaSourceDuration(newDuration);
}
void MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv) {
MOZ_ASSERT(NS_IsMainThread());
MSE_DEBUG("DurationChange(aNewDuration=%f)", aNewDuration);
// 1. If the current value of duration is equal to new duration, then return.
if (mDecoder->GetDuration() == aNewDuration) {
return;
}
// 2. If new duration is less than the highest starting presentation timestamp
// of any buffered coded frames for all SourceBuffer objects in sourceBuffers,
// then throw an InvalidStateError exception and abort these steps.
if (aNewDuration < mSourceBuffers->HighestStartTime().ToSeconds()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// 3. Let highest end time be the largest track buffer ranges end time across
// all the track buffers across all SourceBuffer objects in sourceBuffers.
double highestEndTime = mSourceBuffers->HighestEndTime().ToSeconds();
// 4. If new duration is less than highest end time, then
// 4.1 Update new duration to equal highest end time.
double newDuration = std::max(aNewDuration, highestEndTime);
// 5. Update the media duration to new duration and run the HTMLMediaElement
// duration change algorithm.
mDecoder->SetMediaSourceDuration(newDuration);
}
already_AddRefed<Promise> MediaSource::MozDebugReaderData(ErrorResult& aRv) {

View File

@ -139,6 +139,7 @@ class MediaSource final : public DOMEventTargetHelper,
void DispatchSimpleEvent(const char* aName);
void QueueAsyncSimpleEvent(const char* aName);
void DurationChange(const media::TimeUnit& aNewDuration, ErrorResult& aRv);
void DurationChange(double aNewDuration, ErrorResult& aRv);
// SetDuration with no checks.

View File

@ -8,6 +8,7 @@
#include "base/process_util.h"
#include "mozilla/Logging.h"
#include "ExternalEngineStateMachine.h"
#include "MediaDecoder.h"
#include "MediaDecoderStateMachine.h"
#include "MediaShutdownManager.h"
#include "MediaSource.h"
@ -111,8 +112,8 @@ media::TimeIntervals MediaSourceDecoder::GetSeekable() {
seekable += media::TimeInterval(TimeUnit::Zero(), buffered.GetEnd());
}
} else {
seekable +=
media::TimeInterval(TimeUnit::Zero(), TimeUnit::FromSeconds(duration));
seekable +=
media::TimeInterval(TimeUnit::Zero(), mDuration.match(DurationToTimeUnit()));
}
MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get());
return seekable;
@ -217,7 +218,17 @@ void MediaSourceDecoder::SetMediaSourceDuration(const TimeUnit& aDuration) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsShutdown());
if (aDuration.IsPositiveOrZero()) {
SetExplicitDuration(aDuration.ToSeconds());
SetExplicitDuration(ToMicrosecondResolution(aDuration.ToSeconds()));
} else {
SetExplicitDuration(PositiveInfinity<double>());
}
}
void MediaSourceDecoder::SetMediaSourceDuration(double aDuration) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsShutdown());
if (aDuration >= 0) {
SetExplicitDuration(aDuration);
} else {
SetExplicitDuration(PositiveInfinity<double>());
}
@ -288,12 +299,12 @@ bool MediaSourceDecoder::CanPlayThroughImpl() {
}
// If we have data up to the mediasource's duration or 3s ahead, we can
// assume that we can play without interruption.
TimeIntervals buffered = GetBuffered();
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
dom::SourceBufferList* sourceBuffers = mMediaSource->ActiveSourceBuffers();
TimeUnit bufferedEnd = sourceBuffers->GetHighestBufferedEndTime();
TimeUnit timeAhead =
std::min(duration, currentPosition + TimeUnit::FromSeconds(3));
TimeInterval interval(currentPosition, timeAhead);
return buffered.ToMicrosecondResolution().ContainsWithStrictEnd(ClampIntervalToEnd(interval));
return bufferedEnd >= timeAhead;
}
TimeInterval MediaSourceDecoder::ClampIntervalToEnd(
@ -303,7 +314,7 @@ TimeInterval MediaSourceDecoder::ClampIntervalToEnd(
if (!mEnded) {
return aInterval;
}
TimeUnit duration = mDuration;
TimeUnit duration = mDuration.match(DurationToTimeUnit());
if (duration < aInterval.mStart) {
return aInterval;
}
@ -313,7 +324,6 @@ TimeInterval MediaSourceDecoder::ClampIntervalToEnd(
void MediaSourceDecoder::NotifyInitDataArrived() {
MOZ_ASSERT(NS_IsMainThread());
if (mDemuxer) {
mDemuxer->NotifyInitDataArrived();
}

View File

@ -45,6 +45,7 @@ class MediaSourceDecoder : public MediaDecoder,
void SetInitialDuration(const media::TimeUnit& aDuration);
void SetMediaSourceDuration(const media::TimeUnit& aDuration);
void SetMediaSourceDuration(double aDuration);
MediaSourceDemuxer* GetDemuxer() { return mDemuxer; }

View File

@ -102,6 +102,11 @@ void SourceBuffer::SetTimestampOffset(double aTimestampOffset,
}
}
media::TimeIntervals SourceBuffer::GetBufferedIntervals() {
MOZ_ASSERT(mTrackBuffersManager);
return mTrackBuffersManager->Buffered();
}
TimeRanges* SourceBuffer::GetBuffered(ErrorResult& aRv) {
MOZ_ASSERT(NS_IsMainThread());
// http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
@ -131,6 +136,7 @@ TimeRanges* SourceBuffer::GetBuffered(ErrorResult& aRv) {
}
media::TimeIntervals SourceBuffer::GetTimeIntervals() {
MOZ_ASSERT(mTrackBuffersManager);
return mTrackBuffersManager->Buffered();
}
@ -708,32 +714,23 @@ already_AddRefed<MediaByteBuffer> SourceBuffer::PrepareAppend(
return data.forget();
}
double SourceBuffer::GetBufferedStart() {
TimeUnit SourceBuffer::GetBufferedEnd() {
MOZ_ASSERT(NS_IsMainThread());
ErrorResult dummy;
RefPtr<TimeRanges> ranges = GetBuffered(dummy);
return ranges->Length() > 0 ? ranges->GetStartTime() : 0;
media::TimeIntervals intervals = GetBufferedIntervals();
return intervals.GetEnd();
}
double SourceBuffer::GetBufferedEnd() {
TimeUnit SourceBuffer::HighestStartTime() {
MOZ_ASSERT(NS_IsMainThread());
ErrorResult dummy;
RefPtr<TimeRanges> ranges = GetBuffered(dummy);
return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
MOZ_ASSERT(mTrackBuffersManager);
return mTrackBuffersManager->HighestStartTime();
}
double SourceBuffer::HighestStartTime() {
TimeUnit SourceBuffer::HighestEndTime() {
MOZ_ASSERT(NS_IsMainThread());
return mTrackBuffersManager
? mTrackBuffersManager->HighestStartTime().ToSeconds()
: 0.0;
}
double SourceBuffer::HighestEndTime() {
MOZ_ASSERT(NS_IsMainThread());
return mTrackBuffersManager
? mTrackBuffersManager->HighestEndTime().ToSeconds()
: 0.0;
MOZ_ASSERT(mTrackBuffersManager);
return mTrackBuffersManager->HighestEndTime();
}
NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)

View File

@ -120,10 +120,10 @@ class SourceBuffer final : public DOMEventTargetHelper,
void Ended();
double GetBufferedStart();
double GetBufferedEnd();
double HighestStartTime();
double HighestEndTime();
media::TimeIntervals GetBufferedIntervals();
media::TimeUnit GetBufferedEnd();
media::TimeUnit HighestStartTime();
media::TimeUnit HighestEndTime();
// Runs the range removal algorithm as defined by the MSE spec.
void RangeRemoval(double aStart, double aEnd);

View File

@ -29,6 +29,8 @@ extern mozilla::LogModule* GetMediaSourceAPILog();
struct JSContext;
class JSObject;
using TimeUnit = mozilla::media::TimeUnit;
namespace mozilla::dom {
SourceBufferList::~SourceBufferList() = default;
@ -115,9 +117,9 @@ void SourceBufferList::Ended() {
}
}
double SourceBufferList::GetHighestBufferedEndTime() {
TimeUnit SourceBufferList::GetHighestBufferedEndTime() {
MOZ_ASSERT(NS_IsMainThread());
double highestEndTime = 0;
TimeUnit highestEndTime = TimeUnit::Zero();
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
highestEndTime =
std::max(highestEndTime, mSourceBuffers[i]->GetBufferedEnd());
@ -147,9 +149,9 @@ SourceBufferList::SourceBufferList(MediaSource* aMediaSource)
MediaSource* SourceBufferList::GetParentObject() const { return mMediaSource; }
double SourceBufferList::HighestStartTime() {
TimeUnit SourceBufferList::HighestStartTime() {
MOZ_ASSERT(NS_IsMainThread());
double highestStartTime = 0;
TimeUnit highestStartTime = TimeUnit::Zero();
for (auto& sourceBuffer : mSourceBuffers) {
highestStartTime =
std::max(sourceBuffer->HighestStartTime(), highestStartTime);
@ -157,9 +159,9 @@ double SourceBufferList::HighestStartTime() {
return highestStartTime;
}
double SourceBufferList::HighestEndTime() {
TimeUnit SourceBufferList::HighestEndTime() {
MOZ_ASSERT(NS_IsMainThread());
double highestEndTime = 0;
TimeUnit highestEndTime = TimeUnit::Zero();
for (auto& sourceBuffer : mSourceBuffers) {
highestEndTime = std::max(sourceBuffer->HighestEndTime(), highestEndTime);
}

View File

@ -79,7 +79,7 @@ class SourceBufferList final : public DOMEventTargetHelper {
void Ended();
// Returns the highest end time of any of the Sourcebuffers.
double GetHighestBufferedEndTime();
media::TimeUnit GetHighestBufferedEndTime();
// Append a SourceBuffer to the list. No event is fired.
void AppendSimple(SourceBuffer* aSourceBuffer);
@ -88,8 +88,8 @@ class SourceBufferList final : public DOMEventTargetHelper {
// No event is fired and no action is performed on the sourcebuffers.
void ClearSimple();
double HighestStartTime();
double HighestEndTime();
media::TimeUnit HighestStartTime();
media::TimeUnit HighestEndTime();
private:
~SourceBufferList();