зеркало из https://github.com/mozilla/gecko-dev.git
Bug 973408 - Set MediaDecoderReaders idle when they're not decoding. r=kinetik
This commit is contained in:
Родитель
7024b04cc3
Коммит
6b29454867
|
@ -78,16 +78,16 @@ public:
|
|||
int64_t aEndTime,
|
||||
int64_t aCurrentTime) = 0;
|
||||
|
||||
// Called when the decode thread is started, before calling any other
|
||||
// decode, read metadata, or seek functions. Do any thread local setup
|
||||
// in this function.
|
||||
virtual void OnDecodeThreadStart() {}
|
||||
|
||||
// Called when the decode thread is about to finish, after all calls to
|
||||
// any other decode, read metadata, or seek functions. Any backend specific
|
||||
// thread local tear down must be done in this function. Note that another
|
||||
// decode thread could start up and run in future.
|
||||
virtual void OnDecodeThreadFinish() {}
|
||||
// Called to move the reader into idle/active state. When the reader is
|
||||
// created it is assumed to be active (i.e. not idle). When the media
|
||||
// element is paused and we don't need to decode any more data, the state
|
||||
// machine calls SetIdle() to inform the reader that its decoder won't be
|
||||
// needed for a while. When we need to decode data again, the state machine
|
||||
// calls SetActive() to activate the decoder. The reader can use these
|
||||
// notifications to enter/exit a low power state when the decoder isn't
|
||||
// needed, if desired. This is most useful on mobile.
|
||||
virtual void SetIdle() { }
|
||||
virtual void SetActive() { }
|
||||
|
||||
// Tell the reader that the data decoded are not for direct playback, so it
|
||||
// can accept more files, in particular those which have more channels than
|
||||
|
|
|
@ -175,6 +175,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
|
||||
mDispatchedAudioDecodeTask(false),
|
||||
mDispatchedVideoDecodeTask(false),
|
||||
mIsReaderIdle(false),
|
||||
mAudioCaptured(false),
|
||||
mTransportSeekable(true),
|
||||
mMediaSeekable(true),
|
||||
|
@ -559,6 +560,7 @@ MediaDecoderStateMachine::DecodeVideo()
|
|||
mDispatchedVideoDecodeTask = false;
|
||||
return;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
// We don't want to consider skipping to the next keyframe if we've
|
||||
// only just started up the decode loop, so wait until we've decoded
|
||||
|
@ -628,6 +630,8 @@ MediaDecoderStateMachine::DecodeVideo()
|
|||
mDispatchedVideoDecodeTask = false;
|
||||
if (NeedToDecodeVideo()) {
|
||||
EnsureVideoDecodeTaskQueued();
|
||||
} else {
|
||||
UpdateIdleState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,6 +655,7 @@ MediaDecoderStateMachine::DecodeAudio()
|
|||
mDispatchedAudioDecodeTask = false;
|
||||
return;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
// We don't want to consider skipping to the next keyframe if we've
|
||||
// only just started up the decode loop, so wait until we've decoded
|
||||
|
@ -683,6 +688,8 @@ MediaDecoderStateMachine::DecodeAudio()
|
|||
mDispatchedAudioDecodeTask = false;
|
||||
if (NeedToDecodeAudio()) {
|
||||
EnsureAudioDecodeTaskQueued();
|
||||
} else {
|
||||
UpdateIdleState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,6 +709,7 @@ MediaDecoderStateMachine::CheckIfDecodeComplete()
|
|||
// We've finished decoding all active streams,
|
||||
// so move to COMPLETED state.
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
UpdateIdleState();
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
|
@ -1053,6 +1061,8 @@ void MediaDecoderStateMachine::StopPlayback()
|
|||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
|
||||
mDecoder->UpdateStreamBlockingForStateMachinePlaying();
|
||||
|
||||
UpdateIdleState();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::SetSyncPointForMediaStream()
|
||||
|
@ -1317,6 +1327,11 @@ void MediaDecoderStateMachine::StartDecoding()
|
|||
mIsVideoDecoding = HasVideo() && !mReader->VideoQueue().IsFinished();
|
||||
mIsAudioDecoding = HasAudio() && !mReader->AudioQueue().IsFinished();
|
||||
|
||||
CheckIfDecodeComplete();
|
||||
if (mState == DECODER_STATE_COMPLETED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset other state to pristine values before starting decode.
|
||||
mSkipToNextKeyFrame = false;
|
||||
mIsAudioPrerolling = true;
|
||||
|
@ -1482,6 +1497,64 @@ MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::EnsureActive()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
if (!mIsReaderIdle) {
|
||||
return;
|
||||
}
|
||||
mIsReaderIdle = false;
|
||||
SetReaderActive();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::SetReaderIdle()
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p SetReaderIdle()", mDecoder.get()));
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
mReader->SetIdle();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::SetReaderActive()
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p SetReaderActive()", mDecoder.get()));
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
mReader->SetActive();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::UpdateIdleState()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
// If we're in completed state, we should not need to decode anything else.
|
||||
MOZ_ASSERT(mState != DECODER_STATE_COMPLETED ||
|
||||
(!NeedToDecodeAudio() && !NeedToDecodeVideo()));
|
||||
|
||||
bool needIdle = mDecoder->GetState() == MediaDecoder::PLAY_STATE_PAUSED &&
|
||||
!NeedToDecodeAudio() &&
|
||||
!NeedToDecodeVideo() &&
|
||||
!IsPlaying();
|
||||
|
||||
if (mIsReaderIdle == needIdle) {
|
||||
return;
|
||||
}
|
||||
mIsReaderIdle = needIdle;
|
||||
nsRefPtr<nsIRunnable> event;
|
||||
if (mIsReaderIdle) {
|
||||
event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderIdle);
|
||||
} else {
|
||||
event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderActive);
|
||||
}
|
||||
if (NS_FAILED(mDecodeTaskQueue->Dispatch(event)) &&
|
||||
mState != DECODER_STATE_SHUTDOWN) {
|
||||
NS_WARNING("Failed to dispatch event to set decoder idle state");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDecoderStateMachine::EnqueueDecodeSeekTask()
|
||||
{
|
||||
|
@ -1698,6 +1771,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||
if (mState != DECODER_STATE_DECODING_METADATA) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
nsresult res;
|
||||
MediaInfo info;
|
||||
|
@ -1803,6 +1877,7 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||
if (mState != DECODER_STATE_SEEKING) {
|
||||
return;
|
||||
}
|
||||
EnsureActive();
|
||||
|
||||
// During the seek, don't have a lock on the decoder state,
|
||||
// otherwise long seek operations can block the main thread.
|
||||
|
@ -1905,7 +1980,12 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
|
||||
mDecoder.get(), seekTime));
|
||||
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
|
||||
// Explicitly set our state so we don't decode further, and so
|
||||
// we report playback ended to the media element.
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
mIsAudioDecoding = false;
|
||||
mIsVideoDecoding = false;
|
||||
UpdateIdleState();
|
||||
} else {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to DECODING",
|
||||
mDecoder.get(), seekTime));
|
||||
|
@ -2508,11 +2588,11 @@ void MediaDecoderStateMachine::StartBuffering()
|
|||
mDecoder.get(), decodeDuration.ToSeconds()));
|
||||
#ifdef PR_LOGGING
|
||||
MediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||
#endif
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
|
||||
mDecoder.get(),
|
||||
stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
|
||||
stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult MediaDecoderStateMachine::GetBuffered(dom::TimeRanges* aBuffered) {
|
||||
|
|
|
@ -564,6 +564,22 @@ private:
|
|||
// The decoder monitor must be held.
|
||||
nsresult EnqueueDecodeSeekTask();
|
||||
|
||||
// Calls the reader's SetIdle(), with aIsIdle as parameter. This is only
|
||||
// called in a task dispatched to the decode task queue, don't call it
|
||||
// directly.
|
||||
void SetReaderIdle();
|
||||
void SetReaderActive();
|
||||
|
||||
// Re-evaluates the state and determines whether we should set the reader
|
||||
// to idle mode. This is threadsafe, and can be called from any thread.
|
||||
// The decoder monitor must be held.
|
||||
void UpdateIdleState();
|
||||
|
||||
// Called before we do anything on the decode task queue to set the reader
|
||||
// as not idle if it was idle. This is called before we decode, seek, or
|
||||
// decode metadata (in case we were dormant or awaiting resources).
|
||||
void EnsureActive();
|
||||
|
||||
// Queries our state to see whether the decode has finished for all streams.
|
||||
// If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
|
||||
// to run.
|
||||
|
@ -831,6 +847,12 @@ private:
|
|||
// the video decode.
|
||||
bool mDispatchedVideoDecodeTask;
|
||||
|
||||
// True when the reader is initialized, but has been ordered "idle" by the
|
||||
// state machine. This happens when the MediaQueue's of decoded data are
|
||||
// "full" and playback is paused. The reader may choose to use the idle
|
||||
// notification to enter a low power state.
|
||||
bool mIsReaderIdle;
|
||||
|
||||
// If the video decode is falling behind the audio, we'll start dropping the
|
||||
// inter-frames up until the next keyframe which is at or before the current
|
||||
// playback position. skipToNextKeyframe is true if we're currently
|
||||
|
|
|
@ -393,21 +393,6 @@ DirectShowReader::Seek(int64_t aTargetUs,
|
|||
return DecodeToTarget(aTargetUs);
|
||||
}
|
||||
|
||||
void
|
||||
DirectShowReader::OnDecodeThreadStart()
|
||||
{
|
||||
MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
NS_ENSURE_TRUE_VOID(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
void
|
||||
DirectShowReader::OnDecodeThreadFinish()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
void
|
||||
DirectShowReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
|
|
|
@ -65,9 +65,6 @@ public:
|
|||
int64_t aEndTime,
|
||||
int64_t aCurrentTime) MOZ_OVERRIDE;
|
||||
|
||||
void OnDecodeThreadStart() MOZ_OVERRIDE;
|
||||
void OnDecodeThreadFinish() MOZ_OVERRIDE;
|
||||
|
||||
void NotifyDataArrived(const char* aBuffer,
|
||||
uint32_t aLength,
|
||||
int64_t aOffset) MOZ_OVERRIDE;
|
||||
|
|
|
@ -361,17 +361,19 @@ static uint64_t BytesToTime(int64_t offset, uint64_t length, uint64_t durationUs
|
|||
return uint64_t(double(durationUs) * perc);
|
||||
}
|
||||
|
||||
void MediaOmxReader::OnDecodeThreadFinish() {
|
||||
if (mOmxDecoder.get()) {
|
||||
mOmxDecoder->Pause();
|
||||
void MediaOmxReader::SetIdle() {
|
||||
if (!mOmxDecoder.get()) {
|
||||
return;
|
||||
}
|
||||
mOmxDecoder->Pause();
|
||||
}
|
||||
|
||||
void MediaOmxReader::OnDecodeThreadStart() {
|
||||
if (mOmxDecoder.get()) {
|
||||
DebugOnly<nsresult> result = mOmxDecoder->Play();
|
||||
NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
|
||||
void MediaOmxReader::SetActive() {
|
||||
if (!mOmxDecoder.get()) {
|
||||
return;
|
||||
}
|
||||
DebugOnly<nsresult> result = mOmxDecoder->Play();
|
||||
NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding");
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -79,9 +79,9 @@ public:
|
|||
MetadataTags** aTags);
|
||||
virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
|
||||
|
||||
virtual void OnDecodeThreadStart() MOZ_OVERRIDE;
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
virtual void SetActive() MOZ_OVERRIDE;
|
||||
|
||||
virtual void OnDecodeThreadFinish() MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -299,37 +299,8 @@ nsresult RtspOmxReader::Seek(int64_t aTime, int64_t aStartTime,
|
|||
return MediaOmxReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime);
|
||||
}
|
||||
|
||||
void RtspOmxReader::OnDecodeThreadStart() {
|
||||
// Start RTSP streaming right after starting the decoding thread in
|
||||
// MediaDecoderStateMachine and before starting OMXCodec decoding.
|
||||
if (mRtspResource) {
|
||||
nsIStreamingProtocolController* controller =
|
||||
mRtspResource->GetMediaStreamController();
|
||||
if (controller) {
|
||||
controller->Play();
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent class to start OMXCodec decoding.
|
||||
MediaOmxReader::OnDecodeThreadStart();
|
||||
}
|
||||
|
||||
void RtspOmxReader::OnDecodeThreadFinish() {
|
||||
// Call parent class to pause OMXCodec decoding.
|
||||
MediaOmxReader::OnDecodeThreadFinish();
|
||||
|
||||
// Stop RTSP streaming right before destroying the decoding thread in
|
||||
// MediaDecoderStateMachine and after pausing OMXCodec decoding.
|
||||
// RTSP streaming should not be paused until OMXCodec has been paused and
|
||||
// until the decoding thread in MediaDecoderStateMachine is about to be
|
||||
// destroyed. Otherwise, RtspMediaSource::read() would block the binder
|
||||
// thread of OMXCodecObserver::onMessage() --> OMXCodec::on_message() -->
|
||||
// OMXCodec::drainInputBuffer() due to network data starvation. Because
|
||||
// OMXCodec::mLock is held by the binder thread in this case, all other
|
||||
// threads would be blocked when they try to lock this mutex. As a result, the
|
||||
// decoding thread in MediaDecoderStateMachine would be blocked forever in
|
||||
// OMXCodec::read() if there is no enough data for RtspMediaSource::read() to
|
||||
// return.
|
||||
void RtspOmxReader::SetIdle() {
|
||||
// Need to pause RTSP streaming OMXCodec decoding.
|
||||
if (mRtspResource) {
|
||||
nsIStreamingProtocolController* controller =
|
||||
mRtspResource->GetMediaStreamController();
|
||||
|
@ -337,6 +308,23 @@ void RtspOmxReader::OnDecodeThreadFinish() {
|
|||
controller->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent class to set OMXCodec idle.
|
||||
MediaOmxReader::SetIdle();
|
||||
}
|
||||
|
||||
void RtspOmxReader::SetActive() {
|
||||
// Need to start RTSP streaming OMXCodec decoding.
|
||||
if (mRtspResource) {
|
||||
nsIStreamingProtocolController* controller =
|
||||
mRtspResource->GetMediaStreamController();
|
||||
if (controller) {
|
||||
controller->Play();
|
||||
}
|
||||
}
|
||||
|
||||
// Call parent class to set OMXCodec active.
|
||||
MediaOmxReader::SetActive();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -71,9 +71,8 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void OnDecodeThreadStart() MOZ_OVERRIDE;
|
||||
|
||||
virtual void OnDecodeThreadFinish() MOZ_OVERRIDE;
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
virtual void SetActive() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
|
|
|
@ -269,8 +269,6 @@ MediaDecodeTask::Decode()
|
|||
// bakend support.
|
||||
mDecoderReader->SetIgnoreAudioOutputFormat();
|
||||
|
||||
mDecoderReader->OnDecodeThreadStart();
|
||||
|
||||
MediaInfo mediaInfo;
|
||||
nsAutoPtr<MetadataTags> tags;
|
||||
nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags));
|
||||
|
@ -289,8 +287,6 @@ MediaDecodeTask::Decode()
|
|||
continue;
|
||||
}
|
||||
|
||||
mDecoderReader->OnDecodeThreadFinish();
|
||||
|
||||
MediaQueue<AudioData>& audioQueue = mDecoderReader->AudioQueue();
|
||||
uint32_t frameCount = audioQueue.FrameCount();
|
||||
uint32_t channelCount = mediaInfo.mAudio.mChannels;
|
||||
|
|
|
@ -80,28 +80,6 @@ WMFReader::~WMFReader()
|
|||
MOZ_COUNT_DTOR(WMFReader);
|
||||
}
|
||||
|
||||
void
|
||||
WMFReader::OnDecodeThreadStart()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
// XXX WebAudio will call this on the main thread so CoInit will definitely
|
||||
// fail. You cannot change the concurrency model once already set.
|
||||
// The main thread will continue to be STA, which seems to work, but MSDN
|
||||
// recommends that MTA be used.
|
||||
mCOMInitialized = SUCCEEDED(CoInitializeEx(0, COINIT_MULTITHREADED));
|
||||
NS_ENSURE_TRUE_VOID(mCOMInitialized);
|
||||
}
|
||||
|
||||
void
|
||||
WMFReader::OnDecodeThreadFinish()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
if (mCOMInitialized) {
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WMFReader::InitializeDXVA()
|
||||
{
|
||||
|
|
|
@ -47,10 +47,6 @@ public:
|
|||
int64_t aStartTime,
|
||||
int64_t aEndTime,
|
||||
int64_t aCurrentTime) MOZ_OVERRIDE;
|
||||
|
||||
void OnDecodeThreadStart() MOZ_OVERRIDE;
|
||||
void OnDecodeThreadFinish() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
HRESULT ConfigureAudioDecoder();
|
||||
|
|
Загрузка…
Ссылка в новой задаче