import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1215411 - Define MOZ_FALLTHROUGH annotation to suppress clang's -Wimplicit-fallthrough warnings. r=botond (5549d55e8b)
- Bug 1202794 - Explicitly clear array in SortChildrenBy3DZOrder to satisfy the move analysis, r=mattwoodrow (0c53a3db36)
- Bug 1217168 - Respect layer clip rects during plugin visibility computation. r=jimm (6289d35ff1)
- Bug 1220693 - Make mozilla::Atomic<enum class> work even on compilers that don't have <atomic>. r=froydnj (ef1b490ffd)
- Bug 947062 - Make background-position inline-style changes and CSS animations trigger layer activity. r=roc (44bc576960)
- Bug 1201330 - Refactor LayerActivity property management. r=roc (92528b8765)
- Bug 1201330 - Keep scroll handler induced layer activity active until the scroll frame becomes inactive. r=roc (08670902ec)
- Bug 1147707 - Intersect correctly in DisplayItemClip::ApplyNonRoundedIntersection. r=roc (66991b6be6)
- better attempt at 10.5 compaitbility, avoiding out-of-bounds array access (18f481ff6b)
- Bug 1217662 - part 1 - make LayerManagerUserDataDestroy a static function of LayerManager; r=mattwoodrow (f2d34451e0)
- Bug 1217662 - part 2 - move mozilla::layers::LayerUserData to a separate header; r=mattwoodrow (036d7327fa)
- Bug 1217662 - part 3 - move nsDisplayBlendContainer::GetLayerState out-of-line; r=mattwoodrow (fb2bd6bd20)
- Bug 1217662 - part 4 - move {LayerManager,Layer}::RemoveUserData out-of-line; r=mattwoodrow (86836f2a9b)
- Bug 1217662 - part 5 - move FrameLayerBuilder and helper classes's ctors/dtors out-of-line; r=mattwoodrow (e838bde0ec)
- Bug 1217662 - part 6 - remove Layers.h #include from FrameListBuilder.h; r=mattwoodrow (8aea4cb842)
- Bug 1216288 - Disable warning when we don't build an active layer for RenderFrameParent within an opacity:0 subtree. r=roc (2c5e70760a)
- Bug 1169996 - Don't lose eEditorMailMask; r=ehsan (b4647557bb)
- Bug 1211654 - Force opacity layers that were only created for APZ hit-testing information to always be inactive. r=mstange (4c56d440cf)
- Bug 1154336 - Convert nsTextEditorState::mRestoringSelection into a strong reference; r=baku (3e24a6ad18)
- Bug 549674 part.1 Commit composition string at setting value of <input> or <textarea> r=smaug (e6a6471370)
- Bug 1109410 Resolve CSS transform in ContentEventHandler::ConvertToRootViewRelativeOffset() r=roc (5ff3388f28)
- Bug 1180589 part 3 - Rename shadowed variable name; r=bholley (7194cda020)
- Bug 1222145 - Bump maximum video size to 8k. r=jya (056778dda9)
- Bug 1180589 part 1 - Add simulator code for TV Manager API; r=seanlin (dd78a38a27)
- Bug 1180589 part 2 - Add code to create a simulated mediastream; r=se# (c255e0dd07)
- spacing (856ee42504)
- missing bit of Bug 1136827 - Stop synchronously (befed33dbe)
- bit of Bug 1204401 (c1f98ed982)
- bits of 1142527 (dc39662797)
- Bug 1212220 - cache pref values so they are safe to access off the main thread. r=roc. (adb186836b)
- Bug 1194918 - Add VideoSink which contains either AudioSinkWrapper or DecodedStreamSink as a default operating MediaSink in MDSM. r=jwwang. (7ccda9b055)
- Bug 1194918 - Move av-sync and video frame rendering logic from MDSM to VideoSink. r=jwwang. (ba56ae120b)
- Bug 1202533 - Fix naming convention of MediaSink::PlaybackParams. (eed5ed3839)
- Bug 1194918 - Override function SetVolume/SetPreservesPitch in VideoSink for the contained AudioSink. r=jwwang. (0d96e6a395)
- Bug 1198663. Skip null Images in VideoSink::RenderVideoFrames instead of treating them as valid. r=jwwang (aaac235c1f)
- Bug 1207198: P2. Defer dormant request while ReadMetadata is pending in MDSM. r=sotaro (0a8e1f4bb0)
- Bug 1209850: Only attempt to initialize decoders as they are required. r=alfredo (615e41b66b)
- Bug 1192733: fix the MediaFormatReader can not back from dormant state. r=jya (c266107d33)
- Bug 1207198: P1. Do not initialize decoders during ReadMetadata. r=cpearce (4174dbc409)
- missing bit of 1196696 (7b1c0fbe95)
- Bug 1208922. Part 6 - IsWaitingOnCDMResource() is not used by MDSM anymore. Remove it from MediaDecoderReader and make it private in MediaFormatReader. r=cpearce. (db67939710)
- adapted of Bug 1208922. Part 3 - forward the CDMProxy from MediaDecoder (a5dca2f89d)
This commit is contained in:
2022-12-09 12:38:41 +08:00
parent a56e853455
commit 886c0a2723
61 changed files with 2540 additions and 591 deletions
+140 -149
View File
@@ -1,3 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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
@@ -16,6 +17,7 @@
#include "mediasink/DecodedAudioDataSink.h"
#include "mediasink/AudioSinkWrapper.h"
#include "mediasink/VideoSink.h"
#include "mediasink/DecodedStream.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Logging.h"
@@ -136,7 +138,7 @@ static_assert(LOW_DATA_THRESHOLD_USECS > AMPLE_AUDIO_USECS,
} // namespace detail
// Amount of excess usecs of data to add in to the "should we buffer" calculation.
static const uint32_t EXHAUSTED_DATA_MARGIN_USECS = 60000;
static const uint32_t EXHAUSTED_DATA_MARGIN_USECS = 100000;
// If we enter buffering within QUICK_BUFFER_THRESHOLD_USECS seconds of starting
// decoding, we'll enter "quick buffering" mode, which exits a lot sooner than
@@ -180,6 +182,20 @@ static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE;
static uint32_t sVideoQueueSendToCompositorSize = VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE;
static void InitVideoQueuePrefs() {
MOZ_ASSERT(NS_IsMainThread());
static bool sPrefInit = false;
if (!sPrefInit) {
sPrefInit = true;
sVideoQueueDefaultSize = Preferences::GetUint(
"media.video-queue.default-size", MAX_VIDEO_QUEUE_SIZE);
sVideoQueueHWAccelSize = Preferences::GetUint(
"media.video-queue.hw-accel-size", MIN_VIDEO_QUEUE_SIZE);
sVideoQueueSendToCompositorSize = Preferences::GetUint(
"media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
}
}
MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderReader* aReader,
bool aRealTime) :
@@ -187,7 +203,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true)),
mWatchManager(this, mTaskQueue),
mProducerID(ImageContainer::AllocateProducerID()),
mRealTime(aRealTime),
mDispatchedStateMachine(false),
mDelayedScheduler(mTaskQueue),
@@ -197,7 +212,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mFragmentEndTime(-1),
mReader(aReader),
mDecodedAudioEndTime(-1),
mVideoFrameEndTime(-1),
mDecodedVideoEndTime(-1),
mPlaybackRate(1.0),
mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS),
@@ -269,19 +283,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::InitializationTask);
mTaskQueue->Dispatch(r.forget());
static bool sPrefCacheInit = false;
if (!sPrefCacheInit) {
sPrefCacheInit = true;
Preferences::AddUintVarCache(&sVideoQueueDefaultSize,
"media.video-queue.default-size",
MAX_VIDEO_QUEUE_SIZE);
Preferences::AddUintVarCache(&sVideoQueueHWAccelSize,
"media.video-queue.hw-accel-size",
MIN_VIDEO_QUEUE_SIZE);
Preferences::AddUintVarCache(&sVideoQueueSendToCompositorSize,
"media.video-queue.send-to-compositor-size",
VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
}
InitVideoQueuePrefs();
mBufferingWait = IsRealTime() ? 0 : 15;
mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
@@ -302,7 +304,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
mMediaSink = CreateAudioSink();
mMediaSink = CreateMediaSink(mAudioCaptured);
}
MediaDecoderStateMachine::~MediaDecoderStateMachine()
@@ -371,6 +373,23 @@ MediaDecoderStateMachine::CreateAudioSink()
return new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
}
already_AddRefed<media::MediaSink>
MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured)
{
// TODO: We can't really create a new DecodedStream until OutputStreamManager
// is extracted. It is tricky that the implementation of DecodedStream
// happens to allow reuse after shutdown without creating a new one.
RefPtr<media::MediaSink> audioSink = aAudioCaptured ?
mStreamSink : CreateAudioSink();
RefPtr<media::MediaSink> mediaSink =
new VideoSink(mTaskQueue, audioSink, mVideoQueue,
mDecoder->GetVideoFrameContainer(), mRealTime,
mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS,
sVideoQueueSendToCompositorSize);
return mediaSink.forget();
}
bool MediaDecoderStateMachine::HasFutureAudio()
{
MOZ_ASSERT(OnTaskQueue());
@@ -1037,7 +1056,6 @@ void MediaDecoderStateMachine::StopPlayback()
mDecoder->DispatchPlaybackStopped();
if (IsPlaying()) {
RenderVideoFrames(1);
mMediaSink->SetPlaying(false);
MOZ_ASSERT(!IsPlaying());
}
@@ -1218,6 +1236,18 @@ MediaDecoderStateMachine::SetDormant(bool aDormant)
return;
}
if (mMetadataRequest.Exists()) {
if (mPendingDormant && mPendingDormant.ref() != aDormant && !aDormant) {
// We already have a dormant request pending; the new request would have
// resumed from dormant, we can just cancel any pending dormant requests.
mPendingDormant.reset();
} else {
mPendingDormant = Some(aDormant);
}
return;
}
mPendingDormant.reset();
DECODER_LOG("SetDormant=%d", aDormant);
if (aDormant) {
@@ -1247,6 +1277,9 @@ MediaDecoderStateMachine::SetDormant(bool aDormant)
mPendingSeek.RejectIfExists(__func__);
mCurrentSeek.RejectIfExists(__func__);
SetState(DECODER_STATE_DORMANT);
if (IsPlaying()) {
StopPlayback();
}
Reset();
@@ -1453,7 +1486,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget)
DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.mTime);
SetState(DECODER_STATE_SEEKING);
ScheduleStateMachine();
return mPendingSeek.mPromise.Ensure(__func__);
}
@@ -1470,7 +1503,8 @@ void MediaDecoderStateMachine::StopMediaSink()
if (mMediaSink->IsStarted()) {
DECODER_LOG("Stop MediaSink");
mMediaSink->Stop();
mMediaSinkPromise.DisconnectIfExists();
mMediaSinkAudioPromise.DisconnectIfExists();
mMediaSinkVideoPromise.DisconnectIfExists();
}
}
@@ -1745,12 +1779,20 @@ MediaDecoderStateMachine::StartMediaSink()
mAudioCompleted = false;
mMediaSink->Start(GetMediaTime(), mInfo);
auto promise = mMediaSink->OnEnded(TrackInfo::kAudioTrack);
if (promise) {
mMediaSinkPromise.Begin(promise->Then(
auto videoPromise = mMediaSink->OnEnded(TrackInfo::kVideoTrack);
auto audioPromise = mMediaSink->OnEnded(TrackInfo::kAudioTrack);
if (audioPromise) {
mMediaSinkAudioPromise.Begin(audioPromise->Then(
OwnerThread(), __func__, this,
&MediaDecoderStateMachine::OnMediaSinkComplete,
&MediaDecoderStateMachine::OnMediaSinkError));
&MediaDecoderStateMachine::OnMediaSinkAudioComplete,
&MediaDecoderStateMachine::OnMediaSinkAudioError));
}
if (videoPromise) {
mMediaSinkVideoPromise.Begin(videoPromise->Then(
OwnerThread(), __func__, this,
&MediaDecoderStateMachine::OnMediaSinkVideoComplete,
&MediaDecoderStateMachine::OnMediaSinkVideoError));
}
}
}
@@ -1819,7 +1861,7 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
int64_t endOfDecodedVideoData = INT64_MAX;
if (HasVideo() && !VideoQueue().AtEndOfStream()) {
endOfDecodedVideoData = VideoQueue().Peek() ? VideoQueue().Peek()->GetEndTime() : mVideoFrameEndTime;
endOfDecodedVideoData = VideoQueue().Peek() ? VideoQueue().Peek()->GetEndTime() : VideoEndTime();
}
int64_t endOfDecodedAudioData = INT64_MAX;
if (HasAudio() && !AudioQueue().AtEndOfStream()) {
@@ -1868,6 +1910,11 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
mMetadataRequest.Complete();
if (mPendingDormant) {
SetDormant(mPendingDormant.ref());
return;
}
// Set mode to PLAYBACK after reading metadata.
mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
mDecoder->DispatchSetMediaSeekable(mReader->IsMediaSeekable());
@@ -1917,12 +1964,14 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
// feeding in the CDM, which we need to decode the first frame (and
// thus get the metadata). We could fix this if we could compute the start
// time by demuxing without necessaring decoding.
mNotifyMetadataBeforeFirstFrame = mDuration.Ref().isSome() || mReader->IsWaitingOnCDMResource();
bool waitingForCDM =
false;
mNotifyMetadataBeforeFirstFrame = mDuration.Ref().isSome() || waitingForCDM;
if (mNotifyMetadataBeforeFirstFrame) {
EnqueueLoadedMetadataEvent();
}
if (mReader->IsWaitingOnCDMResource()) {
if (waitingForCDM) {
// Metadata parsing was successful but we're still waiting for CDM caps
// to become available so that we can build the correct decryptor/decoder.
SetState(DECODER_STATE_WAIT_FOR_CDM);
@@ -2013,7 +2062,7 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame()
DECODER_LOG("FinishDecodeFirstFrame");
if (!IsRealTime() && !mSentFirstFrameLoadedEvent) {
RenderVideoFrames(1);
mMediaSink->Redraw();
}
// If we don't know the duration by this point, we assume infinity, per spec.
@@ -2101,7 +2150,6 @@ MediaDecoderStateMachine::SeekCompleted()
// Try to decode another frame to detect if we're at the end...
DECODER_LOG("Seek completed, mCurrentPosition=%lld", mCurrentPosition.Ref());
// Reset quick buffering status. This ensures that if we began the
// seek while quick-buffering, we won't bypass quick buffering mode
// if we need to buffer after the seek.
@@ -2111,7 +2159,7 @@ MediaDecoderStateMachine::SeekCompleted()
ScheduleStateMachine();
if (video) {
RenderVideoFrames(1);
mMediaSink->Redraw();
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
AbstractThread::MainThread()->Dispatch(event.forget());
@@ -2124,6 +2172,7 @@ public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderDisposer)
DecoderDisposer(MediaDecoder* aDecoder, MediaDecoderStateMachine* aStateMachine)
: mDecoder(aDecoder), mStateMachine(aStateMachine) {}
void OnTaskQueueShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
@@ -2266,7 +2315,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Start playback if necessary so that the clock can be properly queried.
MaybeStartPlayback();
UpdateRenderedVideoFrames();
UpdatePlaybackPositionPeriodically();
NS_ASSERTION(!IsPlaying() ||
mLogicallySeeking ||
IsStateMachineScheduled(),
@@ -2337,7 +2386,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
{
// Start playback if necessary to play the remaining media.
MaybeStartPlayback();
UpdateRenderedVideoFrames();
UpdatePlaybackPositionPeriodically();
NS_ASSERTION(!IsPlaying() ||
mLogicallySeeking ||
IsStateMachineScheduled(),
@@ -2359,7 +2408,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
!mSentPlaybackEndedEvent)
{
int64_t clockTime = std::max(AudioEndTime(), mVideoFrameEndTime);
int64_t clockTime = std::max(AudioEndTime(), VideoEndTime());
clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
UpdatePlaybackPosition(clockTime);
@@ -2390,8 +2439,8 @@ MediaDecoderStateMachine::Reset()
// dormant state. We could also be in the process of going dormant, and have
// just switched to exiting dormant before we finished entering dormant,
// hence the DECODING_NONE case below.
MOZ_ASSERT(mState == DECODER_STATE_SEEKING ||
mState == DECODER_STATE_SHUTDOWN ||
MOZ_ASSERT(IsShutdown() ||
mState == DECODER_STATE_SEEKING ||
mState == DECODER_STATE_DORMANT ||
mState == DECODER_STATE_DECODING_NONE);
@@ -2400,7 +2449,6 @@ MediaDecoderStateMachine::Reset()
// crash for no samples to be popped.
StopMediaSink();
mVideoFrameEndTime = -1;
mDecodedVideoEndTime = -1;
mDecodedAudioEndTime = -1;
mAudioCompleted = false;
@@ -2452,70 +2500,6 @@ MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData)
}
}
void MediaDecoderStateMachine::RenderVideoFrames(int32_t aMaxFrames,
int64_t aClockTime,
const TimeStamp& aClockTimeStamp)
{
MOZ_ASSERT(OnTaskQueue());
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
nsAutoTArray<RefPtr<MediaData>,16> frames;
VideoQueue().GetFirstElements(aMaxFrames, &frames);
if (frames.IsEmpty() || !container) {
return;
}
nsAutoTArray<ImageContainer::NonOwningImage,16> images;
TimeStamp lastFrameTime;
for (uint32_t i = 0; i < frames.Length(); ++i) {
VideoData* frame = frames[i]->As<VideoData>();
bool valid = !frame->mImage || frame->mImage->IsValid();
frame->mSentToCompositor = true;
if (!valid) {
continue;
}
int64_t frameTime = frame->mTime;
if (frameTime < 0) {
// Frame times before the start time are invalid; drop such frames
continue;
}
TimeStamp t;
if (aMaxFrames > 1) {
MOZ_ASSERT(!aClockTimeStamp.IsNull());
int64_t delta = frame->mTime - aClockTime;
t = aClockTimeStamp +
TimeDuration::FromMicroseconds(delta / mPlaybackRate);
if (!lastFrameTime.IsNull() && t <= lastFrameTime) {
// Timestamps out of order; drop the new frame. In theory we should
// probably replace the previous frame with the new frame if the
// timestamps are equal, but this is a corrupt video file already so
// never mind.
continue;
}
lastFrameTime = t;
}
ImageContainer::NonOwningImage* img = images.AppendElement();
img->mTimeStamp = t;
img->mImage = frame->mImage;
img->mFrameID = frame->mFrameID;
img->mProducerID = mProducerID;
VERBOSE_LOG("playing video frame %lld (id=%x) (queued=%i, state-machine=%i, decoder-queued=%i)",
frame->mTime, frame->mFrameID,
VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
}
container->SetCurrentFrames(frames[0]->As<VideoData>()->mDisplay, images);
}
int64_t
MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const
{
@@ -2525,7 +2509,8 @@ MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const
return clockTime;
}
void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
void
MediaDecoderStateMachine::UpdatePlaybackPositionPeriodically()
{
MOZ_ASSERT(OnTaskQueue());
@@ -2537,47 +2522,21 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
DiscardStreamData();
}
TimeStamp nowTime;
const int64_t clockTime = GetClock(&nowTime);
// Skip frames up to the frame at the playback position, and figure out
// the time remaining until it's time to display the next frame and drop
// the current frame.
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
int64_t remainingTime = AUDIO_DURATION_USECS;
if (VideoQueue().GetSize() > 0) {
RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
int32_t framesRemoved = 0;
while (VideoQueue().GetSize() > 0) {
MediaData* nextFrame = VideoQueue().PeekFront();
if (!IsRealTime() && nextFrame->mTime > clockTime) {
remainingTime = nextFrame->mTime - clockTime;
break;
}
++framesRemoved;
if (!currentFrame->As<VideoData>()->mSentToCompositor) {
mDecoder->NotifyDecodedFrames(0, 0, 1);
VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld",
currentFrame->mTime, clockTime);
}
currentFrame = VideoQueue().PopFront();
}
VideoQueue().PushFront(currentFrame);
if (framesRemoved > 0) {
mVideoFrameEndTime = currentFrame->GetEndTime();
FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
frameStats.NotifyPresentedFrame();
}
}
RenderVideoFrames(sVideoQueueSendToCompositorSize, clockTime, nowTime);
// Cap the current time to the larger of the audio and video end time.
// This ensures that if we're running off the system clock, we don't
// advance the clock to after the media end time.
if (mVideoFrameEndTime != -1 || AudioEndTime() != -1) {
if (VideoEndTime() != -1 || AudioEndTime() != -1) {
const int64_t clockTime = GetClock();
// Skip frames up to the frame at the playback position, and figure out
// the time remaining until it's time to display the next frame and drop
// the current frame.
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
// These will be non -1 if we've displayed a video frame, or played an audio frame.
int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, AudioEndTime()));
int64_t t = std::min(clockTime, std::max(VideoEndTime(), AudioEndTime()));
// FIXME: Bug 1091422 - chained ogg files hit this assertion.
//MOZ_ASSERT(t >= GetMediaTime());
if (t > GetMediaTime()) {
UpdatePlaybackPosition(t);
}
@@ -2587,7 +2546,7 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
// the monitor and get a staled value from GetCurrentTimeUs() which hits the
// assertion in GetClock().
int64_t delay = std::max<int64_t>(1, remainingTime / mPlaybackRate);
int64_t delay = std::max<int64_t>(1, AUDIO_DURATION_USECS / mPlaybackRate);
ScheduleStateMachineIn(delay);
}
@@ -2669,7 +2628,7 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(MediaData* aSample)
NS_ASSERTION(mCurrentSeek.mTarget.mTime < audio->mTime + sampleDuration.value(),
"Data must end after target.");
CheckedInt64 framesToPrune =
CheckedInt64 framesToPrune =
UsecsToFrames(mCurrentSeek.mTarget.mTime - audio->mTime, mInfo.mAudio.mRate);
if (!framesToPrune.isValid()) {
return NS_ERROR_FAILURE;
@@ -2890,22 +2849,57 @@ MediaDecoderStateMachine::AudioEndTime() const
return -1;
}
void MediaDecoderStateMachine::OnMediaSinkComplete()
int64_t
MediaDecoderStateMachine::VideoEndTime() const
{
MOZ_ASSERT(OnTaskQueue());
if (mMediaSink->IsStarted()) {
return mMediaSink->GetEndTime(TrackInfo::kVideoTrack);
}
return -1;
}
mMediaSinkPromise.Complete();
void
MediaDecoderStateMachine::OnMediaSinkVideoComplete()
{
MOZ_ASSERT(OnTaskQueue());
VERBOSE_LOG("[%s]", __func__);
mMediaSinkVideoPromise.Complete();
ScheduleStateMachine();
}
void
MediaDecoderStateMachine::OnMediaSinkVideoError()
{
MOZ_ASSERT(OnTaskQueue());
VERBOSE_LOG("[%s]", __func__);
mMediaSinkVideoPromise.Complete();
if (HasAudio()) {
return;
}
DecodeError();
}
void MediaDecoderStateMachine::OnMediaSinkAudioComplete()
{
MOZ_ASSERT(OnTaskQueue());
VERBOSE_LOG("[%s]", __func__);
mMediaSinkAudioPromise.Complete();
// Set true only when we have audio.
mAudioCompleted = mInfo.HasAudio();
// To notify PlaybackEnded as soon as possible.
ScheduleStateMachine();
}
void MediaDecoderStateMachine::OnMediaSinkError()
void MediaDecoderStateMachine::OnMediaSinkAudioError()
{
MOZ_ASSERT(OnTaskQueue());
VERBOSE_LOG("[%s]", __func__);
mMediaSinkPromise.Complete();
mMediaSinkAudioPromise.Complete();
// Set true only when we have audio.
mAudioCompleted = mInfo.HasAudio();
@@ -2936,10 +2930,7 @@ MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
mMediaSink->Shutdown();
// Create a new sink according to whether audio is captured.
// TODO: We can't really create a new DecodedStream until OutputStreamManager
// is extracted. It is tricky that the implementation of DecodedStream
// happens to allow reuse after shutdown without creating a new one.
mMediaSink = aCaptured ? mStreamSink : CreateAudioSink();
mMediaSink = CreateMediaSink(aCaptured);
// Restore playback parameters.
mMediaSink->SetPlaybackParams(params);