Bug 1109437 - Implement non-polling buffering. r=cpearce

This commit is contained in:
Bobby Holley 2014-12-22 00:20:31 -08:00
Родитель 05e1250b67
Коммит 49601684ad
6 изменённых файлов: 116 добавлений и 14 удалений

Просмотреть файл

@ -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;

Просмотреть файл

@ -759,8 +759,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;
}
@ -863,15 +865,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;
}
@ -972,6 +974,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
@ -2679,9 +2684,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);
}