mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user