mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 21:35:39 +00:00
Backed out changeset b76636e9f49a (bug 1097823)
This commit is contained in:
parent
60f876f46a
commit
8327697375
@ -30,6 +30,37 @@ public:
|
||||
MOZ_ASSERT(aTaskQueue);
|
||||
}
|
||||
|
||||
virtual void OnAudioDecoded(AudioData* aSample) MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(new DeliverAudioTask(aSample, mTarget));
|
||||
mTaskQueue->Dispatch(task);
|
||||
}
|
||||
|
||||
virtual void OnVideoDecoded(VideoData* aSample) MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(new DeliverVideoTask(aSample, mTarget));
|
||||
mTaskQueue->Dispatch(task);
|
||||
}
|
||||
|
||||
virtual void OnNotDecoded(MediaData::Type aType,
|
||||
MediaDecoderReader::NotDecodedReason aReason) MOZ_OVERRIDE {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mTarget || !mTaskQueue) {
|
||||
// We've been shutdown, abort.
|
||||
return;
|
||||
}
|
||||
RefPtr<nsIRunnable> task(new DeliverNotDecodedTask(aType, aReason, mTarget));
|
||||
mTaskQueue->Dispatch(task);
|
||||
}
|
||||
|
||||
void BreakCycles() {
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mTarget = nullptr;
|
||||
@ -51,6 +82,80 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class DeliverAudioTask : public nsRunnable {
|
||||
public:
|
||||
DeliverAudioTask(AudioData* aSample, Target* aTarget)
|
||||
: mSample(aSample)
|
||||
, mTarget(aTarget)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeliverAudioTask);
|
||||
}
|
||||
protected:
|
||||
~DeliverAudioTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DeliverAudioTask);
|
||||
}
|
||||
public:
|
||||
NS_METHOD Run() {
|
||||
mTarget->OnAudioDecoded(mSample);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<AudioData> mSample;
|
||||
RefPtr<Target> mTarget;
|
||||
};
|
||||
|
||||
class DeliverVideoTask : public nsRunnable {
|
||||
public:
|
||||
DeliverVideoTask(VideoData* aSample, Target* aTarget)
|
||||
: mSample(aSample)
|
||||
, mTarget(aTarget)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeliverVideoTask);
|
||||
}
|
||||
protected:
|
||||
~DeliverVideoTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DeliverVideoTask);
|
||||
}
|
||||
public:
|
||||
NS_METHOD Run() {
|
||||
mTarget->OnVideoDecoded(mSample);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<VideoData> mSample;
|
||||
RefPtr<Target> mTarget;
|
||||
};
|
||||
|
||||
class DeliverNotDecodedTask : public nsRunnable {
|
||||
public:
|
||||
DeliverNotDecodedTask(MediaData::Type aType,
|
||||
MediaDecoderReader::NotDecodedReason aReason,
|
||||
Target* aTarget)
|
||||
: mType(aType)
|
||||
, mReason(aReason)
|
||||
, mTarget(aTarget)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DeliverNotDecodedTask);
|
||||
}
|
||||
protected:
|
||||
~DeliverNotDecodedTask()
|
||||
{
|
||||
MOZ_COUNT_DTOR(DeliverNotDecodedTask);
|
||||
}
|
||||
public:
|
||||
NS_METHOD Run() {
|
||||
mTarget->OnNotDecoded(mType, mReason);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
MediaData::Type mType;
|
||||
MediaDecoderReader::NotDecodedReason mReason;
|
||||
RefPtr<Target> mTarget;
|
||||
};
|
||||
|
||||
Monitor mMonitor;
|
||||
RefPtr<MediaTaskQueue> mTaskQueue;
|
||||
RefPtr<Target> mTarget;
|
||||
|
@ -73,7 +73,6 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||
, mDecoder(aDecoder)
|
||||
, mIgnoreAudioOutputFormat(false)
|
||||
, mStartTime(-1)
|
||||
, mHitAudioDecodeError(false)
|
||||
, mTaskQueueIsBorrowed(false)
|
||||
, mAudioDiscontinuity(false)
|
||||
, mVideoDiscontinuity(false)
|
||||
@ -189,11 +188,10 @@ private:
|
||||
int64_t mTimeThreshold;
|
||||
};
|
||||
|
||||
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
||||
void
|
||||
MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
|
||||
bool skip = aSkipToNextKeyframe;
|
||||
while (VideoQueue().GetSize() == 0 &&
|
||||
!VideoQueue().IsFinished()) {
|
||||
@ -206,7 +204,7 @@ MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
// would hog the decode task queue.
|
||||
RefPtr<nsIRunnable> task(new RequestVideoWithSkipTask(this, aTimeThreshold));
|
||||
mTaskQueue->Dispatch(task);
|
||||
return p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (VideoQueue().GetSize() > 0) {
|
||||
@ -215,20 +213,15 @@ MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
v->mDiscontinuity = true;
|
||||
mVideoDiscontinuity = false;
|
||||
}
|
||||
mVideoPromise.Resolve(v, __func__);
|
||||
GetCallback()->OnVideoDecoded(v);
|
||||
} else if (VideoQueue().IsFinished()) {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Dropping this promise on the floor");
|
||||
GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, END_OF_STREAM);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
void
|
||||
MediaDecoderReader::RequestAudioData()
|
||||
{
|
||||
nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
|
||||
while (AudioQueue().GetSize() == 0 &&
|
||||
!AudioQueue().IsFinished()) {
|
||||
if (!DecodeAudioData()) {
|
||||
@ -243,7 +236,7 @@ MediaDecoderReader::RequestAudioData()
|
||||
RefPtr<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &MediaDecoderReader::RequestAudioData));
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
return p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (AudioQueue().GetSize() > 0) {
|
||||
@ -252,15 +245,12 @@ MediaDecoderReader::RequestAudioData()
|
||||
a->mDiscontinuity = true;
|
||||
mAudioDiscontinuity = false;
|
||||
}
|
||||
mAudioPromise.Resolve(a, __func__);
|
||||
GetCallback()->OnAudioDecoded(a);
|
||||
return;
|
||||
} else if (AudioQueue().IsFinished()) {
|
||||
mAudioPromise.Reject(mHitAudioDecodeError ? DECODE_ERROR : END_OF_STREAM, __func__);
|
||||
mHitAudioDecodeError = false;
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Dropping this promise on the floor");
|
||||
GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, END_OF_STREAM);
|
||||
return;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
@ -298,16 +288,6 @@ MediaDecoderReader::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
mShutdown = true;
|
||||
|
||||
mAudioPromise.SetMonitor(nullptr);
|
||||
if (!mAudioPromise.IsEmpty()) {
|
||||
mAudioPromise.Reject(END_OF_STREAM, __func__);
|
||||
}
|
||||
mVideoPromise.SetMonitor(nullptr);
|
||||
if (!mVideoPromise.IsEmpty()) {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
}
|
||||
|
||||
ReleaseMediaResources();
|
||||
if (mTaskQueue && !mTaskQueueIsBorrowed) {
|
||||
// We may be running in the task queue ourselves, so we don't block this
|
||||
@ -317,9 +297,8 @@ MediaDecoderReader::Shutdown()
|
||||
mTaskQueue = nullptr;
|
||||
}
|
||||
|
||||
AudioDecodeRendezvous::AudioDecodeRendezvous(MediaDecoderReader *aReader)
|
||||
: mReader(aReader)
|
||||
, mMonitor("AudioDecodeRendezvous")
|
||||
AudioDecodeRendezvous::AudioDecodeRendezvous()
|
||||
: mMonitor("AudioDecodeRendezvous")
|
||||
, mHaveResult(false)
|
||||
{
|
||||
}
|
||||
@ -339,8 +318,10 @@ AudioDecodeRendezvous::OnAudioDecoded(AudioData* aSample)
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
|
||||
AudioDecodeRendezvous::OnNotDecoded(MediaData::Type aType,
|
||||
MediaDecoderReader::NotDecodedReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(aType == MediaData::AUDIO_DATA);
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mSample = nullptr;
|
||||
mStatus = aReason == MediaDecoderReader::DECODE_ERROR ? NS_ERROR_FAILURE : NS_OK;
|
||||
@ -358,18 +339,9 @@ AudioDecodeRendezvous::Reset()
|
||||
}
|
||||
|
||||
nsresult
|
||||
AudioDecodeRendezvous::RequestAndWait(nsRefPtr<AudioData>& aSample)
|
||||
AudioDecodeRendezvous::Await(nsRefPtr<AudioData>& aSample)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
// XXXbholley: We hackily use the main thread for calling back the rendezvous,
|
||||
// since the decode thread is blocked. This is fine for correctness but not
|
||||
// for jank, and so it goes away in a subsequent patch.
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mReader->RequestAudioData()->Then(mainThread.get(), __func__, this,
|
||||
&AudioDecodeRendezvous::OnAudioDecoded,
|
||||
&AudioDecodeRendezvous::OnAudioNotDecoded);
|
||||
while (!mHaveResult) {
|
||||
mon.Wait();
|
||||
}
|
||||
@ -378,4 +350,13 @@ AudioDecodeRendezvous::RequestAndWait(nsRefPtr<AudioData>& aSample)
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
void
|
||||
AudioDecodeRendezvous::Cancel()
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mStatus = NS_ERROR_ABORT;
|
||||
mHaveResult = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaPromise.h"
|
||||
#include "MediaQueue.h"
|
||||
#include "AudioCompactor.h"
|
||||
|
||||
@ -38,9 +37,6 @@ public:
|
||||
CANCELED
|
||||
};
|
||||
|
||||
typedef MediaPromise<nsRefPtr<AudioData>, NotDecodedReason> AudioDataPromise;
|
||||
typedef MediaPromise<nsRefPtr<VideoData>, NotDecodedReason> VideoDataPromise;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
|
||||
|
||||
explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
|
||||
@ -97,22 +93,22 @@ public:
|
||||
// properly!
|
||||
virtual nsresult ResetDecode();
|
||||
|
||||
// Requests one audio sample from the reader.
|
||||
//
|
||||
// The decode should be performed asynchronously, and the promise should
|
||||
// be resolved when it is complete. Don't hold the decoder
|
||||
// Requests the Reader to call OnAudioDecoded() on aCallback with one
|
||||
// audio sample. The decode should be performed asynchronously, and
|
||||
// the callback can be performed on any thread. Don't hold the decoder
|
||||
// monitor while calling this, as the implementation may try to wait
|
||||
// on something that needs the monitor and deadlock.
|
||||
virtual nsRefPtr<AudioDataPromise> RequestAudioData();
|
||||
virtual void RequestAudioData();
|
||||
|
||||
// Requests one video sample from the reader.
|
||||
//
|
||||
// Don't hold the decoder monitor while calling this, as the implementation
|
||||
// may try to wait on something that needs the monitor and deadlock.
|
||||
// Requests the Reader to call OnVideoDecoded() on aCallback with one
|
||||
// video sample. The decode should be performed asynchronously, and
|
||||
// the callback can be performed on any thread. Don't hold the decoder
|
||||
// monitor while calling this, as the implementation may try to wait
|
||||
// on something that needs the monitor and deadlock.
|
||||
// If aSkipToKeyframe is true, the decode should skip ahead to the
|
||||
// the next keyframe at or after aTimeThreshold microseconds.
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold);
|
||||
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
@ -279,32 +275,8 @@ protected:
|
||||
// and then set to a value >= by MediaDecoderStateMachine::SetStartTime(),
|
||||
// after which point it never changes.
|
||||
int64_t mStartTime;
|
||||
|
||||
MediaPromiseHolder<AudioDataPromise> mAudioPromise;
|
||||
MediaPromiseHolder<VideoDataPromise> mVideoPromise;
|
||||
|
||||
// Convenience methods.
|
||||
bool HasPromise(MediaData::Type aType)
|
||||
{
|
||||
return aType == MediaData::AUDIO_DATA ? !mAudioPromise.IsEmpty() : !mVideoPromise.IsEmpty();
|
||||
}
|
||||
void RejectPromise(MediaData::Type aType, NotDecodedReason aReason,
|
||||
const char* aMethodName)
|
||||
{
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
mAudioPromise.Reject(aReason, aMethodName);
|
||||
} else {
|
||||
mVideoPromise.Reject(aReason, aMethodName);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a quick-and-dirty way for DecodeAudioData implementations to
|
||||
// communicate the presence of a decoding error to RequestAudioData. We should
|
||||
// replace this with a promise-y mechanism as we make this stuff properly
|
||||
// async.
|
||||
bool mHitAudioDecodeError;
|
||||
|
||||
private:
|
||||
|
||||
nsRefPtr<RequestSampleCallback> mSampleDecodedCallback;
|
||||
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
@ -325,6 +297,17 @@ class RequestSampleCallback {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RequestSampleCallback)
|
||||
|
||||
// Receives the result of a RequestAudioData() call.
|
||||
virtual void OnAudioDecoded(AudioData* aSample) = 0;
|
||||
|
||||
// Receives the result of a RequestVideoData() call.
|
||||
virtual void OnVideoDecoded(VideoData* aSample) = 0;
|
||||
|
||||
// Called when a RequestAudioData() or RequestVideoData() call can't be
|
||||
// fulfiled. The reason is passed as aReason.
|
||||
virtual void OnNotDecoded(MediaData::Type aType,
|
||||
MediaDecoderReader::NotDecodedReason aReason) = 0;
|
||||
|
||||
virtual void OnSeekCompleted(nsresult aResult) = 0;
|
||||
|
||||
// Called during shutdown to break any reference cycles.
|
||||
@ -338,28 +321,29 @@ protected:
|
||||
// MediaDecoderReader to block the thread requesting an audio sample until
|
||||
// the audio decode is complete. This is used to adapt the asynchronous
|
||||
// model of the MediaDecoderReader to a synchronous model.
|
||||
class AudioDecodeRendezvous {
|
||||
class AudioDecodeRendezvous : public RequestSampleCallback {
|
||||
public:
|
||||
AudioDecodeRendezvous(MediaDecoderReader *aReader);
|
||||
AudioDecodeRendezvous();
|
||||
~AudioDecodeRendezvous();
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDecodeRendezvous)
|
||||
|
||||
void OnAudioDecoded(AudioData* aSample);
|
||||
void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
|
||||
// RequestSampleCallback implementation. Called when decode is complete.
|
||||
// Note: aSample is null at end of stream.
|
||||
virtual void OnAudioDecoded(AudioData* aSample) MOZ_OVERRIDE;
|
||||
virtual void OnVideoDecoded(VideoData* aSample) MOZ_OVERRIDE {}
|
||||
virtual void OnNotDecoded(MediaData::Type aType,
|
||||
MediaDecoderReader::NotDecodedReason aReason) MOZ_OVERRIDE;
|
||||
virtual void OnSeekCompleted(nsresult aResult) MOZ_OVERRIDE {};
|
||||
virtual void BreakCycles() MOZ_OVERRIDE {};
|
||||
void Reset();
|
||||
|
||||
// Returns failure on error, or NS_OK.
|
||||
// If *aSample is null, EOS has been reached.
|
||||
nsresult RequestAndWait(nsRefPtr<AudioData>& aSample);
|
||||
nsresult Await(nsRefPtr<AudioData>& aSample);
|
||||
|
||||
// Interrupts a call to Wait().
|
||||
void Cancel();
|
||||
|
||||
protected:
|
||||
~AudioDecodeRendezvous();
|
||||
|
||||
private:
|
||||
nsRefPtr<MediaDecoderReader> mReader;
|
||||
Monitor mMonitor;
|
||||
nsresult mStatus;
|
||||
nsRefPtr<AudioData> mSample;
|
||||
|
@ -623,10 +623,7 @@ MediaDecoderStateMachine::DecodeVideo()
|
||||
mVideoDecodeStartTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
|
||||
->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded);
|
||||
mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -669,9 +666,7 @@ MediaDecoderStateMachine::DecodeAudio()
|
||||
mIsAudioPrerolling = false;
|
||||
}
|
||||
}
|
||||
mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded);
|
||||
mReader->RequestAudioData();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -920,7 +915,6 @@ MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame()
|
||||
void
|
||||
MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
|
||||
{
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
nsRefPtr<VideoData> video(aVideoSample);
|
||||
mVideoRequestPending = false;
|
||||
@ -2123,17 +2117,12 @@ MediaDecoderStateMachine::DecodeFirstFrame()
|
||||
} else {
|
||||
if (HasAudio()) {
|
||||
ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
|
||||
mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded);
|
||||
mReader->RequestAudioData();
|
||||
}
|
||||
if (HasVideo()) {
|
||||
mVideoDecodeStartTime = TimeStamp::Now();
|
||||
ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
|
||||
mReader->RequestVideoData(false, 0)
|
||||
->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
&MediaDecoderStateMachine::OnVideoNotDecoded);
|
||||
mReader->RequestVideoData(false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,17 +369,6 @@ public:
|
||||
void OnAudioDecoded(AudioData* aSample);
|
||||
void OnVideoDecoded(VideoData* aSample);
|
||||
void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
|
||||
void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
OnNotDecoded(MediaData::AUDIO_DATA, aReason);
|
||||
}
|
||||
void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
OnNotDecoded(MediaData::VIDEO_DATA, aReason);
|
||||
}
|
||||
|
||||
void OnSeekCompleted(nsresult aResult);
|
||||
|
||||
private:
|
||||
|
@ -190,8 +190,9 @@ public:
|
||||
|
||||
template<typename TargetType, typename ThisType,
|
||||
typename ResolveMethodType, typename RejectMethodType>
|
||||
void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal,
|
||||
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
|
||||
void Then(TargetType* aResponseTarget, ThisType* aThisVal,
|
||||
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
|
||||
const char* aCallSite)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
ThenValueBase* thenValue = new ThenValue<TargetType, ThisType, ResolveMethodType,
|
||||
|
@ -121,8 +121,8 @@ private:
|
||||
|
||||
MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder)
|
||||
, mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
|
||||
, mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
|
||||
, mAudio("MP4 audio decoder data", Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
|
||||
, mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
|
||||
, mLastReportedNumDecodedFrames(0)
|
||||
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
|
||||
, mDemuxerInitialized(false)
|
||||
@ -132,11 +132,6 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(MP4Reader);
|
||||
|
||||
// For MP4Reader, the promises are managed along with other data in
|
||||
// DecoderData, and protected by the same monitor.
|
||||
mAudioPromise.SetMonitor(&mAudio.mMonitor);
|
||||
mVideoPromise.SetMonitor(&mVideo.mMonitor);
|
||||
}
|
||||
|
||||
MP4Reader::~MP4Reader()
|
||||
@ -177,11 +172,6 @@ MP4Reader::Shutdown()
|
||||
mPlatform = nullptr;
|
||||
}
|
||||
|
||||
mAudioPromise.SetMonitor(nullptr);
|
||||
MOZ_ASSERT(mAudioPromise.IsEmpty());
|
||||
mVideoPromise.SetMonitor(nullptr);
|
||||
MOZ_ASSERT(mVideoPromise.IsEmpty());
|
||||
|
||||
MediaDecoderReader::Shutdown();
|
||||
}
|
||||
|
||||
@ -509,7 +499,7 @@ MP4Reader::GetDecoderData(TrackType aTrack)
|
||||
return (aTrack == kAudio) ? mAudio : mVideo;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
||||
void
|
||||
MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
@ -523,42 +513,34 @@ MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
|
||||
MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
|
||||
|
||||
bool eos = false;
|
||||
if (aSkipToNextKeyframe) {
|
||||
eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
|
||||
if (!eos && NS_FAILED(mVideo.mDecoder->Flush())) {
|
||||
if (!SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed) ||
|
||||
NS_FAILED(mVideo.mDecoder->Flush())) {
|
||||
NS_WARNING("Failed to skip/flush video when skipping-to-next-keyframe.");
|
||||
}
|
||||
}
|
||||
|
||||
auto& decoder = GetDecoderData(kVideo);
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
|
||||
if (eos) {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
} else {
|
||||
ScheduleUpdate(kVideo);
|
||||
}
|
||||
decoder.mOutputRequested = true;
|
||||
ScheduleUpdate(kVideo);
|
||||
|
||||
// Report the number of "decoded" frames as the difference in the
|
||||
// mNumSamplesOutput field since the last time we were called.
|
||||
uint64_t delta = mVideo.mNumSamplesOutput - mLastReportedNumDecodedFrames;
|
||||
decoded = static_cast<uint32_t>(delta);
|
||||
mLastReportedNumDecodedFrames = mVideo.mNumSamplesOutput;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
void
|
||||
MP4Reader::RequestAudioData()
|
||||
{
|
||||
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
|
||||
VLOG("RequestAudioData");
|
||||
auto& decoder = GetDecoderData(kAudio);
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
|
||||
decoder.mOutputRequested = true;
|
||||
ScheduleUpdate(kAudio);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
@ -588,7 +570,7 @@ MP4Reader::NeedInput(DecoderData& aDecoder)
|
||||
return
|
||||
!aDecoder.mError &&
|
||||
!aDecoder.mDemuxEOS &&
|
||||
HasPromise(aDecoder.mType) &&
|
||||
aDecoder.mOutputRequested &&
|
||||
aDecoder.mOutput.IsEmpty() &&
|
||||
(aDecoder.mInputExhausted ||
|
||||
aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput < aDecoder.mDecodeAhead);
|
||||
@ -601,7 +583,9 @@ MP4Reader::Update(TrackType aTrack)
|
||||
|
||||
bool needInput = false;
|
||||
bool needOutput = false;
|
||||
bool eos = false;
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
nsRefPtr<MediaData> output;
|
||||
{
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
decoder.mUpdateScheduled = false;
|
||||
@ -610,23 +594,19 @@ MP4Reader::Update(TrackType aTrack)
|
||||
decoder.mInputExhausted = false;
|
||||
decoder.mNumSamplesInput++;
|
||||
}
|
||||
if (HasPromise(decoder.mType)) {
|
||||
needOutput = true;
|
||||
if (!decoder.mOutput.IsEmpty()) {
|
||||
nsRefPtr<MediaData> output = decoder.mOutput[0];
|
||||
decoder.mOutput.RemoveElementAt(0);
|
||||
ReturnOutput(output, aTrack);
|
||||
} else if (decoder.mDrainComplete) {
|
||||
RejectPromise(decoder.mType, END_OF_STREAM, __func__);
|
||||
}
|
||||
needOutput = decoder.mOutputRequested;
|
||||
if (needOutput && !decoder.mOutput.IsEmpty()) {
|
||||
output = decoder.mOutput[0];
|
||||
decoder.mOutput.RemoveElementAt(0);
|
||||
}
|
||||
eos = decoder.mDrainComplete;
|
||||
}
|
||||
|
||||
VLOG("Update(%s) ni=%d no=%d iex=%d fl=%d",
|
||||
VLOG("Update(%s) ni=%d no=%d iex=%d or=%d fl=%d",
|
||||
TrackTypeToStr(aTrack),
|
||||
needInput,
|
||||
needOutput,
|
||||
decoder.mInputExhausted,
|
||||
decoder.mOutputRequested,
|
||||
decoder.mIsFlushing);
|
||||
|
||||
if (needInput) {
|
||||
@ -643,17 +623,27 @@ MP4Reader::Update(TrackType aTrack)
|
||||
decoder.mDecoder->Drain();
|
||||
}
|
||||
}
|
||||
if (needOutput) {
|
||||
if (output) {
|
||||
ReturnOutput(output, aTrack);
|
||||
} else if (eos) {
|
||||
ReturnEOS(aTrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4Reader::ReturnOutput(MediaData* aData, TrackType aTrack)
|
||||
{
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
decoder.mMonitor.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(HasPromise(decoder.mType));
|
||||
if (decoder.mDiscontinuity) {
|
||||
decoder.mDiscontinuity = false;
|
||||
aData->mDiscontinuity = true;
|
||||
{
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
MOZ_ASSERT(decoder.mOutputRequested);
|
||||
decoder.mOutputRequested = false;
|
||||
if (decoder.mDiscontinuity) {
|
||||
decoder.mDiscontinuity = false;
|
||||
aData->mDiscontinuity = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aTrack == kAudio) {
|
||||
@ -667,12 +657,19 @@ MP4Reader::ReturnOutput(MediaData* aData, TrackType aTrack)
|
||||
mInfo.mAudio.mChannels = audioData->mChannels;
|
||||
}
|
||||
|
||||
mAudioPromise.Resolve(audioData, __func__);
|
||||
GetCallback()->OnAudioDecoded(audioData);
|
||||
} else if (aTrack == kVideo) {
|
||||
mVideoPromise.Resolve(static_cast<VideoData*>(aData), __func__);
|
||||
GetCallback()->OnVideoDecoded(static_cast<VideoData*>(aData));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4Reader::ReturnEOS(TrackType aTrack)
|
||||
{
|
||||
GetCallback()->OnNotDecoded(aTrack == kAudio ? MediaData::AUDIO_DATA : MediaData::VIDEO_DATA,
|
||||
RequestSampleCallback::END_OF_STREAM);
|
||||
}
|
||||
|
||||
MP4Sample*
|
||||
MP4Reader::PopSample(TrackType aTrack)
|
||||
{
|
||||
@ -725,7 +722,7 @@ MP4Reader::Output(TrackType aTrack, MediaData* aSample)
|
||||
|
||||
decoder.mOutput.AppendElement(aSample);
|
||||
decoder.mNumSamplesOutput++;
|
||||
if (NeedInput(decoder) || HasPromise(decoder.mType)) {
|
||||
if (NeedInput(decoder) || decoder.mOutputRequested) {
|
||||
ScheduleUpdate(aTrack);
|
||||
}
|
||||
}
|
||||
@ -755,10 +752,9 @@ MP4Reader::Error(TrackType aTrack)
|
||||
{
|
||||
MonitorAutoLock mon(data.mMonitor);
|
||||
data.mError = true;
|
||||
if (HasPromise(data.mType)) {
|
||||
RejectPromise(data.mType, DECODE_ERROR, __func__);
|
||||
}
|
||||
}
|
||||
GetCallback()->OnNotDecoded(aTrack == kVideo ? MediaData::VIDEO_DATA : MediaData::AUDIO_DATA,
|
||||
RequestSampleCallback::DECODE_ERROR);
|
||||
}
|
||||
|
||||
void
|
||||
@ -787,9 +783,11 @@ MP4Reader::Flush(TrackType aTrack)
|
||||
data.mNumSamplesInput = 0;
|
||||
data.mNumSamplesOutput = 0;
|
||||
data.mInputExhausted = false;
|
||||
if (HasPromise(data.mType)) {
|
||||
RejectPromise(data.mType, CANCELED, __func__);
|
||||
if (data.mOutputRequested) {
|
||||
GetCallback()->OnNotDecoded(aTrack == kVideo ? MediaData::VIDEO_DATA : MediaData::AUDIO_DATA,
|
||||
RequestSampleCallback::CANCELED);
|
||||
}
|
||||
data.mOutputRequested = false;
|
||||
data.mDiscontinuity = true;
|
||||
}
|
||||
if (aTrack == kVideo) {
|
||||
@ -811,8 +809,10 @@ MP4Reader::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed
|
||||
while (true) {
|
||||
nsAutoPtr<MP4Sample> compressed(PopSample(kVideo));
|
||||
if (!compressed) {
|
||||
// EOS, or error. Let the state machine know.
|
||||
GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA,
|
||||
RequestSampleCallback::END_OF_STREAM);
|
||||
{
|
||||
// EOS, or error. This code assumes EOS, which may or may not be right.
|
||||
MonitorAutoLock mon(mVideo.mMonitor);
|
||||
mVideo.mDemuxEOS = true;
|
||||
}
|
||||
|
@ -37,10 +37,10 @@ public:
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
|
||||
virtual void RequestAudioData() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool HasAudio() MOZ_OVERRIDE;
|
||||
virtual bool HasVideo() MOZ_OVERRIDE;
|
||||
@ -75,6 +75,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void ReturnEOS(TrackType aTrack);
|
||||
void ReturnOutput(MediaData* aData, TrackType aTrack);
|
||||
|
||||
// Sends input to decoder for aTrack, and output to the state machine,
|
||||
@ -145,11 +146,9 @@ private:
|
||||
};
|
||||
|
||||
struct DecoderData {
|
||||
DecoderData(MediaData::Type aType,
|
||||
DecoderData(const char* aMonitorName,
|
||||
uint32_t aDecodeAhead)
|
||||
: mType(aType)
|
||||
, mMonitor(aType == MediaData::AUDIO_DATA ? "MP4 audio decoder data"
|
||||
: "MP4 video decoder data")
|
||||
: mMonitor(aMonitorName)
|
||||
, mNumSamplesInput(0)
|
||||
, mNumSamplesOutput(0)
|
||||
, mDecodeAhead(aDecodeAhead)
|
||||
@ -157,6 +156,7 @@ private:
|
||||
, mInputExhausted(false)
|
||||
, mError(false)
|
||||
, mIsFlushing(false)
|
||||
, mOutputRequested(false)
|
||||
, mUpdateScheduled(false)
|
||||
, mDemuxEOS(false)
|
||||
, mDrainComplete(false)
|
||||
@ -174,8 +174,6 @@ private:
|
||||
// Decoded samples returned my mDecoder awaiting being returned to
|
||||
// state machine upon request.
|
||||
nsTArray<nsRefPtr<MediaData> > mOutput;
|
||||
// Disambiguate Audio vs Video.
|
||||
MediaData::Type mType;
|
||||
|
||||
// Monitor that protects all non-threadsafe state; the primitives
|
||||
// that follow.
|
||||
@ -188,6 +186,7 @@ private:
|
||||
bool mInputExhausted;
|
||||
bool mError;
|
||||
bool mIsFlushing;
|
||||
bool mOutputRequested;
|
||||
bool mUpdateScheduled;
|
||||
bool mDemuxEOS;
|
||||
bool mDrainComplete;
|
||||
|
@ -94,22 +94,18 @@ MediaSourceReader::IsWaitingMediaResources()
|
||||
return !mHasEssentialTrackBuffers;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
void
|
||||
MediaSourceReader::RequestAudioData()
|
||||
{
|
||||
nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
|
||||
MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
|
||||
if (!mAudioReader) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
|
||||
mAudioPromise.Reject(DECODE_ERROR, __func__);
|
||||
return p;
|
||||
GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
|
||||
return;
|
||||
}
|
||||
mAudioIsSeeking = false;
|
||||
SwitchAudioReader(mLastAudioTime);
|
||||
mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded);
|
||||
return p;
|
||||
mAudioReader->RequestAudioData();
|
||||
}
|
||||
|
||||
void
|
||||
@ -121,9 +117,7 @@ MediaSourceReader::OnAudioDecoded(AudioData* aSample)
|
||||
if (aSample->mTime < mTimeThreshold) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
|
||||
this, aSample->mTime, mTimeThreshold);
|
||||
mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded);
|
||||
mAudioReader->RequestAudioData();
|
||||
return;
|
||||
}
|
||||
mDropAudioBeforeThreshold = false;
|
||||
@ -135,84 +129,18 @@ MediaSourceReader::OnAudioDecoded(AudioData* aSample)
|
||||
if (!mAudioIsSeeking) {
|
||||
mLastAudioTime = aSample->mTime + aSample->mDuration;
|
||||
}
|
||||
|
||||
mAudioPromise.Resolve(aSample, __func__);
|
||||
}
|
||||
|
||||
// Find the closest approximation to the end time for this stream.
|
||||
// mLast{Audio,Video}Time differs from the actual end time because of
|
||||
// Bug 1065207 - the duration of a WebM fragment is an estimate not the
|
||||
// actual duration. In the case of audio time an example of where they
|
||||
// differ would be the actual sample duration being small but the
|
||||
// previous sample being large. The buffered end time uses that last
|
||||
// sample duration as an estimate of the end time duration giving an end
|
||||
// time that is greater than mLastAudioTime, which is the actual sample
|
||||
// end time.
|
||||
// Reader switching is based on the buffered end time though so they can be
|
||||
// quite different. By using the EOS_FUZZ_US and the buffered end time we
|
||||
// attempt to account for this difference.
|
||||
static void
|
||||
AdjustEndTime(int64_t* aEndTime, MediaDecoderReader* aReader)
|
||||
{
|
||||
if (aReader) {
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
aReader->GetBuffered(ranges);
|
||||
if (ranges->Length() > 0) {
|
||||
// End time is a double so we convert to nearest by adding 0.5.
|
||||
int64_t end = ranges->GetEndTime() * USECS_PER_S + 0.5;
|
||||
*aEndTime = std::max(*aEndTime, end);
|
||||
}
|
||||
}
|
||||
GetCallback()->OnAudioDecoded(aSample);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
|
||||
{
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnAudioNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
|
||||
if (aReason == DECODE_ERROR || aReason == CANCELED) {
|
||||
mAudioPromise.Reject(aReason, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// End of stream. Force switching past this stream to another reader by
|
||||
// switching to the end of the buffered range.
|
||||
MOZ_ASSERT(aReason == END_OF_STREAM);
|
||||
if (mAudioReader) {
|
||||
AdjustEndTime(&mLastAudioTime, mAudioReader);
|
||||
}
|
||||
|
||||
// See if we can find a different reader that can pick up where we left off. We use the
|
||||
// EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
|
||||
// 1065207.
|
||||
if (SwitchAudioReader(mLastAudioTime + EOS_FUZZ_US)) {
|
||||
mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnAudioDecoded,
|
||||
&MediaSourceReader::OnAudioNotDecoded);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the entire MediaSource is done, generate an EndOfStream.
|
||||
if (IsEnded()) {
|
||||
mAudioPromise.Reject(END_OF_STREAM, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't have the data the caller wants. Tell that we're waiting for JS to
|
||||
// give us more data.
|
||||
mAudioPromise.Reject(WAITING_FOR_DATA, __func__);
|
||||
}
|
||||
|
||||
|
||||
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
||||
MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
|
||||
{
|
||||
nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
|
||||
MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
|
||||
this, aSkipToNextKeyframe, aTimeThreshold);
|
||||
if (!mVideoReader) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
|
||||
mVideoPromise.Reject(DECODE_ERROR, __func__);
|
||||
return p;
|
||||
GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, DECODE_ERROR);
|
||||
return;
|
||||
}
|
||||
if (aSkipToNextKeyframe) {
|
||||
mTimeThreshold = aTimeThreshold;
|
||||
@ -221,11 +149,7 @@ MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThres
|
||||
}
|
||||
mVideoIsSeeking = false;
|
||||
SwitchVideoReader(mLastVideoTime);
|
||||
|
||||
mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
|
||||
return p;
|
||||
mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
|
||||
}
|
||||
|
||||
void
|
||||
@ -237,9 +161,7 @@ MediaSourceReader::OnVideoDecoded(VideoData* aSample)
|
||||
if (aSample->mTime < mTimeThreshold) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
|
||||
this, aSample->mTime, mTimeThreshold);
|
||||
mVideoReader->RequestVideoData(false, 0)->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded,
|
||||
&MediaSourceReader::OnVideoNotDecoded);
|
||||
mVideoReader->RequestVideoData(false, 0);
|
||||
return;
|
||||
}
|
||||
mDropVideoBeforeThreshold = false;
|
||||
@ -251,46 +173,67 @@ MediaSourceReader::OnVideoDecoded(VideoData* aSample)
|
||||
if (!mVideoIsSeeking) {
|
||||
mLastVideoTime = aSample->mTime + aSample->mDuration;
|
||||
}
|
||||
|
||||
mVideoPromise.Resolve(aSample, __func__);
|
||||
GetCallback()->OnVideoDecoded(aSample);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
|
||||
MediaSourceReader::OnNotDecoded(MediaData::Type aType, NotDecodedReason aReason)
|
||||
{
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
|
||||
MSE_DEBUG("MediaSourceReader(%p)::OnNotDecoded aType=%u aReason=%u IsEnded: %d", this, aType, aReason, IsEnded());
|
||||
if (aReason == DECODE_ERROR || aReason == CANCELED) {
|
||||
mVideoPromise.Reject(aReason, __func__);
|
||||
GetCallback()->OnNotDecoded(aType, aReason);
|
||||
return;
|
||||
}
|
||||
|
||||
// End of stream. Force switching past this stream to another reader by
|
||||
// switching to the end of the buffered range.
|
||||
MOZ_ASSERT(aReason == END_OF_STREAM);
|
||||
if (mVideoReader) {
|
||||
AdjustEndTime(&mLastVideoTime, mAudioReader);
|
||||
nsRefPtr<MediaDecoderReader> reader = aType == MediaData::AUDIO_DATA ?
|
||||
mAudioReader : mVideoReader;
|
||||
|
||||
// Find the closest approximation to the end time for this stream.
|
||||
// mLast{Audio,Video}Time differs from the actual end time because of
|
||||
// Bug 1065207 - the duration of a WebM fragment is an estimate not the
|
||||
// actual duration. In the case of audio time an example of where they
|
||||
// differ would be the actual sample duration being small but the
|
||||
// previous sample being large. The buffered end time uses that last
|
||||
// sample duration as an estimate of the end time duration giving an end
|
||||
// time that is greater than mLastAudioTime, which is the actual sample
|
||||
// end time.
|
||||
// Reader switching is based on the buffered end time though so they can be
|
||||
// quite different. By using the EOS_FUZZ_US and the buffered end time we
|
||||
// attempt to account for this difference.
|
||||
int64_t* time = aType == MediaData::AUDIO_DATA ? &mLastAudioTime : &mLastVideoTime;
|
||||
if (reader) {
|
||||
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
|
||||
reader->GetBuffered(ranges);
|
||||
if (ranges->Length() > 0) {
|
||||
// End time is a double so we convert to nearest by adding 0.5.
|
||||
int64_t end = ranges->GetEndTime() * USECS_PER_S + 0.5;
|
||||
*time = std::max(*time, end);
|
||||
}
|
||||
}
|
||||
|
||||
// See if we can find a different reader that can pick up where we left off. We use the
|
||||
// EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
|
||||
// 1065207.
|
||||
if (SwitchVideoReader(mLastVideoTime + EOS_FUZZ_US)) {
|
||||
mVideoReader->RequestVideoData(false, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded,
|
||||
&MediaSourceReader::OnVideoNotDecoded);
|
||||
// 1065207 - the duration of a WebM frame is an estimate.
|
||||
if (aType == MediaData::AUDIO_DATA && SwitchAudioReader(*time + EOS_FUZZ_US)) {
|
||||
RequestAudioData();
|
||||
return;
|
||||
}
|
||||
if (aType == MediaData::VIDEO_DATA && SwitchVideoReader(*time + EOS_FUZZ_US)) {
|
||||
RequestVideoData(false, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the entire MediaSource is done, generate an EndOfStream.
|
||||
if (IsEnded()) {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
GetCallback()->OnNotDecoded(aType, END_OF_STREAM);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't have the data the caller wants. Tell that we're waiting for JS to
|
||||
// give us more data.
|
||||
mVideoPromise.Reject(WAITING_FOR_DATA, __func__);
|
||||
GetCallback()->OnNotDecoded(aType, WAITING_FOR_DATA);
|
||||
}
|
||||
|
||||
void
|
||||
@ -304,9 +247,6 @@ MediaSourceReader::Shutdown()
|
||||
mAudioReader = nullptr;
|
||||
mVideoTrack = nullptr;
|
||||
mVideoReader = nullptr;
|
||||
|
||||
MOZ_ASSERT(mAudioPromise.IsEmpty());
|
||||
MOZ_ASSERT(mVideoPromise.IsEmpty());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -46,14 +46,15 @@ public:
|
||||
|
||||
bool IsWaitingMediaResources() MOZ_OVERRIDE;
|
||||
|
||||
nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
|
||||
nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
void RequestAudioData() MOZ_OVERRIDE;
|
||||
|
||||
void OnAudioDecoded(AudioData* aSample);
|
||||
void OnAudioNotDecoded(NotDecodedReason aReason);
|
||||
|
||||
void RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
void OnVideoDecoded(VideoData* aSample);
|
||||
void OnVideoNotDecoded(NotDecodedReason aReason);
|
||||
|
||||
void OnNotDecoded(MediaData::Type aType, NotDecodedReason aReason);
|
||||
|
||||
void OnSeekCompleted(nsresult aResult);
|
||||
|
||||
|
@ -347,8 +347,6 @@ MediaCodecReader::ReleaseMediaResources()
|
||||
void
|
||||
MediaCodecReader::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(mAudioPromise.IsEmpty());
|
||||
MOZ_ASSERT(mVideoPromise.IsEmpty());
|
||||
ReleaseResources();
|
||||
MediaDecoderReader::Shutdown();
|
||||
}
|
||||
@ -376,28 +374,23 @@ MediaCodecReader::DispatchVideoTask(int64_t aTimeThreshold)
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
void
|
||||
MediaCodecReader::RequestAudioData()
|
||||
{
|
||||
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
|
||||
MOZ_ASSERT(HasAudio());
|
||||
|
||||
nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
|
||||
if (CheckAudioResources()) {
|
||||
DispatchAudioTask();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
||||
void
|
||||
MediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
|
||||
MOZ_ASSERT(HasVideo());
|
||||
|
||||
nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
|
||||
int64_t threshold = sInvalidTimestampUs;
|
||||
if (aSkipToNextKeyframe && IsValidTimestampUs(aTimeThreshold)) {
|
||||
mVideoTrack.mTaskQueue->Flush();
|
||||
@ -406,8 +399,6 @@ MediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
if (CheckVideoResources()) {
|
||||
DispatchVideoTask(threshold);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -493,11 +484,11 @@ MediaCodecReader::DecodeAudioDataTask()
|
||||
a->mDiscontinuity = true;
|
||||
mAudioTrack.mDiscontinuity = false;
|
||||
}
|
||||
mAudioPromise.Resolve(a, __func__);
|
||||
GetCallback()->OnAudioDecoded(a);
|
||||
}
|
||||
}
|
||||
else if (AudioQueue().AtEndOfStream()) {
|
||||
mAudioPromise.Reject(END_OF_STREAM, __func__);
|
||||
if (AudioQueue().AtEndOfStream()) {
|
||||
GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, END_OF_STREAM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -513,11 +504,11 @@ MediaCodecReader::DecodeVideoFrameTask(int64_t aTimeThreshold)
|
||||
v->mDiscontinuity = true;
|
||||
mVideoTrack.mDiscontinuity = false;
|
||||
}
|
||||
mVideoPromise.Resolve(v, __func__);
|
||||
GetCallback()->OnVideoDecoded(v);
|
||||
}
|
||||
}
|
||||
else if (VideoQueue().AtEndOfStream()) {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
if (VideoQueue().AtEndOfStream()) {
|
||||
GetCallback()->OnNotDecoded(MediaData::VIDEO_DATA, END_OF_STREAM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -79,12 +79,11 @@ public:
|
||||
virtual nsresult ResetDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeVideoFrameTask to decode video data.
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeAduioDataTask to decode video data.
|
||||
virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
|
||||
virtual void RequestAudioData() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool HasAudio();
|
||||
virtual bool HasVideo();
|
||||
|
@ -74,19 +74,19 @@ RtspMediaCodecReader::EnsureActive()
|
||||
mRtspResource->SetSuspend(false);
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
void
|
||||
RtspMediaCodecReader::RequestAudioData()
|
||||
{
|
||||
EnsureActive();
|
||||
return MediaCodecReader::RequestAudioData();
|
||||
MediaCodecReader::RequestAudioData();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
||||
void
|
||||
RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
EnsureActive();
|
||||
return MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
|
||||
MediaCodecReader::RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -55,12 +55,11 @@ public:
|
||||
virtual void SetIdle() MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeVideoFrameTask to decode video data.
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeAudioDataTask to decode audio data.
|
||||
virtual nsRefPtr<AudioDataPromise> RequestAudioData() MOZ_OVERRIDE;
|
||||
virtual void RequestAudioData() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) MOZ_OVERRIDE;
|
||||
|
@ -261,10 +261,12 @@ MediaDecodeTask::Decode()
|
||||
}
|
||||
|
||||
MediaQueue<AudioData> audioQueue;
|
||||
nsRefPtr<AudioDecodeRendezvous> barrier(new AudioDecodeRendezvous(mDecoderReader));
|
||||
nsRefPtr<AudioDecodeRendezvous> barrier(new AudioDecodeRendezvous());
|
||||
mDecoderReader->SetCallback(barrier);
|
||||
while (1) {
|
||||
mDecoderReader->RequestAudioData();
|
||||
nsRefPtr<AudioData> audio;
|
||||
if (NS_FAILED(barrier->RequestAndWait(audio))) {
|
||||
if (NS_FAILED(barrier->Await(audio))) {
|
||||
mDecoderReader->Shutdown();
|
||||
ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent);
|
||||
return;
|
||||
|
@ -743,7 +743,7 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||
// Discard padding should be used only on the final packet, so
|
||||
// decoding after a padding discard is invalid.
|
||||
LOG(PR_LOG_DEBUG, ("Opus error, discard padding on interstitial packet"));
|
||||
mHitAudioDecodeError = true;
|
||||
GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -797,7 +797,8 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||
if (discardPadding < 0) {
|
||||
// Negative discard padding is invalid.
|
||||
LOG(PR_LOG_DEBUG, ("Opus error, negative discard padding"));
|
||||
mHitAudioDecodeError = true;
|
||||
GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
|
||||
return false;
|
||||
}
|
||||
if (discardPadding > 0) {
|
||||
CheckedInt64 discardFrames = UsecsToFrames(discardPadding / NS_PER_USEC,
|
||||
@ -809,7 +810,7 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||
if (discardFrames.value() > frames) {
|
||||
// Discarding more than the entire packet is invalid.
|
||||
LOG(PR_LOG_DEBUG, ("Opus error, discard padding larger than packet"));
|
||||
mHitAudioDecodeError = true;
|
||||
GetCallback()->OnNotDecoded(MediaData::AUDIO_DATA, DECODE_ERROR);
|
||||
return false;
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Opus decoder discarding %d of %d frames",
|
||||
|
Loading…
Reference in New Issue
Block a user