зеркало из https://github.com/mozilla/gecko-dev.git
291 строка
10 KiB
C++
291 строка
10 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 "RemoteVideoDecoder.h"
|
|
|
|
#include "mozilla/layers/ImageDataSerializer.h"
|
|
|
|
#ifdef MOZ_AV1
|
|
# include "AOMDecoder.h"
|
|
# include "DAV1DDecoder.h"
|
|
#endif
|
|
#ifdef XP_WIN
|
|
# include "WMFDecoderModule.h"
|
|
#endif
|
|
#include "GPUVideoImage.h"
|
|
#include "ImageContainer.h" // for PlanarYCbCrData and BufferRecycleBin
|
|
#include "MediaDataDecoderProxy.h"
|
|
#include "MediaInfo.h"
|
|
#include "PDMFactory.h"
|
|
#include "RemoteDecoderManagerParent.h"
|
|
#include "RemoteImageHolder.h"
|
|
#include "mozilla/StaticPrefs_media.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/layers/ImageClient.h"
|
|
#include "mozilla/layers/TextureClient.h"
|
|
#include "mozilla/layers/VideoBridgeChild.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace layers; // for PlanarYCbCrData and BufferRecycleBin
|
|
using namespace ipc;
|
|
using namespace gfx;
|
|
|
|
layers::TextureForwarder* KnowsCompositorVideo::GetTextureForwarder() {
|
|
auto* vbc = VideoBridgeChild::GetSingleton();
|
|
return (vbc && vbc->CanSend()) ? vbc : nullptr;
|
|
}
|
|
layers::LayersIPCActor* KnowsCompositorVideo::GetLayersIPCActor() {
|
|
return GetTextureForwarder();
|
|
}
|
|
|
|
/* static */ already_AddRefed<KnowsCompositorVideo>
|
|
KnowsCompositorVideo::TryCreateForIdentifier(
|
|
const layers::TextureFactoryIdentifier& aIdentifier) {
|
|
VideoBridgeChild* child = VideoBridgeChild::GetSingleton();
|
|
if (!child) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<KnowsCompositorVideo> knowsCompositor = new KnowsCompositorVideo();
|
|
knowsCompositor->IdentifyTextureHost(aIdentifier);
|
|
return knowsCompositor.forget();
|
|
}
|
|
|
|
RemoteVideoDecoderChild::RemoteVideoDecoderChild(RemoteDecodeIn aLocation)
|
|
: RemoteDecoderChild(aLocation == RemoteDecodeIn::GpuProcess),
|
|
mBufferRecycleBin(new BufferRecycleBin),
|
|
mLocation(aLocation) {}
|
|
|
|
MediaResult RemoteVideoDecoderChild::ProcessOutput(
|
|
DecodedOutputIPDL&& aDecodedData) {
|
|
AssertOnManagerThread();
|
|
MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TArrayOfRemoteVideoData);
|
|
|
|
nsTArray<RemoteVideoData>& arrayData =
|
|
aDecodedData.get_ArrayOfRemoteVideoData()->Array();
|
|
|
|
for (auto&& data : arrayData) {
|
|
if (data.image().IsEmpty()) {
|
|
// This is a NullData object.
|
|
mDecodedData.AppendElement(MakeRefPtr<NullData>(
|
|
data.base().offset(), data.base().time(), data.base().duration()));
|
|
continue;
|
|
}
|
|
RefPtr<Image> image = data.image().TransferToImage(mBufferRecycleBin);
|
|
|
|
RefPtr<VideoData> video = VideoData::CreateFromImage(
|
|
data.display(), data.base().offset(), data.base().time(),
|
|
data.base().duration(), image, data.base().keyframe(),
|
|
data.base().timecode());
|
|
|
|
if (!video) {
|
|
// OOM
|
|
return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
|
|
}
|
|
mDecodedData.AppendElement(std::move(video));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
MediaResult RemoteVideoDecoderChild::InitIPDL(
|
|
const VideoInfo& aVideoInfo, float aFramerate,
|
|
const CreateDecoderParams::OptionSet& aOptions,
|
|
const layers::TextureFactoryIdentifier* aIdentifier) {
|
|
MOZ_ASSERT_IF(mLocation == RemoteDecodeIn::GpuProcess, aIdentifier);
|
|
|
|
RefPtr<RemoteDecoderManagerChild> manager =
|
|
RemoteDecoderManagerChild::GetSingleton(mLocation);
|
|
|
|
// The manager isn't available because RemoteDecoderManagerChild has been
|
|
// initialized with null end points and we don't want to decode video on RDD
|
|
// process anymore. Return false here so that we can fallback to other PDMs.
|
|
if (!manager) {
|
|
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
RESULT_DETAIL("RemoteDecoderManager is not available."));
|
|
}
|
|
|
|
if (!manager->CanSend()) {
|
|
if (mLocation == RemoteDecodeIn::GpuProcess) {
|
|
// The manager doesn't support sending messages because we've just crashed
|
|
// and are working on reinitialization. Don't initialize mIPDLSelfRef and
|
|
// leave us in an error state. We'll then immediately reject the promise
|
|
// when Init() is called and the caller can try again. Hopefully by then
|
|
// the new manager is ready, or we've notified the caller of it being no
|
|
// longer available. If not, then the cycle repeats until we're ready.
|
|
return NS_OK;
|
|
}
|
|
|
|
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
|
RESULT_DETAIL("RemoteDecoderManager unable to send."));
|
|
}
|
|
|
|
mIPDLSelfRef = this;
|
|
bool success = false;
|
|
nsCString errorDescription;
|
|
VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate);
|
|
Unused << manager->SendPRemoteDecoderConstructor(this, decoderInfo, aOptions,
|
|
ToMaybe(aIdentifier),
|
|
&success, &errorDescription);
|
|
|
|
return success ? MediaResult(NS_OK)
|
|
: MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
|
|
}
|
|
|
|
RemoteVideoDecoderParent::RemoteVideoDecoderParent(
|
|
RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
|
|
float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
|
|
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
|
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue,
|
|
bool* aSuccess, nsCString* aErrorDescription)
|
|
: RemoteDecoderParent(aParent, aManagerThread, aDecodeTaskQueue),
|
|
mVideoInfo(aVideoInfo) {
|
|
if (aIdentifier) {
|
|
// Check to see if we have a direct PVideoBridge connection to the
|
|
// destination process specified in aIdentifier, and create a
|
|
// KnowsCompositor representing that connection if so. If this fails, then
|
|
// we fall back to returning the decoded frames directly via Output().
|
|
mKnowsCompositor =
|
|
KnowsCompositorVideo::TryCreateForIdentifier(*aIdentifier);
|
|
}
|
|
|
|
// It is possible for CreateDecoder() below to fail. In that case, we need to
|
|
// free the ImageContainer to avoid it leaking.
|
|
auto imageContainer = MakeRefPtr<layers::ImageContainer>();
|
|
if (mKnowsCompositor && XRE_IsRDDProcess()) {
|
|
// Ensure to allocate recycle allocator
|
|
imageContainer->EnsureRecycleAllocatorForRDD(mKnowsCompositor);
|
|
}
|
|
MediaResult error(NS_OK);
|
|
auto params = CreateDecoderParams{
|
|
mVideoInfo, mKnowsCompositor,
|
|
imageContainer, CreateDecoderParams::VideoFrameRate(aFramerate),
|
|
aOptions, CreateDecoderParams::NoWrapper(true),
|
|
&error};
|
|
|
|
auto& factory = aParent->EnsurePDMFactory();
|
|
RefPtr<MediaDataDecoder> decoder = factory.CreateDecoder(params);
|
|
|
|
if (NS_FAILED(error)) {
|
|
MOZ_ASSERT(aErrorDescription);
|
|
*aErrorDescription = error.Description();
|
|
}
|
|
|
|
if (decoder) {
|
|
mDecoder = new MediaDataDecoderProxy(decoder.forget(),
|
|
do_AddRef(mDecodeTaskQueue.get()));
|
|
}
|
|
*aSuccess = !!mDecoder;
|
|
}
|
|
|
|
MediaResult RemoteVideoDecoderParent::ProcessDecodedData(
|
|
MediaDataDecoder::DecodedData&& aData, DecodedOutputIPDL& aDecodedData) {
|
|
MOZ_ASSERT(OnManagerThread());
|
|
|
|
// If the video decoder bridge has shut down, stop.
|
|
if (mKnowsCompositor && !mKnowsCompositor->GetTextureForwarder()) {
|
|
aDecodedData = MakeRefPtr<ArrayOfRemoteVideoData>();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsTArray<RemoteVideoData> array;
|
|
|
|
for (const auto& data : aData) {
|
|
MOZ_ASSERT(data->mType == MediaData::Type::VIDEO_DATA ||
|
|
data->mType == MediaData::Type::NULL_DATA,
|
|
"Can only decode videos using RemoteDecoderParent!");
|
|
if (data->mType == MediaData::Type::NULL_DATA) {
|
|
RemoteVideoData output(
|
|
MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
|
|
data->mDuration, data->mKeyframe),
|
|
IntSize(), RemoteImageHolder(), -1);
|
|
|
|
array.AppendElement(std::move(output));
|
|
continue;
|
|
}
|
|
VideoData* video = static_cast<VideoData*>(data.get());
|
|
|
|
MOZ_ASSERT(video->mImage,
|
|
"Decoded video must output a layer::Image to "
|
|
"be used with RemoteDecoderParent");
|
|
|
|
RefPtr<TextureClient> texture;
|
|
SurfaceDescriptor sd;
|
|
IntSize size;
|
|
bool needStorage = false;
|
|
|
|
if (mKnowsCompositor) {
|
|
texture = video->mImage->GetTextureClient(mKnowsCompositor);
|
|
|
|
if (!texture) {
|
|
texture = ImageClient::CreateTextureClientForImage(video->mImage,
|
|
mKnowsCompositor);
|
|
}
|
|
|
|
if (texture) {
|
|
if (!texture->IsAddedToCompositableClient()) {
|
|
texture->InitIPDLActor(mKnowsCompositor);
|
|
texture->SetAddedToCompositableClient();
|
|
}
|
|
needStorage = true;
|
|
SurfaceDescriptorRemoteDecoder remoteSD;
|
|
texture->GetSurfaceDescriptorRemoteDecoder(&remoteSD);
|
|
sd = remoteSD;
|
|
size = texture->GetSize();
|
|
}
|
|
}
|
|
|
|
// If failed to create a GPU accelerated surface descriptor, fall back to
|
|
// copying frames via shmem.
|
|
if (!IsSurfaceDescriptorValid(sd)) {
|
|
needStorage = false;
|
|
PlanarYCbCrImage* image = video->mImage->AsPlanarYCbCrImage();
|
|
if (!image) {
|
|
return MediaResult(NS_ERROR_UNEXPECTED,
|
|
"Expected Planar YCbCr image in "
|
|
"RemoteVideoDecoderParent::ProcessDecodedData");
|
|
}
|
|
|
|
SurfaceDescriptorBuffer sdBuffer;
|
|
ShmemBuffer buffer = AllocateBuffer(image->GetDataSize());
|
|
if (!buffer.Valid()) {
|
|
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
|
"AllocShmem failed in "
|
|
"RemoteVideoDecoderParent::ProcessDecodedData");
|
|
}
|
|
|
|
sdBuffer.data() = std::move(buffer.Get());
|
|
image->BuildSurfaceDescriptorBuffer(sdBuffer);
|
|
|
|
sd = sdBuffer;
|
|
size = image->GetSize();
|
|
}
|
|
|
|
if (needStorage) {
|
|
MOZ_ASSERT(sd.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer);
|
|
mParent->StoreImage(static_cast<const SurfaceDescriptorGPUVideo&>(sd),
|
|
video->mImage, texture);
|
|
}
|
|
|
|
RemoteVideoData output(
|
|
MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
|
|
data->mDuration, data->mKeyframe),
|
|
video->mDisplay,
|
|
RemoteImageHolder(mParent,
|
|
XRE_IsGPUProcess() ? VideoBridgeSource::GpuProcess
|
|
: VideoBridgeSource::RddProcess,
|
|
size, sd),
|
|
video->mFrameID);
|
|
|
|
array.AppendElement(std::move(output));
|
|
}
|
|
|
|
aDecodedData = MakeRefPtr<ArrayOfRemoteVideoData>(std::move(array));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace mozilla
|