mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 13:25:37 +00:00
Bug 1109437 - Implement non-polling buffering. r=cpearce
This commit is contained in:
parent
1170137ae6
commit
6b50bfd173
@ -13,6 +13,8 @@
|
||||
#include "MediaQueue.h"
|
||||
#include "AudioCompactor.h"
|
||||
|
||||
#include "mozilla/TypedEnum.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
@ -22,6 +24,17 @@ class TimeRanges;
|
||||
class MediaDecoderReader;
|
||||
class SharedDecoderManager;
|
||||
|
||||
struct WaitForDataRejectValue {
|
||||
enum Reason {
|
||||
SHUTDOWN
|
||||
};
|
||||
|
||||
WaitForDataRejectValue(MediaData::Type aType, Reason aReason)
|
||||
:mType(aType), mReason(aReason) {}
|
||||
MediaData::Type mType;
|
||||
Reason mReason;
|
||||
};
|
||||
|
||||
// Encapsulates the decoding and reading of media data. Reading can either
|
||||
// synchronous and done on the calling "decode" thread, or asynchronous and
|
||||
// performed on a background thread, with the result being returned by
|
||||
@ -40,6 +53,7 @@ public:
|
||||
typedef MediaPromise<nsRefPtr<AudioData>, NotDecodedReason> AudioDataPromise;
|
||||
typedef MediaPromise<nsRefPtr<VideoData>, NotDecodedReason> VideoDataPromise;
|
||||
typedef MediaPromise<bool, nsresult> SeekPromise;
|
||||
typedef MediaPromise<MediaData::Type, WaitForDataRejectValue> WaitForDataPromise;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
|
||||
|
||||
@ -113,6 +127,12 @@ public:
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
|
||||
|
||||
// By default, the state machine polls the reader once per second when it's
|
||||
// in buffering mode. Some readers support a promise-based mechanism by which
|
||||
// they notify the state machine when the data arrives.
|
||||
virtual bool IsWaitForDataSupported() { return false; }
|
||||
virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); }
|
||||
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
|
||||
|
@ -729,8 +729,10 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
|
||||
}
|
||||
|
||||
case DECODER_STATE_BUFFERING:
|
||||
// If we're buffering, this may be the sample we need to stop buffering.
|
||||
// Schedule the state machine and then fall through.
|
||||
ScheduleStateMachine();
|
||||
case DECODER_STATE_DECODING: {
|
||||
// In buffering and decoding state, we simply enqueue samples.
|
||||
Push(audio);
|
||||
return;
|
||||
}
|
||||
@ -833,15 +835,15 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
||||
return;
|
||||
}
|
||||
|
||||
// If the decoder is waiting for data, we need to make sure that the requests
|
||||
// are cleared, which happened above. Additionally, if we're out of decoded
|
||||
// samples, we need to switch to buffering mode.
|
||||
// If the decoder is waiting for data, we tell it to call us back when the
|
||||
// data arrives.
|
||||
if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
|
||||
bool outOfSamples = isAudio ? !AudioQueue().GetSize() : !VideoQueue().GetSize();
|
||||
if (outOfSamples) {
|
||||
StartBuffering();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mReader->IsWaitForDataSupported(),
|
||||
"Readers that send WAITING_FOR_DATA need to implement WaitForData");
|
||||
RequestStatusRef(aType) = RequestStatus::Waiting;
|
||||
mReader->WaitForData(aType)->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnWaitForDataResolved,
|
||||
&MediaDecoderStateMachine::OnWaitForDataRejected);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -941,6 +943,9 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
|
||||
}
|
||||
|
||||
case DECODER_STATE_BUFFERING:
|
||||
// If we're buffering, this may be the sample we need to stop buffering.
|
||||
// Schedule the state machine and then fall through.
|
||||
ScheduleStateMachine();
|
||||
case DECODER_STATE_DECODING: {
|
||||
Push(video);
|
||||
// If the requested video sample was slow to arrive, increase the
|
||||
@ -2647,9 +2652,14 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
return NS_OK;
|
||||
}
|
||||
} else if (outOfAudio || outOfVideo) {
|
||||
DECODER_LOG("Out of decoded data - polling for 1s");
|
||||
MOZ_ASSERT(mReader->IsWaitForDataSupported(),
|
||||
"Don't yet have a strategy for non-heuristic + non-WaitForData");
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
ScheduleStateMachine(USECS_PER_S);
|
||||
MOZ_ASSERT_IF(outOfAudio, mAudioRequestStatus != RequestStatus::Idle);
|
||||
MOZ_ASSERT_IF(outOfVideo, mVideoRequestStatus != RequestStatus::Idle);
|
||||
DECODER_LOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
|
||||
"mAudioStatus: %d, outOfVideo: %d, mVideoStatus: %d",
|
||||
outOfAudio, mAudioRequestStatus, outOfVideo, mVideoRequestStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -389,6 +389,23 @@ public:
|
||||
void OnSeekCompleted();
|
||||
void OnSeekFailed(nsresult aResult);
|
||||
|
||||
void OnWaitForDataResolved(MediaData::Type aType)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (RequestStatusRef(aType) == RequestStatus::Waiting) {
|
||||
RequestStatusRef(aType) = RequestStatus::Idle;
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void OnWaitForDataRejected(WaitForDataRejectValue aRejection)
|
||||
{
|
||||
MOZ_ASSERT(aRejection.mReason == WaitForDataRejectValue::SHUTDOWN);
|
||||
if (RequestStatusRef(aRejection.mType) == RequestStatus::Waiting) {
|
||||
RequestStatusRef(aRejection.mType) = RequestStatus::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void AcquireMonitorAndInvokeDecodeError();
|
||||
|
||||
|
@ -345,6 +345,9 @@ MediaSourceReader::ContinueShutdown()
|
||||
MOZ_ASSERT(mAudioPromise.IsEmpty());
|
||||
MOZ_ASSERT(mVideoPromise.IsEmpty());
|
||||
|
||||
mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
|
||||
mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
|
||||
|
||||
MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
|
||||
}
|
||||
|
||||
@ -391,6 +394,15 @@ MediaSourceReader::SelectReader(int64_t aTarget,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
|
||||
{
|
||||
TrackBuffer* trackBuffer = aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
|
||||
MOZ_ASSERT(trackBuffer);
|
||||
nsRefPtr<MediaDecoderReader> reader = SelectReader(aTarget, trackBuffer->Decoders());
|
||||
return !!reader;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::SwitchAudioReader(int64_t aTarget)
|
||||
{
|
||||
@ -720,6 +732,32 @@ MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::WaitForDataPromise>
|
||||
MediaSourceReader::WaitForData(MediaData::Type aType)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
nsRefPtr<WaitForDataPromise> p = WaitPromise(aType).Ensure(__func__);
|
||||
MaybeNotifyHaveData();
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::MaybeNotifyHaveData()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
bool haveAudio = false, haveVideo = false;
|
||||
if (!mAudioIsSeeking && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) {
|
||||
haveAudio = true;
|
||||
WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
|
||||
}
|
||||
if (!mVideoIsSeeking && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) {
|
||||
haveVideo = true;
|
||||
WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
|
||||
}
|
||||
MSE_DEBUG("MediaSourceReader(%p)::MaybeNotifyHaveData haveAudio=%d, haveVideo=%d", this,
|
||||
haveAudio, haveVideo);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
|
@ -61,6 +61,10 @@ public:
|
||||
void OnSeekCompleted();
|
||||
void OnSeekFailed(nsresult aResult);
|
||||
|
||||
virtual bool IsWaitForDataSupported() MOZ_OVERRIDE { return true; }
|
||||
virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) MOZ_OVERRIDE;
|
||||
void MaybeNotifyHaveData();
|
||||
|
||||
bool HasVideo() MOZ_OVERRIDE
|
||||
{
|
||||
return mInfo.HasVideo();
|
||||
@ -132,6 +136,7 @@ private:
|
||||
// available in the range requested by aTarget.
|
||||
already_AddRefed<MediaDecoderReader> SelectReader(int64_t aTarget,
|
||||
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
|
||||
bool HaveData(int64_t aTarget, MediaData::Type aType);
|
||||
|
||||
void AttemptSeek();
|
||||
void FinalizeSeek();
|
||||
@ -148,6 +153,13 @@ private:
|
||||
MediaPromiseHolder<AudioDataPromise> mAudioPromise;
|
||||
MediaPromiseHolder<VideoDataPromise> mVideoPromise;
|
||||
|
||||
MediaPromiseHolder<WaitForDataPromise> mAudioWaitPromise;
|
||||
MediaPromiseHolder<WaitForDataPromise> mVideoWaitPromise;
|
||||
MediaPromiseHolder<WaitForDataPromise>& WaitPromise(MediaData::Type aType)
|
||||
{
|
||||
return aType == MediaData::AUDIO_DATA ? mAudioWaitPromise : mVideoWaitPromise;
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsRefPtr<CDMProxy> mCDMProxy;
|
||||
#endif
|
||||
|
@ -183,9 +183,9 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Schedule the state machine thread to ensure playback starts if required
|
||||
// when data is appended.
|
||||
mParentDecoder->ScheduleStateMachineThread();
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
mParentDecoder->GetReader()->MaybeNotifyHaveData();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -440,6 +440,11 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
RemoveDecoder(aDecoder);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
mParentDecoder->GetReader()->MaybeNotifyHaveData();
|
||||
|
||||
MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user