RemoteVideoDecoder.cpp (12517B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "RemoteVideoDecoder.h" 7 8 #include "mozilla/layers/ImageDataSerializer.h" 9 10 #ifdef MOZ_AV1 11 # include "AOMDecoder.h" 12 # include "DAV1DDecoder.h" 13 #endif 14 #ifdef XP_WIN 15 # include "WMFDecoderModule.h" 16 #endif 17 #include "GPUVideoImage.h" 18 #include "ImageContainer.h" // for PlanarYCbCrData and BufferRecycleBin 19 #include "MediaDataDecoderProxy.h" 20 #include "MediaInfo.h" 21 #include "PDMFactory.h" 22 #include "RemoteCDMParent.h" 23 #include "RemoteImageHolder.h" 24 #include "RemoteMediaManagerParent.h" 25 #include "mozilla/StaticPrefs_media.h" 26 #include "mozilla/layers/ImageClient.h" 27 #include "mozilla/layers/TextureClient.h" 28 #include "mozilla/layers/VideoBridgeChild.h" 29 #ifdef MOZ_WIDGET_ANDROID 30 # include "mozilla/layers/VideoBridgeParent.h" 31 #endif 32 33 namespace mozilla { 34 35 using namespace layers; // for PlanarYCbCrData and BufferRecycleBin 36 using namespace ipc; 37 using namespace gfx; 38 39 layers::TextureForwarder* KnowsCompositorVideo::GetTextureForwarder() { 40 auto* vbc = VideoBridgeChild::GetSingleton(); 41 return (vbc && vbc->CanSend()) ? vbc : nullptr; 42 } 43 layers::LayersIPCActor* KnowsCompositorVideo::GetLayersIPCActor() { 44 return GetTextureForwarder(); 45 } 46 47 /* static */ already_AddRefed<KnowsCompositorVideo> 48 KnowsCompositorVideo::TryCreateForIdentifier( 49 const layers::TextureFactoryIdentifier& aIdentifier) { 50 VideoBridgeChild* child = VideoBridgeChild::GetSingleton(); 51 if (!child) { 52 return nullptr; 53 } 54 55 RefPtr<KnowsCompositorVideo> knowsCompositor = new KnowsCompositorVideo(); 56 knowsCompositor->IdentifyTextureHost(aIdentifier); 57 return knowsCompositor.forget(); 58 } 59 60 RemoteVideoDecoderChild::RemoteVideoDecoderChild(RemoteMediaIn aLocation) 61 : RemoteDecoderChild(aLocation), mBufferRecycleBin(new BufferRecycleBin) {} 62 63 MediaResult RemoteVideoDecoderChild::ProcessOutput( 64 DecodedOutputIPDL&& aDecodedData) { 65 AssertOnManagerThread(); 66 MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TArrayOfRemoteVideoData); 67 68 nsTArray<RemoteVideoData>& arrayData = 69 aDecodedData.get_ArrayOfRemoteVideoData()->Array(); 70 71 for (auto&& data : arrayData) { 72 if (data.image().IsEmpty()) { 73 // This is a NullData object. 74 mDecodedData.AppendElement(MakeRefPtr<NullData>( 75 data.base().offset(), data.base().time(), data.base().duration())); 76 continue; 77 } 78 RefPtr<Image> image = data.image().TransferToImage(mBufferRecycleBin); 79 80 RefPtr<VideoData> video = VideoData::CreateFromImage( 81 data.display(), data.base().offset(), data.base().time(), 82 data.base().duration(), image, data.base().keyframe(), 83 data.base().timecode()); 84 85 if (!video) { 86 // OOM 87 return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__); 88 } 89 mDecodedData.AppendElement(std::move(video)); 90 } 91 return NS_OK; 92 } 93 94 MediaResult RemoteVideoDecoderChild::InitIPDL( 95 const VideoInfo& aVideoInfo, float aFramerate, 96 const CreateDecoderParams::OptionSet& aOptions, 97 Maybe<layers::TextureFactoryIdentifier> aIdentifier, 98 const Maybe<uint64_t>& aMediaEngineId, const Maybe<TrackingId>& aTrackingId, 99 PRemoteCDMActor* aCDM) { 100 MOZ_ASSERT_IF(mLocation == RemoteMediaIn::GpuProcess, aIdentifier); 101 102 RefPtr<RemoteMediaManagerChild> manager = 103 RemoteMediaManagerChild::GetSingleton(mLocation); 104 105 // The manager isn't available because RemoteMediaManagerChild has been 106 // initialized with null end points and we don't want to decode video on RDD 107 // process anymore. Return false here so that we can fallback to other PDMs. 108 if (!manager) { 109 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 110 RESULT_DETAIL("RemoteMediaManager is not available.")); 111 } 112 113 if (!manager->CanSend()) { 114 if (mLocation == RemoteMediaIn::GpuProcess) { 115 // The manager doesn't support sending messages because we've just crashed 116 // and are working on reinitialization. Don't initialize mIPDLSelfRef and 117 // leave us in an error state. We'll then immediately reject the promise 118 // when Init() is called and the caller can try again. Hopefully by then 119 // the new manager is ready, or we've notified the caller of it being no 120 // longer available. If not, then the cycle repeats until we're ready. 121 return NS_OK; 122 } 123 124 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 125 RESULT_DETAIL("RemoteMediaManager unable to send.")); 126 } 127 128 // If we are given a remote CDM, we need to make sure that it has been remoted 129 // into the same process as the decoder. 130 PRemoteCDMChild* cdm = nullptr; 131 if (aCDM) { 132 if (aCDM->GetLocation() != mLocation) { 133 return MediaResult( 134 NS_ERROR_DOM_MEDIA_FATAL_ERR, 135 RESULT_DETAIL("PRemoteCDMActor is not in same process.")); 136 } 137 138 cdm = aCDM->AsPRemoteCDMChild(); 139 if (!cdm) { 140 return MediaResult( 141 NS_ERROR_DOM_MEDIA_FATAL_ERR, 142 RESULT_DETAIL("PRemoteCDMActor is not PRemoteCDMChild.")); 143 } 144 } 145 146 mIPDLSelfRef = this; 147 VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate); 148 MOZ_ALWAYS_TRUE(manager->SendPRemoteDecoderConstructor( 149 this, decoderInfo, aOptions, aIdentifier, aMediaEngineId, aTrackingId, 150 cdm)); 151 152 return NS_OK; 153 } 154 155 RemoteVideoDecoderParent::RemoteVideoDecoderParent( 156 RemoteMediaManagerParent* aParent, const VideoInfo& aVideoInfo, 157 float aFramerate, const CreateDecoderParams::OptionSet& aOptions, 158 const Maybe<layers::TextureFactoryIdentifier>& aIdentifier, 159 nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue, 160 const Maybe<uint64_t>& aMediaEngineId, Maybe<TrackingId> aTrackingId, 161 RemoteCDMParent* aCDM) 162 : RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue, 163 aMediaEngineId, std::move(aTrackingId), aCDM), 164 mVideoInfo(aVideoInfo), 165 mFramerate(aFramerate) { 166 if (aIdentifier) { 167 // Check to see if we have a direct PVideoBridge connection to the 168 // destination process specified in aIdentifier, and create a 169 // KnowsCompositor representing that connection if so. If this fails, then 170 // we fall back to returning the decoded frames directly via Output(). 171 mKnowsCompositor = 172 KnowsCompositorVideo::TryCreateForIdentifier(*aIdentifier); 173 } 174 } 175 176 IPCResult RemoteVideoDecoderParent::RecvConstruct( 177 ConstructResolver&& aResolver) { 178 auto imageContainer = MakeRefPtr<layers::ImageContainer>( 179 layers::ImageUsageType::RemoteVideoDecoder, 180 layers::ImageContainer::SYNCHRONOUS); 181 if (mKnowsCompositor && XRE_IsRDDProcess()) { 182 // Ensure to allocate recycle allocator 183 imageContainer->EnsureRecycleAllocatorForRDD(mKnowsCompositor); 184 } 185 auto params = CreateDecoderParams{ 186 mVideoInfo, 187 mKnowsCompositor, 188 imageContainer, 189 static_cast<PRemoteCDMActor*>(mCDM.get()), 190 CreateDecoderParams::VideoFrameRate(mFramerate), 191 mOptions, 192 CreateDecoderParams::WrapperSet({/* No wrapper */}), 193 mMediaEngineId, 194 mTrackingId, 195 mCDM, 196 }; 197 198 mParent->EnsurePDMFactory().CreateDecoder(params)->Then( 199 GetCurrentSerialEventTarget(), __func__, 200 [resolver = std::move(aResolver), self = RefPtr{this}]( 201 PlatformDecoderModule::CreateDecoderPromise::ResolveOrRejectValue&& 202 aValue) { 203 if (aValue.IsReject()) { 204 resolver(aValue.RejectValue()); 205 return; 206 } 207 MOZ_ASSERT(aValue.ResolveValue()); 208 self->mDecoder = 209 new MediaDataDecoderProxy(aValue.ResolveValue().forget(), 210 do_AddRef(self->mDecodeTaskQueue.get())); 211 resolver(NS_OK); 212 }); 213 return IPC_OK(); 214 } 215 216 MediaResult RemoteVideoDecoderParent::ProcessDecodedData( 217 MediaDataDecoder::DecodedData&& aData, DecodedOutputIPDL& aDecodedData) { 218 MOZ_ASSERT(OnManagerThread()); 219 220 // If the video decoder bridge has shut down, stop. 221 if (mKnowsCompositor && !mKnowsCompositor->GetTextureForwarder()) { 222 aDecodedData = MakeRefPtr<ArrayOfRemoteVideoData>(); 223 return NS_OK; 224 } 225 226 nsTArray<RemoteVideoData> array; 227 228 for (const auto& data : aData) { 229 MOZ_ASSERT(data->mType == MediaData::Type::VIDEO_DATA || 230 data->mType == MediaData::Type::NULL_DATA, 231 "Can only decode videos using RemoteDecoderParent!"); 232 if (data->mType == MediaData::Type::NULL_DATA) { 233 RemoteVideoData output( 234 MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode, 235 data->mDuration, data->mKeyframe), 236 IntSize(), RemoteImageHolder(), -1); 237 238 array.AppendElement(std::move(output)); 239 continue; 240 } 241 VideoData* video = static_cast<VideoData*>(data.get()); 242 243 MOZ_ASSERT(video->mImage, 244 "Decoded video must output a layer::Image to " 245 "be used with RemoteDecoderParent"); 246 247 RefPtr<TextureClient> texture; 248 SurfaceDescriptor sd; 249 IntSize size; 250 bool needStorage = false; 251 252 YUVColorSpace YUVColorSpace = gfx::YUVColorSpace::Default; 253 ColorSpace2 colorPrimaries = gfx::ColorSpace2::UNKNOWN; 254 TransferFunction transferFunction = gfx::TransferFunction::BT709; 255 ColorRange colorRange = gfx::ColorRange::LIMITED; 256 257 if (mKnowsCompositor) { 258 texture = video->mImage->GetTextureClient(mKnowsCompositor); 259 260 if (!texture) { 261 texture = ImageClient::CreateTextureClientForImage(video->mImage, 262 mKnowsCompositor); 263 } 264 265 if (texture) { 266 if (!texture->IsAddedToCompositableClient()) { 267 texture->InitIPDLActor(mKnowsCompositor, mParent->GetContentId()); 268 texture->SetAddedToCompositableClient(); 269 } 270 needStorage = true; 271 SurfaceDescriptorRemoteDecoder remoteSD; 272 texture->GetSurfaceDescriptorRemoteDecoder(&remoteSD); 273 sd = remoteSD; 274 size = texture->GetSize(); 275 } 276 } 277 278 // If failed to create a GPU accelerated surface descriptor, fall back to 279 // copying frames via shmem. 280 if (!IsSurfaceDescriptorValid(sd)) { 281 needStorage = false; 282 PlanarYCbCrImage* image = video->mImage->AsPlanarYCbCrImage(); 283 if (!image) { 284 return MediaResult(NS_ERROR_UNEXPECTED, 285 "Expected Planar YCbCr image in " 286 "RemoteVideoDecoderParent::ProcessDecodedData"); 287 } 288 YUVColorSpace = image->GetData()->mYUVColorSpace; 289 colorPrimaries = image->GetData()->mColorPrimaries; 290 transferFunction = image->GetData()->mTransferFunction; 291 colorRange = image->GetData()->mColorRange; 292 293 SurfaceDescriptorBuffer sdBuffer; 294 nsresult rv = image->BuildSurfaceDescriptorBuffer( 295 sdBuffer, Image::BuildSdbFlags::Default, [&](uint32_t aBufferSize) { 296 ShmemBuffer buffer = AllocateBuffer(aBufferSize); 297 if (buffer.Valid()) { 298 return MemoryOrShmem(std::move(buffer.Get())); 299 } 300 return MemoryOrShmem(); 301 }); 302 303 if (NS_WARN_IF(NS_FAILED(rv))) { 304 if (sdBuffer.data().type() == MemoryOrShmem::TShmem) { 305 DeallocShmem(sdBuffer.data().get_Shmem()); 306 } 307 return rv; 308 } 309 310 sd = sdBuffer; 311 size = image->GetSize(); 312 } 313 314 if (needStorage) { 315 MOZ_ASSERT(sd.type() != SurfaceDescriptor::TSurfaceDescriptorBuffer); 316 mParent->StoreImage(static_cast<const SurfaceDescriptorGPUVideo&>(sd), 317 video->mImage, texture); 318 } 319 320 RemoteVideoData output( 321 MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode, 322 data->mDuration, data->mKeyframe), 323 video->mDisplay, 324 RemoteImageHolder( 325 mParent, 326 XRE_IsGPUProcess() 327 ? VideoBridgeSource::GpuProcess 328 : (XRE_IsRDDProcess() 329 ? VideoBridgeSource::RddProcess 330 : VideoBridgeSource::MFMediaEngineCDMProcess), 331 size, video->mImage->GetColorDepth(), sd, YUVColorSpace, 332 colorPrimaries, transferFunction, colorRange), 333 video->mFrameID); 334 335 array.AppendElement(std::move(output)); 336 } 337 338 aDecodedData = MakeRefPtr<ArrayOfRemoteVideoData>(std::move(array)); 339 340 return NS_OK; 341 } 342 343 } // namespace mozilla