Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RemoteDecoderChild.h"
#include "RemoteDecoderManagerChild.h"
#include "mozilla/RemoteDecodeUtils.h"
namespace mozilla {
RemoteDecoderChild::RemoteDecoderChild(RemoteDecodeIn aLocation)
: ShmemRecycleAllocator(this),
mLocation(aLocation),
mThread(GetCurrentSerialEventTarget()) {
MOZ_DIAGNOSTIC_ASSERT(
RemoteDecoderManagerChild::GetManagerThread() &&
RemoteDecoderManagerChild::GetManagerThread()->IsOnCurrentThread(),
"Must be created on the manager thread");
}
RemoteDecoderChild::~RemoteDecoderChild() = default;
void RemoteDecoderChild::HandleRejectionError(
const ipc::ResponseRejectReason& aReason,
std::function<void(const MediaResult&)>&& aCallback) {
// If the channel goes down and CanSend() returns false, the IPDL promise will
// be rejected with SendError rather than ActorDestroyed. Both means the same
// thing and we can consider that the parent has crashed. The child can no
// longer be used.
//
// The GPU/RDD process crashed.
if (mLocation == RemoteDecodeIn::GpuProcess) {
// The GPU process will get automatically restarted by the parent process.
// Once it has been restarted the ContentChild will receive the message and
// will call GetManager()->InitForGPUProcess.
// We defer reporting an error until we've recreated the RemoteDecoder
// manager so that it'll be safe for MediaFormatReader to recreate decoders
RefPtr<RemoteDecoderChild> self = this;
GetManager()->RunWhenGPUProcessRecreated(NS_NewRunnableFunction(
"RemoteDecoderChild::HandleRejectionError",
[self, callback = std::move(aCallback)]() {
MediaResult error(
NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_RDD_OR_GPU_ERR,
__func__);
callback(error);
}));
return;
}
nsresult err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_UTILITY_ERR;
if (mLocation == RemoteDecodeIn::GpuProcess ||
mLocation == RemoteDecodeIn::RddProcess) {
err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_RDD_OR_GPU_ERR;
} else if (mLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) {
err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_MF_CDM_ERR;
}
// The RDD process is restarted on demand and asynchronously, we can
// immediately inform the caller that a new decoder is needed. The RDD will
// then be restarted during the new decoder creation by
aCallback(MediaResult(err, __func__));
}
// ActorDestroy is called if the channel goes down while waiting for a response.
void RemoteDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
mRemoteDecoderCrashed = (aWhy == AbnormalShutdown);
mDecodedData.Clear();
CleanupShmemRecycleAllocator();
RecordShutdownTelemetry(mRemoteDecoderCrashed);
}
void RemoteDecoderChild::DestroyIPDL() {
AssertOnManagerThread();
MOZ_DIAGNOSTIC_ASSERT(mInitPromise.IsEmpty() && mDecodePromise.IsEmpty() &&
mDrainPromise.IsEmpty() &&
mFlushPromise.IsEmpty() &&
mShutdownPromise.IsEmpty(),
"All promises should have been rejected");
if (CanSend()) {
PRemoteDecoderChild::Send__delete__(this);
}
}
void RemoteDecoderChild::IPDLActorDestroyed() { mIPDLSelfRef = nullptr; }
// MediaDataDecoder methods
RefPtr<MediaDataDecoder::InitPromise> RemoteDecoderChild::Init() {
AssertOnManagerThread();
mRemoteDecoderCrashed = false;
RefPtr<RemoteDecoderChild> self = this;
SendInit()
->Then(
mThread, __func__,
[self, this](InitResultIPDL&& aResponse) {
mInitPromiseRequest.Complete();
if (aResponse.type() == InitResultIPDL::TMediaResult) {
mInitPromise.Reject(aResponse.get_MediaResult(), __func__);
return;
}
const auto& initResponse = aResponse.get_InitCompletionIPDL();
mDescription = initResponse.decoderDescription();
mDescription.Append(" (");
mDescription.Append(RemoteDecodeInToStr(GetManager()->Location()));
mDescription.Append(" remote)");
mProcessName = initResponse.decoderProcessName();
mCodecName = initResponse.decoderCodecName();
mIsHardwareAccelerated = initResponse.hardware();
mHardwareAcceleratedReason = initResponse.hardwareReason();
mConversion = initResponse.conversion();
mShouldDecoderAlwaysBeRecycled =
initResponse.shouldDecoderAlwaysBeRecycled();
// Either the promise has not yet been resolved or the handler has
// been disconnected and we can't get here.
mInitPromise.Resolve(initResponse.type(), __func__);
},
[self](const mozilla::ipc::ResponseRejectReason& aReason) {
self->mInitPromiseRequest.Complete();
self->HandleRejectionError(
aReason, [self](const MediaResult& aError) {
self->mInitPromise.RejectIfExists(aError, __func__);
});
})
->Track(mInitPromiseRequest);
return mInitPromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Decode(
const nsTArray<RefPtr<MediaRawData>>& aSamples) {
AssertOnManagerThread();
if (mRemoteDecoderCrashed) {
nsresult err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_UTILITY_ERR;
if (mLocation == RemoteDecodeIn::GpuProcess ||
mLocation == RemoteDecodeIn::RddProcess) {
err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_RDD_OR_GPU_ERR;
} else if (mLocation == RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM) {
err = NS_ERROR_DOM_MEDIA_REMOTE_DECODER_CRASHED_MF_CDM_ERR;
}
return MediaDataDecoder::DecodePromise::CreateAndReject(err, __func__);
}
auto samples = MakeRefPtr<ArrayOfRemoteMediaRawData>();
if (!samples->Fill(aSamples,
[&](size_t aSize) { return AllocateBuffer(aSize); })) {
return MediaDataDecoder::DecodePromise::CreateAndReject(
NS_ERROR_OUT_OF_MEMORY, __func__);
}
SendDecode(samples)->Then(
mThread, __func__,
[self = RefPtr{this}, this](
PRemoteDecoderChild::DecodePromise::ResolveOrRejectValue&& aValue) {
// We no longer need the samples as the data has been
// processed by the parent.
// If the parent died, the error being fatal will cause the
// decoder to be torn down and all shmem in the pool will be
// deallocated.
ReleaseAllBuffers();
if (aValue.IsReject()) {
HandleRejectionError(
aValue.RejectValue(), [self](const MediaResult& aError) {
self->mDecodePromise.RejectIfExists(aError, __func__);
});
return;
}
MOZ_DIAGNOSTIC_ASSERT(CanSend(),
"The parent unexpectedly died, promise should "
"have been rejected first");
if (mDecodePromise.IsEmpty()) {
// We got flushed.
return;
}
auto response = std::move(aValue.ResolveValue());
if (response.type() == DecodeResultIPDL::TMediaResult &&
NS_FAILED(response.get_MediaResult())) {
mDecodePromise.Reject(response.get_MediaResult(), __func__);
return;
}
if (response.type() == DecodeResultIPDL::TDecodedOutputIPDL) {
ProcessOutput(std::move(response.get_DecodedOutputIPDL()));
}
mDecodePromise.Resolve(std::move(mDecodedData), __func__);
mDecodedData = MediaDataDecoder::DecodedData();
});
return mDecodePromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::FlushPromise> RemoteDecoderChild::Flush() {
AssertOnManagerThread();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
RefPtr<RemoteDecoderChild> self = this;
SendFlush()->Then(
mThread, __func__,
[self](const MediaResult& aResult) {
if (NS_SUCCEEDED(aResult)) {
self->mFlushPromise.ResolveIfExists(true, __func__);
} else {
self->mFlushPromise.RejectIfExists(aResult, __func__);
}
},
[self](const mozilla::ipc::ResponseRejectReason& aReason) {
self->HandleRejectionError(aReason, [self](const MediaResult& aError) {
self->mFlushPromise.RejectIfExists(aError, __func__);
});
});
return mFlushPromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Drain() {
AssertOnManagerThread();
RefPtr<RemoteDecoderChild> self = this;
SendDrain()->Then(
mThread, __func__,
[self, this](DecodeResultIPDL&& aResponse) {
if (mDrainPromise.IsEmpty()) {
// We got flushed.
return;
}
if (aResponse.type() == DecodeResultIPDL::TMediaResult &&
NS_FAILED(aResponse.get_MediaResult())) {
mDrainPromise.Reject(aResponse.get_MediaResult(), __func__);
return;
}
MOZ_DIAGNOSTIC_ASSERT(CanSend(),
"The parent unexpectedly died, promise should "
"have been rejected first");
if (aResponse.type() == DecodeResultIPDL::TDecodedOutputIPDL) {
ProcessOutput(std::move(aResponse.get_DecodedOutputIPDL()));
}
mDrainPromise.Resolve(std::move(mDecodedData), __func__);
mDecodedData = MediaDataDecoder::DecodedData();
},
[self](const mozilla::ipc::ResponseRejectReason& aReason) {
self->HandleRejectionError(aReason, [self](const MediaResult& aError) {
self->mDrainPromise.RejectIfExists(aError, __func__);
});
});
return mDrainPromise.Ensure(__func__);
}
RefPtr<mozilla::ShutdownPromise> RemoteDecoderChild::Shutdown() {
AssertOnManagerThread();
// Shutdown() can be called while an InitPromise is pending.
mInitPromiseRequest.DisconnectIfExists();
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
RefPtr<RemoteDecoderChild> self = this;
SendShutdown()->Then(
mThread, __func__,
[self](
PRemoteDecoderChild::ShutdownPromise::ResolveOrRejectValue&& aValue) {
self->mShutdownPromise.Resolve(aValue.IsResolve(), __func__);
});
return mShutdownPromise.Ensure(__func__);
}
bool RemoteDecoderChild::IsHardwareAccelerated(
nsACString& aFailureReason) const {
AssertOnManagerThread();
aFailureReason = mHardwareAcceleratedReason;
return mIsHardwareAccelerated;
}
nsCString RemoteDecoderChild::GetDescriptionName() const {
AssertOnManagerThread();
return mDescription;
}
nsCString RemoteDecoderChild::GetProcessName() const {
AssertOnManagerThread();
return mProcessName;
}
nsCString RemoteDecoderChild::GetCodecName() const {
AssertOnManagerThread();
return mCodecName;
}
void RemoteDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) {
AssertOnManagerThread();
Unused << SendSetSeekThreshold(aTime);
}
MediaDataDecoder::ConversionRequired RemoteDecoderChild::NeedsConversion()
const {
AssertOnManagerThread();
return mConversion;
}
bool RemoteDecoderChild::ShouldDecoderAlwaysBeRecycled() const {
AssertOnManagerThread();
return mShouldDecoderAlwaysBeRecycled;
}
void RemoteDecoderChild::AssertOnManagerThread() const {
MOZ_ASSERT(mThread->IsOnCurrentThread());
}
RemoteDecoderManagerChild* RemoteDecoderChild::GetManager() {
if (!CanSend()) {
return nullptr;
}
return static_cast<RemoteDecoderManagerChild*>(Manager());
}
} // namespace mozilla