MFMediaEngineChild.cpp (15127B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "MFMediaEngineChild.h" 6 7 #include "MFMediaEngineUtils.h" 8 #include "RemoteMediaManagerChild.h" 9 10 #ifdef MOZ_WMF_CDM 11 # include "WMFCDMProxy.h" 12 #endif 13 14 namespace mozilla { 15 16 #define CLOG(msg, ...) \ 17 MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \ 18 ("MFMediaEngineChild=%p, Id=%" PRId64 ", " msg, this, this->Id(), \ 19 ##__VA_ARGS__)) 20 21 #define WLOG(msg, ...) \ 22 MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \ 23 ("MFMediaEngineWrapper=%p, Id=%" PRId64 ", " msg, this, this->Id(), \ 24 ##__VA_ARGS__)) 25 26 #define WLOGV(msg, ...) \ 27 MOZ_LOG(gMFMediaEngineLog, LogLevel::Verbose, \ 28 ("MFMediaEngineWrapper=%p, Id=%" PRId64 ", " msg, this, this->Id(), \ 29 ##__VA_ARGS__)) 30 31 using media::TimeUnit; 32 33 MFMediaEngineChild::MFMediaEngineChild(MFMediaEngineWrapper* aOwner, 34 FrameStatistics* aFrameStats) 35 : mOwner(aOwner), 36 mManagerThread(RemoteMediaManagerChild::GetManagerThread()), 37 mMediaEngineId(0 /* invalid id, will be initialized later */), 38 mFrameStats(WrapNotNull(aFrameStats)) { 39 if (mFrameStats->GetPresentedFrames() > 0) { 40 mAccumulatedPresentedFramesFromPrevEngine = 41 Some(mFrameStats->GetPresentedFrames()); 42 } 43 if (mFrameStats->GetDroppedSinkFrames() > 0) { 44 mAccumulatedDroppedFramesFromPrevEngine = 45 Some(mFrameStats->GetDroppedSinkFrames()); 46 } 47 } 48 49 RefPtr<GenericNonExclusivePromise> MFMediaEngineChild::Init( 50 const MediaInfo& aInfo, const ExternalPlaybackEngine::InitFlagSet& aFlags) { 51 if (!mManagerThread) { 52 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE, 53 __func__); 54 } 55 56 CLOG("Init, hasAudio=%d, hasVideo=%d, encrypted=%d", aInfo.HasAudio(), 57 aInfo.HasVideo(), aInfo.IsEncrypted()); 58 59 MOZ_ASSERT(mMediaEngineId == 0); 60 RefPtr<MFMediaEngineChild> self = this; 61 RemoteMediaManagerChild::LaunchUtilityProcessIfNeeded( 62 RemoteMediaIn::UtilityProcess_MFMediaEngineCDM) 63 ->Then( 64 mManagerThread, __func__, 65 [self, this, flag = aFlags, info = aInfo](bool) { 66 RefPtr<RemoteMediaManagerChild> manager = 67 RemoteMediaManagerChild::GetSingleton( 68 RemoteMediaIn::UtilityProcess_MFMediaEngineCDM); 69 if (!manager || !manager->CanSend()) { 70 CLOG("Manager not exists or can't send"); 71 mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__); 72 return; 73 } 74 75 mIPDLSelfRef = this; 76 (void)manager->SendPMFMediaEngineConstructor(this); 77 78 MediaInfoIPDL mediaInfo( 79 info.HasAudio() ? Some(info.mAudio) : Nothing(), 80 info.HasVideo() ? Some(info.mVideo) : Nothing()); 81 82 MediaEngineInfoIPDL initInfo( 83 mediaInfo, 84 flag.contains(ExternalPlaybackEngine::InitFlag::ShouldPreload), 85 flag.contains( 86 ExternalPlaybackEngine::InitFlag::EncryptedCustomIdent)); 87 SendInitMediaEngine(initInfo) 88 ->Then( 89 mManagerThread, __func__, 90 [self, this](uint64_t aId) { 91 mInitEngineRequest.Complete(); 92 // Id 0 is used to indicate error. 93 if (aId == 0) { 94 CLOG("Failed to initialize MFMediaEngineChild"); 95 mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, 96 __func__); 97 return; 98 } 99 mMediaEngineId = aId; 100 CLOG("Initialized MFMediaEngineChild"); 101 mInitPromiseHolder.ResolveIfExists(true, __func__); 102 }, 103 [self, 104 this](const mozilla::ipc::ResponseRejectReason& aReason) { 105 mInitEngineRequest.Complete(); 106 CLOG( 107 "Failed to initialize MFMediaEngineChild due to " 108 "IPC failure"); 109 mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, 110 __func__); 111 }) 112 ->Track(mInitEngineRequest); 113 }, 114 [self, this](nsresult aResult) { 115 CLOG("SendInitMediaEngine Failed"); 116 self->mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__); 117 }); 118 return mInitPromiseHolder.Ensure(__func__); 119 } 120 121 mozilla::ipc::IPCResult MFMediaEngineChild::RecvRequestSample(TrackType aType, 122 bool aIsEnough) { 123 AssertOnManagerThread(); 124 if (!mOwner || mShutdown) { 125 return IPC_OK(); 126 } 127 if (aType == TrackType::kVideoTrack) { 128 mOwner->NotifyEvent(aIsEnough ? ExternalEngineEvent::VideoEnough 129 : ExternalEngineEvent::RequestForVideo); 130 } else if (aType == TrackType::kAudioTrack) { 131 mOwner->NotifyEvent(aIsEnough ? ExternalEngineEvent::AudioEnough 132 : ExternalEngineEvent::RequestForAudio); 133 } 134 return IPC_OK(); 135 } 136 137 mozilla::ipc::IPCResult MFMediaEngineChild::RecvUpdateCurrentTime( 138 double aCurrentTimeInSecond) { 139 AssertOnManagerThread(); 140 if (mShutdown) { 141 return IPC_OK(); 142 } 143 if (mOwner) { 144 mOwner->UpdateCurrentTime(aCurrentTimeInSecond); 145 } 146 return IPC_OK(); 147 } 148 149 mozilla::ipc::IPCResult MFMediaEngineChild::RecvNotifyEvent( 150 MFMediaEngineEvent aEvent) { 151 AssertOnManagerThread(); 152 if (mShutdown) { 153 return IPC_OK(); 154 } 155 switch (aEvent) { 156 case MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY: 157 mOwner->NotifyEvent(ExternalEngineEvent::LoadedFirstFrame); 158 break; 159 case MF_MEDIA_ENGINE_EVENT_LOADEDDATA: 160 mOwner->NotifyEvent(ExternalEngineEvent::LoadedData); 161 break; 162 case MF_MEDIA_ENGINE_EVENT_WAITING: 163 mOwner->NotifyEvent(ExternalEngineEvent::Waiting); 164 break; 165 case MF_MEDIA_ENGINE_EVENT_SEEKED: 166 mOwner->NotifyEvent(ExternalEngineEvent::Seeked); 167 break; 168 case MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED: 169 mOwner->NotifyEvent(ExternalEngineEvent::BufferingStarted); 170 break; 171 case MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED: 172 mOwner->NotifyEvent(ExternalEngineEvent::BufferingEnded); 173 break; 174 case MF_MEDIA_ENGINE_EVENT_ENDED: 175 mOwner->NotifyEvent(ExternalEngineEvent::Ended); 176 break; 177 case MF_MEDIA_ENGINE_EVENT_PLAYING: 178 mOwner->NotifyEvent(ExternalEngineEvent::Playing); 179 break; 180 default: 181 NS_WARNING( 182 nsPrintfCString("Unhandled event=%s", MediaEngineEventToStr(aEvent)) 183 .get()); 184 break; 185 } 186 return IPC_OK(); 187 } 188 189 mozilla::ipc::IPCResult MFMediaEngineChild::RecvNotifyError( 190 const MediaResult& aError) { 191 AssertOnManagerThread(); 192 if (mShutdown) { 193 return IPC_OK(); 194 } 195 mOwner->NotifyError(aError); 196 return IPC_OK(); 197 } 198 199 mozilla::ipc::IPCResult MFMediaEngineChild::RecvUpdateStatisticData( 200 const StatisticData& aData) { 201 AssertOnManagerThread(); 202 const uint64_t currentRenderedFrames = mFrameStats->GetPresentedFrames(); 203 const uint64_t newRenderedFrames = GetUpdatedRenderedFrames(aData); 204 // Media engine won't tell us that which stage those dropped frames happened, 205 // so we treat all of them as the frames dropped in the a/v sync stage (sink). 206 const uint64_t currentDroppedSinkFrames = mFrameStats->GetDroppedSinkFrames(); 207 const uint64_t newDroppedSinkFrames = GetUpdatedDroppedFrames(aData); 208 mFrameStats->Accumulate({0, 0, newRenderedFrames - currentRenderedFrames, 0, 209 newDroppedSinkFrames - currentDroppedSinkFrames, 0}); 210 CLOG("Update statictis data (rendered %" PRIu64 " -> %" PRIu64 211 ", dropped %" PRIu64 " -> %" PRIu64 ")", 212 currentRenderedFrames, mFrameStats->GetPresentedFrames(), 213 currentDroppedSinkFrames, mFrameStats->GetDroppedSinkFrames()); 214 MOZ_ASSERT(mFrameStats->GetPresentedFrames() >= currentRenderedFrames); 215 MOZ_ASSERT(mFrameStats->GetDroppedSinkFrames() >= currentDroppedSinkFrames); 216 return IPC_OK(); 217 } 218 219 mozilla::ipc::IPCResult MFMediaEngineChild::RecvNotifyResizing( 220 uint32_t aWidth, uint32_t aHeight) { 221 AssertOnManagerThread(); 222 if (mShutdown) { 223 return IPC_OK(); 224 } 225 mOwner->NotifyResizing(aWidth, aHeight); 226 return IPC_OK(); 227 } 228 229 uint64_t MFMediaEngineChild::GetUpdatedRenderedFrames( 230 const StatisticData& aData) { 231 return mAccumulatedPresentedFramesFromPrevEngine 232 ? (aData.renderedFrames() + 233 *mAccumulatedPresentedFramesFromPrevEngine) 234 : aData.renderedFrames(); 235 } 236 237 uint64_t MFMediaEngineChild::GetUpdatedDroppedFrames( 238 const StatisticData& aData) { 239 return mAccumulatedDroppedFramesFromPrevEngine 240 ? (aData.droppedFrames() + 241 *mAccumulatedDroppedFramesFromPrevEngine) 242 : aData.droppedFrames(); 243 } 244 245 void MFMediaEngineChild::OwnerDestroyed() { 246 (void)ManagerThread()->Dispatch(NS_NewRunnableFunction( 247 "MFMediaEngineChild::OwnerDestroy", [self = RefPtr{this}, this] { 248 self->mOwner = nullptr; 249 // Ask to destroy IPDL. 250 if (CanSend()) { 251 MFMediaEngineChild::Send__delete__(this); 252 } 253 })); 254 } 255 256 void MFMediaEngineChild::IPDLActorDestroyed() { 257 AssertOnManagerThread(); 258 if (!mShutdown) { 259 CLOG("Destroyed actor without shutdown, remote process has crashed!"); 260 mOwner->NotifyError(NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_MF_CDM_ERR); 261 } 262 mIPDLSelfRef = nullptr; 263 } 264 265 void MFMediaEngineChild::Shutdown() { 266 AssertOnManagerThread(); 267 if (mShutdown) { 268 return; 269 } 270 SendShutdown(); 271 mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__); 272 mInitEngineRequest.DisconnectIfExists(); 273 mShutdown = true; 274 } 275 276 MFMediaEngineWrapper::MFMediaEngineWrapper(ExternalEngineStateMachine* aOwner, 277 FrameStatistics* aFrameStats) 278 : ExternalPlaybackEngine(aOwner), 279 mEngine(new MFMediaEngineChild(this, aFrameStats)), 280 mCurrentTimeInSecond(0.0) {} 281 282 RefPtr<GenericNonExclusivePromise> MFMediaEngineWrapper::Init( 283 const MediaInfo& aInfo, const InitFlagSet& aFlags) { 284 WLOG("Init"); 285 return mEngine->Init(aInfo, aFlags); 286 } 287 288 MFMediaEngineWrapper::~MFMediaEngineWrapper() { mEngine->OwnerDestroyed(); } 289 290 void MFMediaEngineWrapper::Play() { 291 WLOG("Play"); 292 MOZ_ASSERT(IsInited()); 293 (void)ManagerThread()->Dispatch( 294 NS_NewRunnableFunction("MFMediaEngineWrapper::Play", 295 [engine = mEngine] { engine->SendPlay(); })); 296 } 297 298 void MFMediaEngineWrapper::Pause() { 299 WLOG("Pause"); 300 MOZ_ASSERT(IsInited()); 301 (void)ManagerThread()->Dispatch( 302 NS_NewRunnableFunction("MFMediaEngineWrapper::Pause", 303 [engine = mEngine] { engine->SendPause(); })); 304 } 305 306 void MFMediaEngineWrapper::Seek(const TimeUnit& aTargetTime) { 307 auto currentTimeInSecond = aTargetTime.ToSeconds(); 308 mCurrentTimeInSecond = currentTimeInSecond; 309 WLOG("Seek to %f", currentTimeInSecond); 310 MOZ_ASSERT(IsInited()); 311 (void)ManagerThread()->Dispatch(NS_NewRunnableFunction( 312 "MFMediaEngineWrapper::Seek", [engine = mEngine, currentTimeInSecond] { 313 engine->SendSeek(currentTimeInSecond); 314 })); 315 } 316 317 void MFMediaEngineWrapper::Shutdown() { 318 WLOG("Shutdown"); 319 (void)ManagerThread()->Dispatch( 320 NS_NewRunnableFunction("MFMediaEngineWrapper::Shutdown", 321 [engine = mEngine] { engine->Shutdown(); })); 322 } 323 324 void MFMediaEngineWrapper::SetPlaybackRate(double aPlaybackRate) { 325 WLOG("Set playback rate %f", aPlaybackRate); 326 MOZ_ASSERT(IsInited()); 327 (void)ManagerThread()->Dispatch( 328 NS_NewRunnableFunction("MFMediaEngineWrapper::SetPlaybackRate", 329 [engine = mEngine, aPlaybackRate] { 330 engine->SendSetPlaybackRate(aPlaybackRate); 331 })); 332 } 333 334 void MFMediaEngineWrapper::SetVolume(double aVolume) { 335 WLOG("Set volume %f", aVolume); 336 MOZ_ASSERT(IsInited()); 337 (void)ManagerThread()->Dispatch(NS_NewRunnableFunction( 338 "MFMediaEngineWrapper::SetVolume", 339 [engine = mEngine, aVolume] { engine->SendSetVolume(aVolume); })); 340 } 341 342 void MFMediaEngineWrapper::SetLooping(bool aLooping) { 343 WLOG("Set looping %d", aLooping); 344 MOZ_ASSERT(IsInited()); 345 (void)ManagerThread()->Dispatch(NS_NewRunnableFunction( 346 "MFMediaEngineWrapper::SetLooping", 347 [engine = mEngine, aLooping] { engine->SendSetLooping(aLooping); })); 348 } 349 350 void MFMediaEngineWrapper::SetPreservesPitch(bool aPreservesPitch) { 351 // Media Engine doesn't support this. 352 } 353 354 void MFMediaEngineWrapper::NotifyEndOfStream(TrackInfo::TrackType aType) { 355 WLOG("NotifyEndOfStream, type=%s", TrackTypeToStr(aType)); 356 MOZ_ASSERT(IsInited()); 357 (void)ManagerThread()->Dispatch(NS_NewRunnableFunction( 358 "MFMediaEngineWrapper::NotifyEndOfStream", 359 [engine = mEngine, aType] { engine->SendNotifyEndOfStream(aType); })); 360 } 361 362 bool MFMediaEngineWrapper::SetCDMProxy(CDMProxy* aProxy) { 363 #ifdef MOZ_WMF_CDM 364 WMFCDMProxy* proxy = aProxy->AsWMFCDMProxy(); 365 if (!proxy) { 366 WLOG("Only WFMCDM Proxy is supported for the media engine!"); 367 return false; 368 } 369 370 const uint64_t proxyId = proxy->GetCDMProxyId(); 371 WLOG("SetCDMProxy, CDM-Id=%" PRIu64, proxyId); 372 MOZ_ASSERT(IsInited()); 373 (void)ManagerThread()->Dispatch(NS_NewRunnableFunction( 374 "MFMediaEngineWrapper::SetCDMProxy", 375 [engine = mEngine, proxy = RefPtr{aProxy}, proxyId] { 376 engine->SendSetCDMProxyId(proxyId); 377 })); 378 return true; 379 #else 380 return false; 381 #endif 382 } 383 384 TimeUnit MFMediaEngineWrapper::GetCurrentPosition() { 385 return TimeUnit::FromSeconds(mCurrentTimeInSecond); 386 } 387 388 void MFMediaEngineWrapper::UpdateCurrentTime(double aCurrentTimeInSecond) { 389 AssertOnManagerThread(); 390 WLOGV("Update current time %f", aCurrentTimeInSecond); 391 mCurrentTimeInSecond = aCurrentTimeInSecond; 392 NotifyEvent(ExternalEngineEvent::Timeupdate); 393 } 394 395 void MFMediaEngineWrapper::NotifyEvent(ExternalEngineEvent aEvent) { 396 AssertOnManagerThread(); 397 WLOGV("Received event %s", ExternalEngineEventToStr(aEvent)); 398 mOwner->NotifyEvent(aEvent); 399 } 400 401 void MFMediaEngineWrapper::NotifyError(const MediaResult& aError) { 402 AssertOnManagerThread(); 403 WLOG("Received error: %s", aError.Description().get()); 404 mOwner->NotifyError(aError); 405 } 406 407 void MFMediaEngineWrapper::NotifyResizing(uint32_t aWidth, uint32_t aHeight) { 408 AssertOnManagerThread(); 409 WLOG("Video resizing, new size [%u,%u]", aWidth, aHeight); 410 mOwner->NotifyResizing(aWidth, aHeight); 411 } 412 413 #undef CLOG 414 #undef WLOG 415 #undef WLOGV 416 417 } // namespace mozilla