MediaStreamTrack.cpp (20369B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "MediaStreamTrack.h" 7 8 #include "DOMMediaStream.h" 9 #include "MediaSegment.h" 10 #include "MediaStreamError.h" 11 #include "MediaTrackGraphImpl.h" 12 #include "MediaTrackListener.h" 13 #include "mozilla/BasePrincipal.h" 14 #include "mozilla/dom/Promise.h" 15 #include "nsContentUtils.h" 16 #include "nsGlobalWindowInner.h" 17 #include "nsIUUIDGenerator.h" 18 #include "nsServiceManagerUtils.h" 19 #include "systemservices/MediaUtils.h" 20 21 mozilla::LazyLogModule gMediaStreamTrackLog("MediaStreamTrack"); 22 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg) 23 24 using namespace mozilla::media; 25 26 namespace mozilla::dom { 27 28 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSource) 29 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSource) 30 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSource) 31 NS_INTERFACE_MAP_ENTRY(nsISupports) 32 NS_INTERFACE_MAP_END 33 34 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource) 35 36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamTrackSource) 37 tmp->Destroy(); 38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal) 39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 40 41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaStreamTrackSource) 42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) 43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 44 45 auto MediaStreamTrackSource::Clone() -> CloneResult { return {}; } 46 47 auto MediaStreamTrackSource::ApplyConstraints( 48 const dom::MediaTrackConstraints& aConstraints, CallerType aCallerType) 49 -> RefPtr<ApplyConstraintsPromise> { 50 return ApplyConstraintsPromise::CreateAndReject( 51 MakeRefPtr<MediaMgrError>(MediaMgrError::Name::OverconstrainedError, ""), 52 __func__); 53 } 54 55 /** 56 * MTGListener monitors state changes of the media flowing through the 57 * MediaTrackGraph. 58 * 59 * 60 * For changes to PrincipalHandle the following applies: 61 * 62 * When the main thread principal for a MediaStreamTrack changes, its principal 63 * will be set to the combination of the previous principal and the new one. 64 * 65 * As a PrincipalHandle change later happens on the MediaTrackGraph thread, we 66 * will be notified. If the latest principal on main thread matches the 67 * PrincipalHandle we just saw on MTG thread, we will set the track's principal 68 * to the new one. 69 * 70 * We know at this point that the old principal has been flushed out and data 71 * under it cannot leak to consumers. 72 * 73 * In case of multiple changes to the main thread state, the track's principal 74 * will be a combination of its old principal and all the new ones until the 75 * latest main thread principal matches the PrincipalHandle on the MTG thread. 76 */ 77 class MediaStreamTrack::MTGListener : public MediaTrackListener { 78 public: 79 explicit MTGListener(MediaStreamTrack* aTrack) : mTrack(aTrack) {} 80 81 void DoNotifyPrincipalHandleChanged( 82 const PrincipalHandle& aNewPrincipalHandle) { 83 MOZ_ASSERT(NS_IsMainThread()); 84 85 if (!mTrack) { 86 return; 87 } 88 89 mTrack->NotifyPrincipalHandleChanged(aNewPrincipalHandle); 90 } 91 92 void NotifyPrincipalHandleChanged( 93 MediaTrackGraph* aGraph, 94 const PrincipalHandle& aNewPrincipalHandle) override { 95 aGraph->DispatchToMainThreadStableState( 96 NewRunnableMethod<StoreCopyPassByConstLRef<PrincipalHandle>>( 97 "dom::MediaStreamTrack::MTGListener::" 98 "DoNotifyPrincipalHandleChanged", 99 this, &MTGListener::DoNotifyPrincipalHandleChanged, 100 aNewPrincipalHandle)); 101 } 102 103 void NotifyRemoved(MediaTrackGraph* aGraph) override { 104 // `mTrack` is a WeakPtr and must be destroyed on main thread. 105 // We dispatch ourselves to main thread here in case the MediaTrackGraph 106 // is holding the last reference to us. 107 aGraph->DispatchToMainThreadStableState( 108 NS_NewRunnableFunction("MediaStreamTrack::MTGListener::mTrackReleaser", 109 [self = RefPtr<MTGListener>(this)]() {})); 110 } 111 112 void DoNotifyEnded() { 113 MOZ_ASSERT(NS_IsMainThread()); 114 115 if (!mTrack) { 116 return; 117 } 118 119 if (!mTrack->GetParentObject()) { 120 return; 121 } 122 123 AbstractThread::MainThread()->Dispatch( 124 NewRunnableMethod("MediaStreamTrack::OverrideEnded", mTrack.get(), 125 &MediaStreamTrack::OverrideEnded)); 126 } 127 128 void NotifyEnded(MediaTrackGraph* aGraph) override { 129 aGraph->DispatchToMainThreadStableState( 130 NewRunnableMethod("MediaStreamTrack::MTGListener::DoNotifyEnded", this, 131 &MTGListener::DoNotifyEnded)); 132 } 133 134 protected: 135 // Main thread only. 136 WeakPtr<MediaStreamTrack> mTrack; 137 }; 138 139 class MediaStreamTrack::TrackSink : public MediaStreamTrackSource::Sink { 140 public: 141 explicit TrackSink(MediaStreamTrack* aTrack) : mTrack(aTrack) {} 142 143 /** 144 * Keep the track source alive. This track and any clones are controlling the 145 * lifetime of the source by being registered as its sinks. 146 */ 147 bool KeepsSourceAlive() const override { return true; } 148 149 bool Enabled() const override { 150 if (!mTrack) { 151 return false; 152 } 153 return mTrack->Enabled(); 154 } 155 156 void PrincipalChanged() override { 157 if (mTrack) { 158 mTrack->PrincipalChanged(); 159 } 160 } 161 162 void MutedChanged(bool aNewState) override { 163 if (mTrack) { 164 mTrack->MutedChanged(aNewState); 165 } 166 } 167 168 void ConstraintsChanged(const MediaTrackConstraints& aConstraints) override { 169 if (mTrack) { 170 mTrack->ConstraintsChanged(aConstraints); 171 } 172 } 173 174 void OverrideEnded() override { 175 if (mTrack) { 176 mTrack->OverrideEnded(); 177 } 178 } 179 180 private: 181 WeakPtr<MediaStreamTrack> mTrack; 182 }; 183 184 MediaStreamTrack::MediaStreamTrack(nsPIDOMWindowInner* aWindow, 185 mozilla::MediaTrack* aInputTrack, 186 MediaStreamTrackSource* aSource, 187 MediaStreamTrackState aReadyState, 188 bool aMuted, 189 const MediaTrackConstraints& aConstraints) 190 : mWindow(aWindow), 191 mInputTrack(aInputTrack), 192 mSource(aSource), 193 mSink(MakeUnique<TrackSink>(this)), 194 mPrincipal(aSource->GetPrincipal()), 195 mReadyState(aReadyState), 196 mEnabled(true), 197 mMuted(aMuted), 198 mConstraints(aConstraints) { 199 if (!Ended()) { 200 GetSource().RegisterSink(mSink.get()); 201 202 // Even if the input track is destroyed we need mTrack so that methods 203 // like AddListener still work. Keeping the number of paths to a minimum 204 // also helps prevent bugs elsewhere. We'll be ended through the 205 // MediaStreamTrackSource soon enough. 206 auto graph = mInputTrack->IsDestroyed() 207 ? MediaTrackGraph::GetInstanceIfExists( 208 mWindow, mInputTrack->mSampleRate, 209 MediaTrackGraph::DEFAULT_OUTPUT_DEVICE) 210 : mInputTrack->Graph(); 211 MOZ_DIAGNOSTIC_ASSERT(graph, 212 "A destroyed input track is only expected when " 213 "cloning, but since we're live there must be another " 214 "live track that is keeping the graph alive"); 215 216 mTrack = graph->CreateForwardedInputTrack(mInputTrack->mType); 217 mPort = mTrack->AllocateInputPort(mInputTrack); 218 mMTGListener = new MTGListener(this); 219 AddListener(mMTGListener); 220 } 221 222 nsresult rv; 223 nsCOMPtr<nsIUUIDGenerator> uuidgen = 224 do_GetService("@mozilla.org/uuid-generator;1", &rv); 225 226 nsID uuid; 227 memset(&uuid, 0, sizeof(uuid)); 228 if (uuidgen) { 229 uuidgen->GenerateUUIDInPlace(&uuid); 230 } 231 232 char chars[NSID_LENGTH]; 233 uuid.ToProvidedString(chars); 234 mID = NS_ConvertASCIItoUTF16(chars); 235 } 236 237 MediaStreamTrack::~MediaStreamTrack() { Destroy(); } 238 239 void MediaStreamTrack::Destroy() { 240 SetReadyState(MediaStreamTrackState::Ended); 241 // Remove all listeners -- avoid iterating over the list we're removing from 242 for (const auto& listener : mTrackListeners.Clone()) { 243 RemoveListener(listener); 244 } 245 // Do the same as above for direct listeners 246 for (const auto& listener : mDirectTrackListeners.Clone()) { 247 RemoveDirectListener(listener); 248 } 249 } 250 251 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack) 252 253 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack, 254 DOMEventTargetHelper) 255 tmp->Destroy(); 256 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 257 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource) 258 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal) 259 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPrincipal) 260 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR 261 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 262 263 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack, 264 DOMEventTargetHelper) 265 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 266 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource) 267 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) 268 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPrincipal) 269 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 270 271 NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper) 272 NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper) 273 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrack) 274 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 275 276 JSObject* MediaStreamTrack::WrapObject(JSContext* aCx, 277 JS::Handle<JSObject*> aGivenProto) { 278 return MediaStreamTrack_Binding::Wrap(aCx, this, aGivenProto); 279 } 280 281 void MediaStreamTrack::GetId(nsAString& aID) const { aID = mID; } 282 283 void MediaStreamTrack::SetEnabled(bool aEnabled) { 284 LOG(LogLevel::Info, 285 ("MediaStreamTrack %p %s", this, aEnabled ? "Enabled" : "Disabled")); 286 287 if (mEnabled == aEnabled) { 288 return; 289 } 290 291 mEnabled = aEnabled; 292 293 if (Ended()) { 294 return; 295 } 296 297 mTrack->SetDisabledTrackMode(mEnabled ? DisabledTrackMode::ENABLED 298 : DisabledTrackMode::SILENCE_BLACK); 299 NotifyEnabledChanged(); 300 } 301 302 void MediaStreamTrack::Stop() { 303 LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this)); 304 305 if (Ended()) { 306 LOG(LogLevel::Warning, ("MediaStreamTrack %p Already ended", this)); 307 return; 308 } 309 310 SetReadyState(MediaStreamTrackState::Ended); 311 312 NotifyEnded(); 313 } 314 315 void MediaStreamTrack::GetCapabilities(MediaTrackCapabilities& aResult, 316 CallerType aCallerType) { 317 GetSource().GetCapabilities(aResult); 318 } 319 320 void MediaStreamTrack::GetConstraints(dom::MediaTrackConstraints& aResult) { 321 aResult = mConstraints; 322 } 323 324 void MediaStreamTrack::GetSettings(dom::MediaTrackSettings& aResult, 325 CallerType aCallerType) { 326 GetSource().GetSettings(aResult); 327 328 // Spoof values when privacy.resistFingerprinting is true. 329 nsIGlobalObject* global = mWindow ? mWindow->AsGlobal() : nullptr; 330 if (!nsContentUtils::ShouldResistFingerprinting( 331 aCallerType, global, RFPTarget::StreamVideoFacingMode)) { 332 return; 333 } 334 if (aResult.mFacingMode.WasPassed()) { 335 aResult.mFacingMode.Value().AssignASCII( 336 GetEnumString(VideoFacingModeEnum::User)); 337 } 338 } 339 340 already_AddRefed<Promise> MediaStreamTrack::ApplyConstraints( 341 const MediaTrackConstraints& aConstraints, CallerType aCallerType, 342 ErrorResult& aRv) { 343 if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) { 344 nsString str; 345 aConstraints.ToJSON(str); 346 347 LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with " 348 "constraints %s", 349 this, NS_ConvertUTF16toUTF8(str).get())); 350 } 351 352 nsIGlobalObject* go = mWindow ? mWindow->AsGlobal() : nullptr; 353 354 RefPtr<Promise> promise = Promise::Create(go, aRv); 355 if (aRv.Failed()) { 356 return nullptr; 357 } 358 359 // Forward constraints to the source. 360 // 361 // After GetSource().ApplyConstraints succeeds (after it's been to 362 // media-thread and back), and no sooner, do we set mConstraints to the newly 363 // applied values. 364 365 // Keep a reference to this, to make sure it's still here when we get back. 366 RefPtr<MediaStreamTrack> self(this); 367 GetSource() 368 .ApplyConstraints(aConstraints, aCallerType) 369 ->Then( 370 GetCurrentSerialEventTarget(), __func__, 371 [this, self, promise, aConstraints](bool aDummy) { 372 if (!mWindow || !mWindow->IsCurrentInnerWindow()) { 373 return; // Leave Promise pending after navigation by design. 374 } 375 promise->MaybeResolve(false); 376 }, 377 [this, self, promise](const RefPtr<MediaMgrError>& aError) { 378 if (!mWindow || !mWindow->IsCurrentInnerWindow()) { 379 return; // Leave Promise pending after navigation by design. 380 } 381 promise->MaybeReject( 382 MakeRefPtr<MediaStreamError>(mWindow, *aError)); 383 }); 384 return promise.forget(); 385 } 386 387 ProcessedMediaTrack* MediaStreamTrack::GetTrack() const { 388 MOZ_DIAGNOSTIC_ASSERT(!Ended()); 389 return mTrack; 390 } 391 392 MediaTrackGraph* MediaStreamTrack::Graph() const { 393 MOZ_DIAGNOSTIC_ASSERT(!Ended()); 394 return mTrack->Graph(); 395 } 396 397 MediaTrackGraphImpl* MediaStreamTrack::GraphImpl() const { 398 MOZ_DIAGNOSTIC_ASSERT(!Ended()); 399 return mTrack->GraphImpl(); 400 } 401 402 void MediaStreamTrack::SetPrincipal(nsIPrincipal* aPrincipal) { 403 if (aPrincipal == mPrincipal) { 404 return; 405 } 406 mPrincipal = aPrincipal; 407 408 LOG(LogLevel::Info, 409 ("MediaStreamTrack %p principal changed to %p. Now: " 410 "null=%d, codebase=%d, expanded=%d, system=%d", 411 this, mPrincipal.get(), mPrincipal->GetIsNullPrincipal(), 412 mPrincipal->GetIsContentPrincipal(), 413 mPrincipal->GetIsExpandedPrincipal(), mPrincipal->IsSystemPrincipal())); 414 for (PrincipalChangeObserver<MediaStreamTrack>* observer : 415 mPrincipalChangeObservers) { 416 observer->PrincipalChanged(this); 417 } 418 } 419 420 void MediaStreamTrack::PrincipalChanged() { 421 mPendingPrincipal = GetSource().GetPrincipal(); 422 nsCOMPtr<nsIPrincipal> newPrincipal = mPrincipal; 423 LOG(LogLevel::Info, ("MediaStreamTrack %p Principal changed on main thread " 424 "to %p (pending). Combining with existing principal %p.", 425 this, mPendingPrincipal.get(), mPrincipal.get())); 426 if (nsContentUtils::CombineResourcePrincipals(&newPrincipal, 427 mPendingPrincipal)) { 428 SetPrincipal(newPrincipal); 429 } 430 } 431 432 void MediaStreamTrack::NotifyPrincipalHandleChanged( 433 const PrincipalHandle& aNewPrincipalHandle) { 434 PrincipalHandle handle(aNewPrincipalHandle); 435 LOG(LogLevel::Info, ("MediaStreamTrack %p principalHandle changed on " 436 "MediaTrackGraph thread to %p. Current principal: %p, " 437 "pending: %p", 438 this, GetPrincipalFromHandle(handle), mPrincipal.get(), 439 mPendingPrincipal.get())); 440 if (PrincipalHandleMatches(handle, mPendingPrincipal)) { 441 SetPrincipal(mPendingPrincipal); 442 mPendingPrincipal = nullptr; 443 } 444 } 445 446 void MediaStreamTrack::MutedChanged(bool aNewState) { 447 MOZ_ASSERT(NS_IsMainThread()); 448 449 /** 450 * 4.3.1 Life-cycle and Media flow - Media flow 451 * To set a track's muted state to newState, the User Agent MUST run the 452 * following steps: 453 * 1. Let track be the MediaStreamTrack in question. 454 * 2. Set track's muted attribute to newState. 455 * 3. If newState is true let eventName be mute, otherwise unmute. 456 * 4. Fire a simple event named eventName on track. 457 */ 458 459 if (mMuted == aNewState) { 460 return; 461 } 462 463 LOG(LogLevel::Info, 464 ("MediaStreamTrack %p became %s", this, aNewState ? "muted" : "unmuted")); 465 466 mMuted = aNewState; 467 468 if (Ended()) { 469 return; 470 } 471 472 nsString eventName = aNewState ? u"mute"_ns : u"unmute"_ns; 473 DispatchTrustedEvent(eventName); 474 } 475 476 void MediaStreamTrack::ConstraintsChanged( 477 const MediaTrackConstraints& aConstraints) { 478 MOZ_ASSERT(NS_IsMainThread()); 479 mConstraints = aConstraints; 480 } 481 482 void MediaStreamTrack::NotifyEnded() { 483 MOZ_ASSERT(mReadyState == MediaStreamTrackState::Ended); 484 485 for (const auto& consumer : mConsumers.Clone()) { 486 if (consumer) { 487 consumer->NotifyEnded(this); 488 } else { 489 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed"); 490 mConsumers.RemoveElement(consumer); 491 } 492 } 493 } 494 495 void MediaStreamTrack::NotifyEnabledChanged() { 496 GetSource().SinkEnabledStateChanged(); 497 498 for (const auto& consumer : mConsumers.Clone()) { 499 if (consumer) { 500 consumer->NotifyEnabledChanged(this, Enabled()); 501 } else { 502 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed"); 503 mConsumers.RemoveElement(consumer); 504 } 505 } 506 } 507 508 bool MediaStreamTrack::AddPrincipalChangeObserver( 509 PrincipalChangeObserver<MediaStreamTrack>* aObserver) { 510 // XXX(Bug 1631371) Check if this should use a fallible operation as it 511 // pretended earlier. 512 mPrincipalChangeObservers.AppendElement(aObserver); 513 return true; 514 } 515 516 bool MediaStreamTrack::RemovePrincipalChangeObserver( 517 PrincipalChangeObserver<MediaStreamTrack>* aObserver) { 518 return mPrincipalChangeObservers.RemoveElement(aObserver); 519 } 520 521 void MediaStreamTrack::AddConsumer(MediaStreamTrackConsumer* aConsumer) { 522 MOZ_ASSERT(!mConsumers.Contains(aConsumer)); 523 mConsumers.AppendElement(aConsumer); 524 525 // Remove destroyed consumers for cleanliness 526 while (mConsumers.RemoveElement(nullptr)) { 527 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed"); 528 } 529 } 530 531 void MediaStreamTrack::RemoveConsumer(MediaStreamTrackConsumer* aConsumer) { 532 mConsumers.RemoveElement(aConsumer); 533 534 // Remove destroyed consumers for cleanliness 535 while (mConsumers.RemoveElement(nullptr)) { 536 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed"); 537 } 538 } 539 540 void MediaStreamTrack::SetReadyState(MediaStreamTrackState aState) { 541 MOZ_ASSERT(!(mReadyState == MediaStreamTrackState::Ended && 542 aState == MediaStreamTrackState::Live), 543 "We don't support overriding the ready state from ended to live"); 544 545 if (Ended()) { 546 return; 547 } 548 549 if (mReadyState == MediaStreamTrackState::Live && 550 aState == MediaStreamTrackState::Ended) { 551 if (mSource) { 552 mSource->UnregisterSink(mSink.get()); 553 } 554 if (mMTGListener) { 555 RemoveListener(mMTGListener); 556 } 557 if (mPort) { 558 mPort->Destroy(); 559 } 560 if (mTrack) { 561 mTrack->Destroy(); 562 } 563 mPort = nullptr; 564 mTrack = nullptr; 565 mMTGListener = nullptr; 566 } 567 568 mReadyState = aState; 569 } 570 571 void MediaStreamTrack::OverrideEnded() { 572 MOZ_ASSERT(NS_IsMainThread()); 573 574 if (Ended()) { 575 return; 576 } 577 578 LOG(LogLevel::Info, ("MediaStreamTrack %p ended", this)); 579 580 SetReadyState(MediaStreamTrackState::Ended); 581 582 NotifyEnded(); 583 584 DispatchTrustedEvent(u"ended"_ns); 585 } 586 587 void MediaStreamTrack::AddListener(MediaTrackListener* aListener) { 588 LOG(LogLevel::Debug, 589 ("MediaStreamTrack %p adding listener %p", this, aListener)); 590 mTrackListeners.AppendElement(aListener); 591 592 if (Ended()) { 593 return; 594 } 595 mTrack->AddListener(aListener); 596 } 597 598 void MediaStreamTrack::RemoveListener(MediaTrackListener* aListener) { 599 LOG(LogLevel::Debug, 600 ("MediaStreamTrack %p removing listener %p", this, aListener)); 601 mTrackListeners.RemoveElement(aListener); 602 603 if (Ended()) { 604 return; 605 } 606 mTrack->RemoveListener(aListener); 607 } 608 609 void MediaStreamTrack::AddDirectListener(DirectMediaTrackListener* aListener) { 610 LOG(LogLevel::Debug, ("MediaStreamTrack %p (%s) adding direct listener %p to " 611 "track %p", 612 this, AsAudioStreamTrack() ? "audio" : "video", 613 aListener, mTrack.get())); 614 mDirectTrackListeners.AppendElement(aListener); 615 616 if (Ended()) { 617 return; 618 } 619 mTrack->AddDirectListener(aListener); 620 } 621 622 void MediaStreamTrack::RemoveDirectListener( 623 DirectMediaTrackListener* aListener) { 624 LOG(LogLevel::Debug, 625 ("MediaStreamTrack %p removing direct listener %p from track %p", this, 626 aListener, mTrack.get())); 627 mDirectTrackListeners.RemoveElement(aListener); 628 629 if (Ended()) { 630 return; 631 } 632 mTrack->RemoveDirectListener(aListener); 633 } 634 635 already_AddRefed<MediaInputPort> MediaStreamTrack::ForwardTrackContentsTo( 636 ProcessedMediaTrack* aTrack) { 637 MOZ_ASSERT(NS_IsMainThread()); 638 MOZ_RELEASE_ASSERT(aTrack); 639 return aTrack->AllocateInputPort(mTrack); 640 } 641 642 } // namespace mozilla::dom 643 644 #undef LOG