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:
- put back VideoIsHardwareAccelerated (1ccd6a84d)
- Bug 1181204 - Prevent use of the decoder outside the reader's taskqueue. r=cpearce (3e85e4af1)
- Bug 1189173 - Drop frames aggressively during internal seek. r=jya (93be063d3)
- Bug 1179909: Build fix. r=me CLOSED TREE (5d35a65d5)
- part of Bug 1160321 - Test whether we can create H.264/AAC decoders before we report we support them. r=mattwoodrow (4efd2b497)
- Bug 1181439: Include init segment range when calling NotifyDataArrived. r=cpearce (fc3406016)
- Bug 1182933: Implement MediaReadAt. r=bholley (c285d05da)
- Bug 1179499 - Assert NS_IsMainThread on a bunch of MediaDecoder methods. r=jww (e6f0f3545)
- Bug 1179110 - Use a Maybe<> to store start time, rather than using -1 as a sentinel. r=jya (9e24b0223)
- bug 1161903 reset mDrainComplete after Flush() as DrainComplete() may be called before Flush() r=mattwoodrow (e49348af4)
- Bug 1171257 - Add force decode ahead to MediaFormatReader r=jya,bholley (5da27853d)
- Bug 1165825 - 1. Release the buffer which contains INFO_FORMAT_CHANGED. 2. Re-trigger the decode task if we still hold a promise. r=sotaro (f0d6c644f)
- Bug 1168778 - Fix crash when seeking: 1. Replace FlushableMediaTaskQueue by MediaTaskQueue. 2. Refact the seek/DecodeAudioDataTask/DecodeVideoFrameTask functions. r=sotaro (6341c41cb)
- put back mEOSVideoCompensation (d3cd0e1e8)
- Bug 1172390 - Align stream blocking of decoded stream with play state of MDSM. r=roc. (f44435942)
- Bug 1179665. Part 1 - move code that remove finished streams into DecodedStream so we don't need to expose GetReentrantMonitor() to the public and it is easier to remove it in the future. r=roc. (78c11a628)
- Bug 1179665. Part 2 - clean up mStreamStartTime since start time of MDSM is now always 0. r=roc. (92c38b802)
- partial Bug 1140995 - Part 1 - At end of stream, send the last video frame to decoded stream with deviation usec if the last video frame's duration is 0. r=jwwang Bug 1140995 - Part 2: Don't send the audio/video data when the EOS flag is raised because the decoded data is invalid. r=cpearce (ec5a3096b)
- Bug 1179665. Part 3 - move code about sending stream data into DecodedStream. r=roc. (4cd542c56)
- fix space and patch order issues (b244ccd80)
- fix space and patch order issues (b47f0d460)
- Bug 1178718. Part 1 - Remove dependency on decoder monitor from DecodedStream. r=roc. (a92b7dcf4)
- Bug 1178718. Part 2 - No need to acquire the monitor in BreakCycles(). r=roc. (3bb146c76)
- Bug 1179665. Part 4 - remove MDSM::RecreateDecodedStream() which runs on the main thread and doesn't fit into the thread model of MDSM. r=roc. (f68c6c17e)
- Bug 1143575. Make GetClock return a TimeStamp as well as the stream time. r=cpearce (f216a2ed2)
- Bug 1143575. ScheduleStateMachine when the playback rate changes, so we can update the rendered frame queue. r=cpearce (498bf78cf)
- Bug 1143575. Rename clock_time to clockTime. r=cpearce (2d4267233)
- Bug 1143575. Keep currently-rendered frame at the front of the video queue. r=cpearce (6984d2ebc)
- Bug 1143575. Add frame IDs to VideoData. r=cpearce (d44122b91)
- Bug 1179499 - Dispatch NotifyPlayback{Started,Stopped}. r=jww (692af1608)
- Bug 1143575. Remove ClearAllImagesExceptFront because it doesn't do anything. r=nical (410ed1a6a)
- Bug 1143575. Clarify code by renaming method to ClearCurrentImageFromImageBridge. r=nical (97b431685)
- Bug 1143575. Make LayerTreeInvalidation invalidate when an ImageLayerComposite's current frame has changed. r=mattwoodrow (f6cf46087)
- Bug 1143575. Exit composition early if nothing is invalid. r=mattwoodrow (ded3bdf5d)
- Bug 1143575. Fix typo in ImageContainer comment. r=nical (2a372a0d3)
- Bug 1143575. Pass a list of timestamped images to ImageContainer::SetCurrentImages. r=nical (e3fec59dd)
- Bug 1143575. Implement ImageContainer::GetPaintDelay. r=nical (987f5c584)
- Bug 1143575. Implement ImageContainer::GetDroppedCount. r=nical (2e8e19731)
This commit is contained in:
@@ -45,7 +45,6 @@
|
||||
namespace mozilla {
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::media;
|
||||
|
||||
@@ -100,7 +99,7 @@ const int64_t NO_VIDEO_AMPLE_AUDIO_DIVISOR = 8;
|
||||
// If we have fewer than LOW_VIDEO_FRAMES decoded frames, and
|
||||
// we're not "prerolling video", we'll skip the video up to the next keyframe
|
||||
// which is at or after the current playback position.
|
||||
static const uint32_t LOW_VIDEO_FRAMES = 1;
|
||||
static const uint32_t LOW_VIDEO_FRAMES = 2;
|
||||
|
||||
// Threshold in usecs that used to check if we are low on decoded video.
|
||||
// If the last video frame's end time |mDecodedVideoEndTime| is more than
|
||||
@@ -171,6 +170,7 @@ static int64_t DurationToUsecs(TimeDuration aDuration) {
|
||||
|
||||
static const uint32_t MIN_VIDEO_QUEUE_SIZE = 3;
|
||||
static const uint32_t MAX_VIDEO_QUEUE_SIZE = 10;
|
||||
static const uint32_t SCARCE_VIDEO_QUEUE_SIZE = 1;
|
||||
|
||||
static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
|
||||
static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE;
|
||||
@@ -240,7 +240,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mSentLoadedMetadataEvent(false),
|
||||
mSentFirstFrameLoadedEvent(false),
|
||||
mSentPlaybackEndedEvent(false),
|
||||
mDecodedStream(mDecoder->GetReentrantMonitor())
|
||||
mDecodedStream(new DecodedStream())
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
@@ -321,8 +321,6 @@ MediaDecoderStateMachine::InitializationTask()
|
||||
mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
|
||||
mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
|
||||
mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
|
||||
mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
|
||||
mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::HasFutureAudio()
|
||||
@@ -346,7 +344,7 @@ bool MediaDecoderStateMachine::HaveNextFrameData()
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
return (!HasAudio() || HasFutureAudio()) &&
|
||||
(!HasVideo() || VideoQueue().GetSize() > 0);
|
||||
(!HasVideo() || VideoQueue().GetSize() > 1);
|
||||
}
|
||||
|
||||
int64_t MediaDecoderStateMachine::GetDecodedAudioDuration()
|
||||
@@ -360,227 +358,21 @@ int64_t MediaDecoderStateMachine::GetDecodedAudioDuration()
|
||||
return audioDecoded;
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
|
||||
DecodedStreamData* aStream,
|
||||
AudioSegment* aOutput)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
// This logic has to mimic AudioSink closely to make sure we write
|
||||
// the exact same silences
|
||||
CheckedInt64 audioWrittenOffset = aStream->mAudioFramesWritten +
|
||||
UsecsToFrames(mInfo.mAudio.mRate, mStreamStartTime);
|
||||
CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime);
|
||||
|
||||
if (!audioWrittenOffset.isValid() ||
|
||||
!frameOffset.isValid() ||
|
||||
// ignore packet that we've already processed
|
||||
frameOffset.value() + aAudio->mFrames <= audioWrittenOffset.value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioWrittenOffset.value() < frameOffset.value()) {
|
||||
int64_t silentFrames = frameOffset.value() - audioWrittenOffset.value();
|
||||
// Write silence to catch up
|
||||
VERBOSE_LOG("writing %lld frames of silence to MediaStream", silentFrames);
|
||||
AudioSegment silence;
|
||||
silence.InsertNullDataAtStart(silentFrames);
|
||||
aStream->mAudioFramesWritten += silentFrames;
|
||||
audioWrittenOffset += silentFrames;
|
||||
aOutput->AppendFrom(&silence);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(audioWrittenOffset.value() >= frameOffset.value());
|
||||
|
||||
int64_t offset = audioWrittenOffset.value() - frameOffset.value();
|
||||
size_t framesToWrite = aAudio->mFrames - offset;
|
||||
|
||||
aAudio->EnsureAudioBuffer();
|
||||
nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
|
||||
AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
|
||||
nsAutoTArray<const AudioDataValue*,2> channels;
|
||||
for (uint32_t i = 0; i < aAudio->mChannels; ++i) {
|
||||
channels.AppendElement(bufferData + i*aAudio->mFrames + offset);
|
||||
}
|
||||
aOutput->AppendFrames(buffer.forget(), channels, framesToWrite);
|
||||
VERBOSE_LOG("writing %u frames of data to MediaStream for AudioData at %lld",
|
||||
static_cast<unsigned>(framesToWrite),
|
||||
aAudio->mTime);
|
||||
aStream->mAudioFramesWritten += framesToWrite;
|
||||
aOutput->ApplyVolume(mVolume);
|
||||
|
||||
aStream->mNextAudioTime = aAudio->GetEndTime();
|
||||
}
|
||||
|
||||
static void WriteVideoToMediaStream(MediaStream* aStream,
|
||||
layers::Image* aImage,
|
||||
int64_t aEndMicroseconds,
|
||||
int64_t aStartMicroseconds,
|
||||
const IntSize& aIntrinsicSize,
|
||||
VideoSegment* aOutput)
|
||||
{
|
||||
nsRefPtr<layers::Image> image = aImage;
|
||||
StreamTime duration =
|
||||
aStream->MicrosecondsToStreamTimeRoundDown(aEndMicroseconds) -
|
||||
aStream->MicrosecondsToStreamTimeRoundDown(aStartMicroseconds);
|
||||
aOutput->AppendFrame(image.forget(), duration, aIntrinsicSize);
|
||||
}
|
||||
|
||||
static void
|
||||
UpdateStreamBlocking(MediaStream* aStream, bool aBlocking)
|
||||
{
|
||||
int32_t delta = aBlocking ? 1 : -1;
|
||||
if (NS_IsMainThread()) {
|
||||
aStream->ChangeExplicitBlockerCount(delta);
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<int32_t>(
|
||||
aStream, &MediaStream::ChangeExplicitBlockerCount, delta);
|
||||
AbstractThread::MainThread()->Dispatch(r.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::SendStreamData()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
|
||||
MOZ_ASSERT(mStreamStartTime != -1);
|
||||
|
||||
DecodedStreamData* stream = GetDecodedStream();
|
||||
bool finished = mDecodedStream->SendData(
|
||||
mStreamStartTime, mInfo, AudioQueue(), VideoQueue(),
|
||||
mVolume, mDecoder->IsSameOriginMedia());
|
||||
|
||||
bool finished =
|
||||
(!mInfo.HasAudio() || AudioQueue().IsFinished()) &&
|
||||
(!mInfo.HasVideo() || VideoQueue().IsFinished());
|
||||
|
||||
{
|
||||
SourceMediaStream* mediaStream = stream->mStream;
|
||||
StreamTime endPosition = 0;
|
||||
const bool isSameOrigin = mDecoder->IsSameOriginMedia();
|
||||
|
||||
if (!stream->mStreamInitialized) {
|
||||
if (mInfo.HasAudio()) {
|
||||
TrackID audioTrackId = mInfo.mAudio.mTrackId;
|
||||
AudioSegment* audio = new AudioSegment();
|
||||
mediaStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
|
||||
SourceMediaStream::ADDTRACK_QUEUED);
|
||||
stream->mNextAudioTime = mStreamStartTime;
|
||||
}
|
||||
if (mInfo.HasVideo()) {
|
||||
TrackID videoTrackId = mInfo.mVideo.mTrackId;
|
||||
VideoSegment* video = new VideoSegment();
|
||||
mediaStream->AddTrack(videoTrackId, 0, video,
|
||||
SourceMediaStream::ADDTRACK_QUEUED);
|
||||
stream->mNextVideoTime = mStreamStartTime;
|
||||
}
|
||||
mediaStream->FinishAddTracks();
|
||||
stream->mStreamInitialized = true;
|
||||
|
||||
// Make sure stream blocking is updated before sending stream data so we
|
||||
// don't 'leak' data when the stream is supposed to be blocked.
|
||||
UpdateStreamBlockingForPlayState();
|
||||
UpdateStreamBlockingForStateMachinePlaying();
|
||||
UpdateStreamBlocking(mediaStream, false);
|
||||
}
|
||||
|
||||
if (mInfo.HasAudio()) {
|
||||
MOZ_ASSERT(stream->mNextAudioTime != -1, "Should've been initialized");
|
||||
TrackID audioTrackId = mInfo.mAudio.mTrackId;
|
||||
nsAutoTArray<nsRefPtr<AudioData>,10> audio;
|
||||
// It's OK to hold references to the AudioData because AudioData
|
||||
// is ref-counted.
|
||||
AudioQueue().GetElementsAfter(stream->mNextAudioTime, &audio);
|
||||
AudioSegment output;
|
||||
for (uint32_t i = 0; i < audio.Length(); ++i) {
|
||||
SendStreamAudio(audio[i], stream, &output);
|
||||
}
|
||||
if (!isSameOrigin) {
|
||||
output.ReplaceWithDisabled();
|
||||
}
|
||||
// |mNextAudioTime| is updated as we process each audio sample in
|
||||
// SendStreamAudio(). This is consistent with how |mNextVideoTime|
|
||||
// is updated for video samples.
|
||||
if (output.GetDuration() > 0) {
|
||||
mediaStream->AppendToTrack(audioTrackId, &output);
|
||||
}
|
||||
if (AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
|
||||
mediaStream->EndTrack(audioTrackId);
|
||||
stream->mHaveSentFinishAudio = true;
|
||||
}
|
||||
endPosition = std::max(endPosition,
|
||||
mediaStream->TicksToTimeRoundDown(mInfo.mAudio.mRate,
|
||||
stream->mAudioFramesWritten));
|
||||
|
||||
CheckedInt64 playedUsecs = mStreamStartTime +
|
||||
FramesToUsecs(stream->mAudioFramesWritten, mInfo.mAudio.mRate);
|
||||
if (playedUsecs.isValid()) {
|
||||
OnAudioEndTimeUpdate(playedUsecs.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (mInfo.HasVideo()) {
|
||||
MOZ_ASSERT(stream->mNextVideoTime != -1, "Should've been initialized");
|
||||
TrackID videoTrackId = mInfo.mVideo.mTrackId;
|
||||
nsAutoTArray<nsRefPtr<VideoData>,10> video;
|
||||
// It's OK to hold references to the VideoData because VideoData
|
||||
// is ref-counted.
|
||||
VideoQueue().GetElementsAfter(stream->mNextVideoTime, &video);
|
||||
VideoSegment output;
|
||||
for (uint32_t i = 0; i < video.Length(); ++i) {
|
||||
VideoData* v = video[i];
|
||||
if (stream->mNextVideoTime < v->mTime) {
|
||||
VERBOSE_LOG("writing last video to MediaStream %p for %lldus",
|
||||
mediaStream, v->mTime - stream->mNextVideoTime);
|
||||
// Write last video frame to catch up. mLastVideoImage can be null here
|
||||
// which is fine, it just means there's no video.
|
||||
|
||||
// TODO: |mLastVideoImage| should come from the last image rendered
|
||||
// by the state machine. This will avoid the black frame when capture
|
||||
// happens in the middle of playback (especially in th middle of a
|
||||
// video frame). E.g. if we have a video frame that is 30 sec long
|
||||
// and capture happens at 15 sec, we'll have to append a black frame
|
||||
// that is 15 sec long.
|
||||
WriteVideoToMediaStream(mediaStream, stream->mLastVideoImage,
|
||||
v->mTime, stream->mNextVideoTime, stream->mLastVideoImageDisplaySize,
|
||||
&output);
|
||||
stream->mNextVideoTime = v->mTime;
|
||||
}
|
||||
if (stream->mNextVideoTime < v->GetEndTime()) {
|
||||
VERBOSE_LOG("writing video frame %lldus to MediaStream %p for %lldus",
|
||||
v->mTime, mediaStream, v->GetEndTime() - stream->mNextVideoTime);
|
||||
WriteVideoToMediaStream(mediaStream, v->mImage,
|
||||
v->GetEndTime(), stream->mNextVideoTime, v->mDisplay,
|
||||
&output);
|
||||
stream->mNextVideoTime = v->GetEndTime();
|
||||
stream->mLastVideoImage = v->mImage;
|
||||
stream->mLastVideoImageDisplaySize = v->mDisplay;
|
||||
} else {
|
||||
VERBOSE_LOG("skipping writing video frame %lldus (end %lldus) to MediaStream",
|
||||
v->mTime, v->GetEndTime());
|
||||
}
|
||||
}
|
||||
if (!isSameOrigin) {
|
||||
output.ReplaceWithDisabled();
|
||||
}
|
||||
if (output.GetDuration() > 0) {
|
||||
mediaStream->AppendToTrack(videoTrackId, &output);
|
||||
}
|
||||
if (VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
|
||||
mediaStream->EndTrack(videoTrackId);
|
||||
stream->mHaveSentFinishVideo = true;
|
||||
}
|
||||
endPosition = std::max(endPosition,
|
||||
mediaStream->MicrosecondsToStreamTimeRoundDown(
|
||||
stream->mNextVideoTime - mStreamStartTime));
|
||||
}
|
||||
|
||||
if (!stream->mHaveSentFinish) {
|
||||
stream->mStream->AdvanceKnownTracksTime(endPosition);
|
||||
}
|
||||
|
||||
if (finished && !stream->mHaveSentFinish) {
|
||||
stream->mHaveSentFinish = true;
|
||||
stream->mStream->Finish();
|
||||
if (mInfo.HasAudio()) {
|
||||
CheckedInt64 playedUsecs = mDecodedStream->AudioEndTime(
|
||||
mStreamStartTime, mInfo.mAudio.mRate);
|
||||
if (playedUsecs.isValid()) {
|
||||
OnAudioEndTimeUpdate(playedUsecs.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,21 +406,8 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
|
||||
GetDecodedAudioDuration() < aAmpleAudioUSecs) {
|
||||
return false;
|
||||
}
|
||||
if (!mAudioCaptured) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DecodedStreamData* stream = GetDecodedStream();
|
||||
|
||||
if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) {
|
||||
MOZ_ASSERT(mInfo.HasAudio());
|
||||
TrackID audioTrackId = mInfo.mAudio.mTrackId;
|
||||
if (!stream->mStream->HaveEnoughBuffered(audioTrackId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return !mAudioCaptured || mDecodedStream->HaveEnoughAudio(mInfo);
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
|
||||
@@ -636,21 +415,11 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
if (static_cast<uint32_t>(VideoQueue().GetSize()) < GetAmpleVideoFrames() * mPlaybackRate) {
|
||||
if (VideoQueue().GetSize() - 1 < GetAmpleVideoFrames() * mPlaybackRate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DecodedStreamData* stream = GetDecodedStream();
|
||||
|
||||
if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) {
|
||||
MOZ_ASSERT(mInfo.HasVideo());
|
||||
TrackID videoTrackId = mInfo.mVideo.mTrackId;
|
||||
if (!stream->mStream->HaveEnoughBuffered(videoTrackId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return !mAudioCaptured || mDecodedStream->HaveEnoughVideo(mInfo);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1262,7 +1031,7 @@ void MediaDecoderStateMachine::StopPlayback()
|
||||
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
mDecoder->NotifyPlaybackStopped();
|
||||
mDecoder->DispatchPlaybackStopped();
|
||||
|
||||
if (IsPlaying()) {
|
||||
mPlayDuration = GetClock();
|
||||
@@ -1272,7 +1041,6 @@ void MediaDecoderStateMachine::StopPlayback()
|
||||
// so it can pause audio playback.
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
|
||||
UpdateStreamBlockingForStateMachinePlaying();
|
||||
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
}
|
||||
@@ -1302,7 +1070,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
|
||||
|
||||
DECODER_LOG("MaybeStartPlayback() starting playback");
|
||||
|
||||
mDecoder->NotifyPlaybackStarted();
|
||||
mDecoder->DispatchPlaybackStarted();
|
||||
SetPlayStartTime(TimeStamp::Now());
|
||||
MOZ_ASSERT(IsPlaying());
|
||||
|
||||
@@ -1310,7 +1078,6 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
UpdateStreamBlockingForStateMachinePlaying();
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
}
|
||||
|
||||
@@ -1790,9 +1557,7 @@ MediaDecoderStateMachine::InitiateSeek()
|
||||
mCurrentSeek.mTarget.mTime = seekTime;
|
||||
|
||||
if (mAudioCaptured) {
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArgs<MediaStreamGraph*>(
|
||||
this, &MediaDecoderStateMachine::RecreateDecodedStream, nullptr);
|
||||
AbstractThread::MainThread()->Dispatch(r.forget());
|
||||
mDecodedStream->RecreateData();
|
||||
}
|
||||
|
||||
mDropAudioUntilNextDiscontinuity = HasAudio();
|
||||
@@ -1928,6 +1693,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
||||
|
||||
bool skipToNextKeyFrame = NeedToSkipToNextKeyframe();
|
||||
int64_t currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
|
||||
bool forceDecodeAhead = static_cast<uint32_t>(VideoQueue().GetSize()) <= SCARCE_VIDEO_QUEUE_SIZE;
|
||||
|
||||
// Time the video decode, so that if it's slow, we can increase our low
|
||||
// audio threshold to reduce the chance of an audio underrun while we're
|
||||
@@ -1940,7 +1706,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
||||
|
||||
mVideoDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData,
|
||||
skipToNextKeyFrame, currentTime)
|
||||
skipToNextKeyFrame, currentTime, forceDecodeAhead)
|
||||
->Then(TaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded));
|
||||
@@ -2258,7 +2024,7 @@ MediaDecoderStateMachine::DecodeFirstFrame()
|
||||
mVideoDecodeStartTime = TimeStamp::Now();
|
||||
mVideoDataRequest.Begin(
|
||||
ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::RequestVideoData, false, int64_t(0))
|
||||
&MediaDecoderReader::RequestVideoData, false, int64_t(0), false)
|
||||
->Then(TaskQueue(), __func__, mStartTimeRendezvous.get(),
|
||||
&StartTimeRendezvous::ProcessFirstSample<VideoDataPromise>,
|
||||
&StartTimeRendezvous::FirstSampleRejected<VideoData>)
|
||||
@@ -2377,7 +2143,7 @@ MediaDecoderStateMachine::SeekCompleted()
|
||||
SetState(DECODER_STATE_SEEKING);
|
||||
} else if (GetMediaTime() == Duration().ToMicroseconds() && !isLiveStream) {
|
||||
// Seeked to end of media, move to COMPLETED state. Note we don't do
|
||||
// this if we're playing a live stream, since the end of media will advance
|
||||
// this when playing a live stream, since the end of media will advance
|
||||
// once we download more data!
|
||||
DECODER_LOG("Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
|
||||
// Explicitly set our state so we don't decode further, and so
|
||||
@@ -2610,9 +2376,9 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
// Play the remaining media. We want to run AdvanceFrame() at least
|
||||
// once to ensure the current playback position is advanced to the
|
||||
// end of the media, and so that we update the readyState.
|
||||
if (VideoQueue().GetSize() > 0 ||
|
||||
if (VideoQueue().GetSize() > 1 ||
|
||||
(HasAudio() && !mAudioCompleted) ||
|
||||
(mAudioCaptured && !GetDecodedStream()->IsFinished()))
|
||||
(mAudioCaptured && !mDecodedStream->IsFinished()))
|
||||
{
|
||||
// Start playback if necessary to play the remaining media.
|
||||
MaybeStartPlayback();
|
||||
@@ -2734,6 +2500,7 @@ void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
|
||||
} else {
|
||||
mCorruptFrames.insert(0);
|
||||
}
|
||||
aData->mSentToCompositor = true;
|
||||
container->SetCurrentFrame(aData->mDisplay, aData->mImage, aTarget);
|
||||
MOZ_ASSERT(container->GetFrameDelay() >= 0 || IsRealTime());
|
||||
}
|
||||
@@ -2766,11 +2533,10 @@ int64_t MediaDecoderStateMachine::GetStreamClock() const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(mStreamStartTime != -1);
|
||||
return mStreamStartTime + GetDecodedStream()->GetPosition();
|
||||
return mStreamStartTime + mDecodedStream->GetPosition();
|
||||
}
|
||||
|
||||
int64_t MediaDecoderStateMachine::GetVideoStreamPosition() const
|
||||
int64_t MediaDecoderStateMachine::GetVideoStreamPosition(TimeStamp aTimeStamp) const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
@@ -2780,13 +2546,13 @@ int64_t MediaDecoderStateMachine::GetVideoStreamPosition() const
|
||||
}
|
||||
|
||||
// Time elapsed since we started playing.
|
||||
int64_t delta = DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
|
||||
int64_t delta = DurationToUsecs(aTimeStamp - mPlayStartTime);
|
||||
// Take playback rate into account.
|
||||
delta *= mPlaybackRate;
|
||||
return mPlayDuration + delta;
|
||||
}
|
||||
|
||||
int64_t MediaDecoderStateMachine::GetClock() const
|
||||
int64_t MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
@@ -2796,6 +2562,7 @@ int64_t MediaDecoderStateMachine::GetClock() const
|
||||
// audio, or don't have audio, use the system clock. If our output is being
|
||||
// fed to a MediaStream, use that stream as the source of the clock.
|
||||
int64_t clock_time = -1;
|
||||
TimeStamp t;
|
||||
if (!IsPlaying()) {
|
||||
clock_time = mPlayDuration;
|
||||
} else {
|
||||
@@ -2804,11 +2571,15 @@ int64_t MediaDecoderStateMachine::GetClock() const
|
||||
} else if (HasAudio() && !mAudioCompleted) {
|
||||
clock_time = GetAudioClock();
|
||||
} else {
|
||||
t = TimeStamp::Now();
|
||||
// Audio is disabled on this system. Sync to the system clock.
|
||||
clock_time = GetVideoStreamPosition();
|
||||
clock_time = GetVideoStreamPosition(t);
|
||||
}
|
||||
NS_ASSERTION(GetMediaTime() <= clock_time, "Clock should go forwards.");
|
||||
}
|
||||
if (aTimeStamp) {
|
||||
*aTimeStamp = t.IsNull() ? TimeStamp::Now() : t;
|
||||
}
|
||||
|
||||
return clock_time;
|
||||
}
|
||||
@@ -2828,34 +2599,35 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
SendStreamData();
|
||||
}
|
||||
|
||||
const int64_t clock_time = GetClock();
|
||||
TimeStamp nowTime = TimeStamp::Now();
|
||||
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.
|
||||
// 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;
|
||||
NS_ASSERTION(clock_time >= 0, "Should have positive clock time.");
|
||||
nsRefPtr<VideoData> currentFrame;
|
||||
if (VideoQueue().GetSize() > 0) {
|
||||
VideoData* frame = VideoQueue().PeekFront();
|
||||
int32_t droppedFrames = 0;
|
||||
while (IsRealTime() || clock_time >= frame->mTime) {
|
||||
mVideoFrameEndTime = frame->GetEndTime();
|
||||
if (currentFrame) {
|
||||
mDecoder->NotifyDecodedFrames(0, 0, 1);
|
||||
VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld (%d so far)",
|
||||
currentFrame->mTime, clock_time, ++droppedFrames);
|
||||
}
|
||||
currentFrame = frame;
|
||||
nsRefPtr<VideoData> releaseMe = VideoQueue().PopFront();
|
||||
OnPlaybackOffsetUpdate(frame->mOffset);
|
||||
if (VideoQueue().GetSize() == 0)
|
||||
currentFrame = VideoQueue().PopFront();
|
||||
int32_t framesRemoved = 0;
|
||||
while (VideoQueue().GetSize() > 0) {
|
||||
VideoData* nextFrame = VideoQueue().PeekFront();
|
||||
if (!IsRealTime() && nextFrame->mTime > clockTime) {
|
||||
remainingTime = nextFrame->mTime - clockTime;
|
||||
break;
|
||||
frame = VideoQueue().PeekFront();
|
||||
}
|
||||
++framesRemoved;
|
||||
if (!currentFrame->mSentToCompositor) {
|
||||
mDecoder->NotifyDecodedFrames(0, 0, 1);
|
||||
VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld",
|
||||
currentFrame->mTime, clockTime);
|
||||
}
|
||||
currentFrame = VideoQueue().PopFront();
|
||||
}
|
||||
// Current frame has already been presented, wait until it's time to
|
||||
// present the next frame.
|
||||
if (frame && !currentFrame) {
|
||||
remainingTime = frame->mTime - clock_time;
|
||||
VideoQueue().PushFront(currentFrame);
|
||||
if (framesRemoved > 0) {
|
||||
OnPlaybackOffsetUpdate(currentFrame->mOffset);
|
||||
mVideoFrameEndTime = currentFrame->GetEndTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2874,9 +2646,6 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
(OutOfDecodedVideo() && mVideoWaitRequest.Exists());
|
||||
}
|
||||
if (shouldBuffer) {
|
||||
if (currentFrame) {
|
||||
PushFront(currentFrame);
|
||||
}
|
||||
StartBuffering();
|
||||
// Don't go straight back to the state machine loop since that might
|
||||
// cause us to start decoding again and we could flip-flop between
|
||||
@@ -2891,7 +2660,7 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
// advance the clock to after the media end time.
|
||||
if (mVideoFrameEndTime != -1 || mAudioEndTime != -1) {
|
||||
// These will be non -1 if we've displayed a video frame, or played an audio frame.
|
||||
int64_t t = std::min(clock_time, std::max(mVideoFrameEndTime, mAudioEndTime));
|
||||
int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, mAudioEndTime));
|
||||
if (t > GetMediaTime()) {
|
||||
UpdatePlaybackPosition(t);
|
||||
}
|
||||
@@ -2903,7 +2672,7 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
|
||||
if (currentFrame) {
|
||||
// Decode one frame and display it.
|
||||
int64_t delta = currentFrame->mTime - clock_time;
|
||||
int64_t delta = currentFrame->mTime - clockTime;
|
||||
TimeStamp presTime = nowTime + TimeDuration::FromMicroseconds(delta / mPlaybackRate);
|
||||
NS_ASSERTION(currentFrame->mTime >= 0, "Should have positive frame time");
|
||||
{
|
||||
@@ -2915,32 +2684,12 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
MOZ_ASSERT(IsPlaying());
|
||||
MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
|
||||
frameStats.NotifyPresentedFrame();
|
||||
remainingTime = currentFrame->GetEndTime() - clock_time;
|
||||
remainingTime = currentFrame->GetEndTime() - clockTime;
|
||||
currentFrame = nullptr;
|
||||
}
|
||||
|
||||
// The remainingTime is negative (include zero):
|
||||
// 1. When the clock_time is larger than the latest video frame's endtime.
|
||||
// All the video frames should be rendered or dropped, nothing left in
|
||||
// VideoQueue. And since the VideoQueue is empty, we don't need to wake up
|
||||
// statemachine thread immediately, so set the remainingTime to default value.
|
||||
// 2. Current frame's endtime is smaller than clock_time but there still exist
|
||||
// newer frames in queue. Re-calculate the remainingTime.
|
||||
if (remainingTime <= 0) {
|
||||
VideoData* nextFrame = VideoQueue().PeekFront();
|
||||
if (nextFrame) {
|
||||
remainingTime = nextFrame->mTime - clock_time;
|
||||
} else {
|
||||
remainingTime = AUDIO_DURATION_USECS;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t delay = remainingTime / mPlaybackRate;
|
||||
if (delay > 0) {
|
||||
ScheduleStateMachineIn(delay);
|
||||
} else {
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
int64_t delay = std::max<int64_t>(1, remainingTime / mPlaybackRate);
|
||||
ScheduleStateMachineIn(delay);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -3134,14 +2883,14 @@ void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
AssertCurrentThreadInMonitor();
|
||||
mPlayStartTime = aTimeStamp;
|
||||
if (!mAudioSink) {
|
||||
return;
|
||||
}
|
||||
if (!mPlayStartTime.IsNull()) {
|
||||
mAudioSink->StartPlayback();
|
||||
} else {
|
||||
mAudioSink->StopPlayback();
|
||||
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPlaying(!mPlayStartTime.IsNull());
|
||||
}
|
||||
// Have DecodedStream remember the playing state so it doesn't need to
|
||||
// ask MDSM about IsPlaying(). Note we have to do this even before capture
|
||||
// happens since capture could happen in the middle of playback.
|
||||
mDecodedStream->SetPlaying(!mPlayStartTime.IsNull());
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder()
|
||||
@@ -3223,14 +2972,17 @@ MediaDecoderStateMachine::LogicalPlaybackRateChanged()
|
||||
if (!HasAudio() && IsPlaying()) {
|
||||
// Remember how much time we've spent in playing the media
|
||||
// for playback rate will change from now on.
|
||||
mPlayDuration = GetVideoStreamPosition();
|
||||
SetPlayStartTime(TimeStamp::Now());
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
mPlayDuration = GetVideoStreamPosition(now);
|
||||
SetPlayStartTime(now);
|
||||
}
|
||||
|
||||
mPlaybackRate = mLogicalPlaybackRate;
|
||||
if (mAudioSink) {
|
||||
mAudioSink->SetPlaybackRate(mPlaybackRate);
|
||||
}
|
||||
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::PreservesPitchChanged()
|
||||
@@ -3312,10 +3064,13 @@ void MediaDecoderStateMachine::OnAudioSinkError()
|
||||
DecodeError();
|
||||
}
|
||||
|
||||
DecodedStreamData* MediaDecoderStateMachine::GetDecodedStream() const
|
||||
|
||||
uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
return mDecodedStream.GetData();
|
||||
return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
|
||||
? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
|
||||
: std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::DispatchAudioCaptured()
|
||||
@@ -3328,9 +3083,6 @@ void MediaDecoderStateMachine::DispatchAudioCaptured()
|
||||
if (!self->mAudioCaptured) {
|
||||
// Stop the audio sink if it's running.
|
||||
self->StopAudioThread();
|
||||
// GetMediaTime() could return -1 because we haven't decoded
|
||||
// the 1st frame. But this is OK since we will update mStreamStartTime
|
||||
// again in SetStartTime().
|
||||
self->mStreamStartTime = self->GetMediaTime();
|
||||
// Reset mAudioEndTime which will be updated as we send audio data to
|
||||
// stream. Otherwise it will remain -1 if we don't have audio.
|
||||
@@ -3347,63 +3099,10 @@ void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DECODER_LOG("AddOutputStream aStream=%p!", aStream);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (!GetDecodedStream()) {
|
||||
RecreateDecodedStream(aStream->Graph());
|
||||
}
|
||||
mDecodedStream.Connect(aStream, aFinishWhenEnded);
|
||||
mDecodedStream->Connect(aStream, aFinishWhenEnded);
|
||||
DispatchAudioCaptured();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::UpdateStreamBlockingForPlayState()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
auto stream = GetDecodedStream();
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool blocking = mPlayState != MediaDecoder::PLAY_STATE_PLAYING ||
|
||||
mLogicallySeeking;
|
||||
if (blocking != stream->mHaveBlockedForPlayState) {
|
||||
stream->mHaveBlockedForPlayState = blocking;
|
||||
UpdateStreamBlocking(stream->mStream, blocking);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::UpdateStreamBlockingForStateMachinePlaying()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
auto stream = GetDecodedStream();
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool blocking = !IsPlaying();
|
||||
if (blocking != stream->mHaveBlockedForStateMachineNotPlaying) {
|
||||
stream->mHaveBlockedForStateMachineNotPlaying = blocking;
|
||||
UpdateStreamBlocking(stream->mStream, blocking);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::RecreateDecodedStream(MediaStreamGraph* aGraph)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDecodedStream.RecreateData(aGraph);
|
||||
}
|
||||
|
||||
uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
|
||||
? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
|
||||
: std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
|
||||
Reference in New Issue
Block a user