Bug 1336358: P2. Drain Android's RemoteDataDecoder one frame at a time. r=jolin
MozReview-Commit-ID: A9R9dR71FTB
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -76,18 +76,19 @@ public:
}
virtual void HandleError(const MediaResult& aError) = 0;
void OnError(bool aIsFatal)
{
if (!mCanceled) {
HandleError(
- aIsFatal ? MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__)
- : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
+ aIsFatal
+ ? MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__)
+ : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
}
}
void DisposeNative()
{
// TODO
}
@@ -141,17 +142,17 @@ public:
class CallbacksSupport final : public JavaCallbacksSupport
{
public:
CallbacksSupport(RemoteVideoDecoder* aDecoder) : mDecoder(aDecoder) { }
void HandleInputExhausted() override
{
- mDecoder->InputExhausted();
+ mDecoder->ReturnDecodedData();
}
void HandleOutput(Sample::Param aSample) override
{
UniquePtr<VideoData::Listener>
releaseSample(new RenderOrReleaseOutput(mDecoder->mJavaDecoder, aSample));
BufferInfo::LocalRef info = aSample->Info();
@@ -309,17 +310,17 @@ public:
bool formatHasCSD = false;
NS_ENSURE_SUCCESS_VOID(
aFormat->ContainsKey(NS_LITERAL_STRING("csd-0"), &formatHasCSD));
if (!formatHasCSD && aConfig.mCodecSpecificConfig->Length() >= 2) {
jni::ByteBuffer::LocalRef buffer(env);
buffer = jni::ByteBuffer::New(aConfig.mCodecSpecificConfig->Elements(),
- aConfig.mCodecSpecificConfig->Length());
+ aConfig.mCodecSpecificConfig->Length());
NS_ENSURE_SUCCESS_VOID(
aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"), buffer));
}
}
RefPtr<InitPromise> Init() override
{
// Register native methods.
@@ -342,17 +343,17 @@ public:
private:
class CallbacksSupport final : public JavaCallbacksSupport
{
public:
CallbacksSupport(RemoteAudioDecoder* aDecoder) : mDecoder(aDecoder) { }
void HandleInputExhausted() override
{
- mDecoder->InputExhausted();
+ mDecoder->ReturnDecodedData();
}
void HandleOutput(Sample::Param aSample) override
{
BufferInfo::LocalRef info = aSample->Info();
int32_t flags;
bool ok = NS_SUCCEEDED(info->Flags(&flags));
@@ -477,36 +478,50 @@ RemoteDataDecoder::RemoteDataDecoder(Med
{
}
RefPtr<MediaDataDecoder::FlushPromise>
RemoteDataDecoder::Flush()
{
RefPtr<RemoteDataDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+ mDecodedData.Clear();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+ mDrainStatus = DrainStatus::DRAINED;
mJavaDecoder->Flush();
return FlushPromise::CreateAndResolve(true, __func__);
});
}
RefPtr<MediaDataDecoder::DecodePromise>
RemoteDataDecoder::Drain()
{
RefPtr<RemoteDataDecoder> self = this;
return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+ RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
+ if (mDrainStatus == DrainStatus::DRAINED) {
+ // There's no operation to perform other than returning any already
+ // decoded data.
+ ReturnDecodedData();
+ return p;
+ }
+
+ if (mDrainStatus == DrainStatus::DRAINING) {
+ // Draining operation already pending, let it complete its course.
+ return p;
+ }
+
BufferInfo::LocalRef bufferInfo;
nsresult rv = BufferInfo::New(&bufferInfo);
if (NS_FAILED(rv)) {
return DecodePromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
}
+ mDrainStatus = DrainStatus::DRAINING;
bufferInfo->Set(0, 0, -1, MediaCodec::BUFFER_FLAG_END_OF_STREAM);
-
- RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
mJavaDecoder->Input(nullptr, bufferInfo, nullptr);
return p;
});
}
RefPtr<ShutdownPromise>
RemoteDataDecoder::Shutdown()
{
@@ -550,16 +565,17 @@ RemoteDataDecoder::Decode(MediaRawData*
BufferInfo::LocalRef bufferInfo;
nsresult rv = BufferInfo::New(&bufferInfo);
if (NS_FAILED(rv)) {
return DecodePromise::CreateAndReject(
MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
}
bufferInfo->Set(0, sample->Size(), sample->mTime, 0);
+ mDrainStatus = DrainStatus::DRAINABLE;
RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample));
return p;
});
}
void
RemoteDataDecoder::Output(MediaData* aSample)
@@ -569,60 +585,66 @@ RemoteDataDecoder::Output(MediaData* aSa
NewRunnableMethod<MediaData*>(this, &RemoteDataDecoder::Output, aSample));
return;
}
AssertOnTaskQueue();
if (mShutdown) {
return;
}
mDecodedData.AppendElement(aSample);
+ ReturnDecodedData();
}
void
-RemoteDataDecoder::InputExhausted()
+RemoteDataDecoder::ReturnDecodedData()
{
if (!mTaskQueue->IsCurrentThreadIn()) {
mTaskQueue->Dispatch(
- NewRunnableMethod(this, &RemoteDataDecoder::InputExhausted));
+ NewRunnableMethod(this, &RemoteDataDecoder::ReturnDecodedData));
return;
}
AssertOnTaskQueue();
if (mShutdown) {
return;
}
- mDecodePromise.ResolveIfExists(mDecodedData, __func__);
- mDecodedData.Clear();
+ // We only want to clear mDecodedData when we have resolved the promises.
+ if (!mDecodePromise.IsEmpty()) {
+ mDecodePromise.Resolve(mDecodedData, __func__);
+ mDecodedData.Clear();
+ } else if (!mDrainPromise.IsEmpty()) {
+ mDrainPromise.Resolve(mDecodedData, __func__);
+ mDecodedData.Clear();
+ }
}
void
RemoteDataDecoder::DrainComplete()
{
if (!mTaskQueue->IsCurrentThreadIn()) {
mTaskQueue->Dispatch(
NewRunnableMethod(this, &RemoteDataDecoder::DrainComplete));
return;
}
AssertOnTaskQueue();
if (mShutdown) {
return;
}
- mDrainPromise.ResolveIfExists(mDecodedData, __func__);
- mDecodedData.Clear();
+ mDrainStatus = DrainStatus::DRAINED;
+ ReturnDecodedData();
}
void
RemoteDataDecoder::Error(const MediaResult& aError)
{
if (!mTaskQueue->IsCurrentThreadIn()) {
mTaskQueue->Dispatch(
NewRunnableMethod<MediaResult>(this, &RemoteDataDecoder::Error, aError));
return;
}
AssertOnTaskQueue();
if (mShutdown) {
return;
}
mDecodePromise.RejectIfExists(aError, __func__);
mDrainPromise.RejectIfExists(aError, __func__);
- mDecodedData.Clear();
}
} // mozilla
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -43,17 +43,17 @@ protected:
RemoteDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
java::sdk::MediaFormat::Param aFormat,
const nsString& aDrmStubId, TaskQueue* aTaskQueue);
// Methods only called on mTaskQueue.
RefPtr<ShutdownPromise> ProcessShutdown();
void Output(MediaData* aSample);
- void InputExhausted();
+ void ReturnDecodedData();
void DrainComplete();
void Error(const MediaResult& aError);
void AssertOnTaskQueue()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
}
MediaData::Type mType;
@@ -65,14 +65,21 @@ protected:
java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
nsString mDrmStubId;
RefPtr<TaskQueue> mTaskQueue;
// Only ever accessed on mTaskqueue.
bool mShutdown = false;
MozPromiseHolder<DecodePromise> mDecodePromise;
MozPromiseHolder<DecodePromise> mDrainPromise;
+ enum class DrainStatus
+ {
+ DRAINED,
+ DRAINABLE,
+ DRAINING,
+ };
+ DrainStatus mDrainStatus = DrainStatus::DRAINED;
DecodedData mDecodedData;
};
} // namespace mozilla
#endif