Files
tubestation/dom/media/ipc/RemoteDecoderChild.cpp
alwu 4cb33ec75b Bug 1909299 - always reuse media engine decoder. r=jolin
Media enegine decoder is different from other decoders, it's act as an
intermediate layer to serve input data to the media engine. Media engine
itself would be able to handle the stream Id change.

If we don't do that, we might see visual defect happening at the
transition of the unencrypted playback to encrypted playback, because
recreating decoder could cause unnecessary data lose when calling
`Flush()` on the media engine decoder.

Differential Revision: https://phabricator.services.mozilla.com/D217315
2024-07-23 23:56:44 +00:00

323 lines
12 KiB
C++

/* -*- 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