DeviceInputTrack.cpp (25307B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=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 https://mozilla.org/MPL/2.0/. */ 6 7 #include "DeviceInputTrack.h" 8 9 #include "Tracing.h" 10 11 namespace mozilla { 12 13 #ifdef LOG_INTERNAL 14 # undef LOG_INTERNAL 15 #endif // LOG_INTERNAL 16 #define LOG_INTERNAL(level, msg, ...) \ 17 MOZ_LOG(gMediaTrackGraphLog, LogLevel::level, (msg, ##__VA_ARGS__)) 18 19 #ifdef LOG 20 # undef LOG 21 #endif // LOG 22 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) 23 24 #ifdef LOGE 25 # undef LOGE 26 #endif // LOGE 27 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__) 28 29 // This can only be called in graph thread since mGraph->CurrentDriver() is 30 // graph thread only 31 #ifdef TRACK_GRAPH_LOG_INTERNAL 32 # undef TRACK_GRAPH_LOG_INTERNAL 33 #endif // TRACK_GRAPH_LOG_INTERNAL 34 #define TRACK_GRAPH_LOG_INTERNAL(level, msg, ...) \ 35 LOG_INTERNAL(level, "(Graph %p, Driver %p) DeviceInputTrack %p, " msg, \ 36 this->mGraph, this->mGraph->CurrentDriver(), this, \ 37 ##__VA_ARGS__) 38 39 #ifdef TRACK_GRAPH_LOG 40 # undef TRACK_GRAPH_LOG 41 #endif // TRACK_GRAPH_LOG 42 #define TRACK_GRAPH_LOG(msg, ...) \ 43 TRACK_GRAPH_LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) 44 45 #ifdef TRACK_GRAPH_LOGV 46 # undef TRACK_GRAPH_LOGV 47 #endif // TRACK_GRAPH_LOGV 48 #define TRACK_GRAPH_LOGV(msg, ...) \ 49 TRACK_GRAPH_LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__) 50 51 #ifdef TRACK_GRAPH_LOGE 52 # undef TRACK_GRAPH_LOGE 53 #endif // TRACK_GRAPH_LOGE 54 #define TRACK_GRAPH_LOGE(msg, ...) \ 55 TRACK_GRAPH_LOG_INTERNAL(Error, msg, ##__VA_ARGS__) 56 57 #ifdef CONSUMER_GRAPH_LOG_INTERNAL 58 # undef CONSUMER_GRAPH_LOG_INTERNAL 59 #endif // CONSUMER_GRAPH_LOG_INTERNAL 60 #define CONSUMER_GRAPH_LOG_INTERNAL(level, msg, ...) \ 61 LOG_INTERNAL( \ 62 level, "(Graph %p, Driver %p) DeviceInputConsumerTrack %p, " msg, \ 63 this->mGraph, this->mGraph->CurrentDriver(), this, ##__VA_ARGS__) 64 65 #ifdef CONSUMER_GRAPH_LOGV 66 # undef CONSUMER_GRAPH_LOGV 67 #endif // CONSUMER_GRAPH_LOGV 68 #define CONSUMER_GRAPH_LOGV(msg, ...) \ 69 CONSUMER_GRAPH_LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__) 70 71 DeviceInputConsumerTrack::DeviceInputConsumerTrack(TrackRate aSampleRate) 72 : ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO, 73 new AudioSegment()) {} 74 75 void DeviceInputConsumerTrack::ConnectDeviceInput( 76 CubebUtils::AudioDeviceID aId, AudioDataListener* aListener, 77 const PrincipalHandle& aPrincipal) { 78 MOZ_ASSERT(NS_IsMainThread()); 79 MOZ_ASSERT(Graph()); 80 MOZ_ASSERT(aListener); 81 MOZ_ASSERT(!mListener); 82 MOZ_ASSERT(!mDeviceInputTrack); 83 MOZ_ASSERT(mDeviceId.isNothing()); 84 MOZ_ASSERT(!mDeviceInputTrack, 85 "Must disconnect a device input before connecting a new one"); 86 87 mListener = aListener; 88 mDeviceId.emplace(aId); 89 90 mDeviceInputTrack = 91 DeviceInputTrack::OpenAudio(Graph(), aId, aPrincipal, this); 92 LOG("Open device %p (DeviceInputTrack %p) for consumer %p", aId, 93 mDeviceInputTrack.get(), this); 94 mPort = AllocateInputPort(mDeviceInputTrack.get()); 95 } 96 97 void DeviceInputConsumerTrack::DisconnectDeviceInput() { 98 MOZ_ASSERT(NS_IsMainThread()); 99 MOZ_ASSERT(Graph()); 100 101 if (!mListener) { 102 MOZ_ASSERT(mDeviceId.isNothing()); 103 MOZ_ASSERT(!mDeviceInputTrack); 104 return; 105 } 106 107 MOZ_ASSERT(mPort); 108 MOZ_ASSERT(mDeviceInputTrack); 109 MOZ_ASSERT(mDeviceId.isSome()); 110 111 LOG("Close device %p (DeviceInputTrack %p) for consumer %p ", *mDeviceId, 112 mDeviceInputTrack.get(), this); 113 mPort->Destroy(); 114 DeviceInputTrack::CloseAudio(mDeviceInputTrack.forget(), this); 115 mListener = nullptr; 116 mDeviceId = Nothing(); 117 } 118 119 Maybe<CubebUtils::AudioDeviceID> DeviceInputConsumerTrack::DeviceId() const { 120 MOZ_ASSERT(NS_IsMainThread()); 121 return mDeviceId; 122 } 123 124 NotNull<AudioDataListener*> DeviceInputConsumerTrack::GetAudioDataListener() 125 const { 126 MOZ_ASSERT(NS_IsMainThread()); 127 return WrapNotNull(mListener.get()); 128 } 129 130 bool DeviceInputConsumerTrack::ConnectedToNativeDevice() const { 131 MOZ_ASSERT(NS_IsMainThread()); 132 return mDeviceInputTrack && mDeviceInputTrack->AsNativeInputTrack(); 133 } 134 135 bool DeviceInputConsumerTrack::ConnectedToNonNativeDevice() const { 136 MOZ_ASSERT(NS_IsMainThread()); 137 return mDeviceInputTrack && mDeviceInputTrack->AsNonNativeInputTrack(); 138 } 139 140 DeviceInputTrack* DeviceInputConsumerTrack::GetDeviceInputTrackGraphThread() 141 const { 142 AssertOnGraphThread(); 143 144 if (mInputs.IsEmpty()) { 145 return nullptr; 146 } 147 MOZ_ASSERT(mInputs.Length() == 1); 148 MediaTrack* track = mInputs[0]->GetSource(); 149 MOZ_ASSERT(track->AsDeviceInputTrack()); 150 return static_cast<DeviceInputTrack*>(track); 151 } 152 153 void DeviceInputConsumerTrack::GetInputSourceData(AudioSegment& aOutput, 154 GraphTime aFrom, 155 GraphTime aTo) const { 156 AssertOnGraphThread(); 157 MOZ_ASSERT(aOutput.IsEmpty()); 158 MOZ_ASSERT(mInputs.Length() == 1); 159 160 MediaInputPort* port = mInputs[0]; 161 MediaTrack* source = port->GetSource(); 162 GraphTime next; 163 for (GraphTime t = aFrom; t < aTo; t = next) { 164 MediaInputPort::InputInterval interval = 165 MediaInputPort::GetNextInputInterval(port, t); 166 interval.mEnd = std::min(interval.mEnd, aTo); 167 168 const bool inputEnded = 169 source->Ended() && 170 source->GetEnd() <= 171 source->GraphTimeToTrackTimeWithBlocking(interval.mStart); 172 173 TrackTime ticks = interval.mEnd - interval.mStart; 174 next = interval.mEnd; 175 176 if (interval.mStart >= interval.mEnd) { 177 break; 178 } 179 180 if (inputEnded) { 181 aOutput.AppendNullData(ticks); 182 CONSUMER_GRAPH_LOGV( 183 "Getting %" PRId64 184 " ticks of null data from input port source (ended input)", 185 ticks); 186 } else if (interval.mInputIsBlocked) { 187 aOutput.AppendNullData(ticks); 188 CONSUMER_GRAPH_LOGV( 189 "Getting %" PRId64 190 " ticks of null data from input port source (blocked input)", 191 ticks); 192 } else if (source->IsSuspended()) { 193 aOutput.AppendNullData(ticks); 194 CONSUMER_GRAPH_LOGV( 195 "Getting %" PRId64 196 " ticks of null data from input port source (source is suspended)", 197 ticks); 198 } else { 199 TrackTime start = 200 source->GraphTimeToTrackTimeWithBlocking(interval.mStart); 201 TrackTime end = source->GraphTimeToTrackTimeWithBlocking(interval.mEnd); 202 MOZ_ASSERT(source->GetData<AudioSegment>()->GetDuration() >= end); 203 aOutput.AppendSlice(*source->GetData<AudioSegment>(), start, end); 204 CONSUMER_GRAPH_LOGV("Getting %" PRId64 205 " ticks of real data from input port source %p", 206 end - start, source); 207 } 208 } 209 } 210 211 /* static */ 212 NotNull<RefPtr<DeviceInputTrack>> DeviceInputTrack::OpenAudio( 213 MediaTrackGraph* aGraph, CubebUtils::AudioDeviceID aDeviceId, 214 const PrincipalHandle& aPrincipalHandle, 215 DeviceInputConsumerTrack* aConsumer) { 216 MOZ_ASSERT(NS_IsMainThread()); 217 MOZ_ASSERT(aConsumer); 218 MOZ_ASSERT(aGraph == aConsumer->Graph()); 219 220 RefPtr<DeviceInputTrack> track = 221 aGraph->GetDeviceInputTrackMainThread(aDeviceId); 222 if (track) { 223 MOZ_ASSERT(!track->mConsumerTracks.IsEmpty()); 224 track->AddDataListener(aConsumer->GetAudioDataListener()); 225 } else { 226 // Create a NativeInputTrack or NonNativeInputTrack, depending on whether 227 // the given graph already has a native device or not. 228 if (aGraph->GetNativeInputTrackMainThread()) { 229 // A native device is already in use. This device will be a non-native 230 // device. 231 track = new NonNativeInputTrack(aGraph->GraphRate(), aDeviceId, 232 aPrincipalHandle); 233 } else { 234 // No native device is in use. This device will be the native device. 235 track = new NativeInputTrack(aGraph->GraphRate(), aDeviceId, 236 aPrincipalHandle); 237 } 238 LOG("Create %sNativeInputTrack %p in MTG %p for device %p", 239 (track->AsNativeInputTrack() ? "" : "Non"), track.get(), aGraph, 240 aDeviceId); 241 aGraph->AddTrack(track); 242 // Add the listener before opening the device so the device passed to 243 // OpenAudioInput always has a non-zero input channel count. 244 track->AddDataListener(aConsumer->GetAudioDataListener()); 245 aGraph->OpenAudioInput(track); 246 } 247 MOZ_ASSERT(track->AsNativeInputTrack() || track->AsNonNativeInputTrack()); 248 MOZ_ASSERT(track->mDeviceId == aDeviceId); 249 250 MOZ_ASSERT(!track->mConsumerTracks.Contains(aConsumer)); 251 track->mConsumerTracks.AppendElement(aConsumer); 252 253 LOG("DeviceInputTrack %p (device %p: %snative) in MTG %p has %zu users now", 254 track.get(), track->mDeviceId, 255 (track->AsNativeInputTrack() ? "" : "non-"), aGraph, 256 track->mConsumerTracks.Length()); 257 if (track->mConsumerTracks.Length() > 1) { 258 track->ReevaluateInputDevice(); 259 } 260 261 return WrapNotNull(track); 262 } 263 264 /* static */ 265 void DeviceInputTrack::CloseAudio(already_AddRefed<DeviceInputTrack> aTrack, 266 DeviceInputConsumerTrack* aConsumer) { 267 MOZ_ASSERT(NS_IsMainThread()); 268 269 RefPtr<DeviceInputTrack> track = aTrack; 270 MOZ_ASSERT(track); 271 272 track->RemoveDataListener(aConsumer->GetAudioDataListener()); 273 DebugOnly<bool> removed = track->mConsumerTracks.RemoveElement(aConsumer); 274 MOZ_ASSERT(removed); 275 LOG("DeviceInputTrack %p (device %p) in MTG %p has %zu users now", 276 track.get(), track->mDeviceId, track->Graph(), 277 track->mConsumerTracks.Length()); 278 if (track->mConsumerTracks.IsEmpty()) { 279 track->Graph()->CloseAudioInput(track); 280 track->Destroy(); 281 } else { 282 track->ReevaluateInputDevice(); 283 } 284 } 285 286 const nsTArray<RefPtr<DeviceInputConsumerTrack>>& 287 DeviceInputTrack::GetConsumerTracks() const { 288 MOZ_ASSERT(NS_IsMainThread()); 289 return mConsumerTracks; 290 } 291 292 DeviceInputTrack::DeviceInputTrack(TrackRate aSampleRate, 293 CubebUtils::AudioDeviceID aDeviceId, 294 const PrincipalHandle& aPrincipalHandle) 295 : ProcessedMediaTrack(aSampleRate, MediaSegment::AUDIO, new AudioSegment()), 296 mDeviceId(aDeviceId), 297 mPrincipalHandle(aPrincipalHandle) {} 298 299 uint32_t DeviceInputTrack::MaxRequestedInputChannels() const { 300 AssertOnGraphThreadOrNotRunning(); 301 uint32_t maxInputChannels = 0; 302 for (const auto& listener : mListeners) { 303 maxInputChannels = std::max(maxInputChannels, 304 listener->RequestedInputChannelCount(mGraph)); 305 } 306 return maxInputChannels; 307 } 308 309 bool DeviceInputTrack::HasVoiceInput() const { 310 AssertOnGraphThreadOrNotRunning(); 311 for (const auto& listener : mListeners) { 312 if (listener->IsVoiceInput(mGraph)) { 313 return true; 314 } 315 } 316 return false; 317 } 318 319 AudioInputProcessingParamsRequest 320 DeviceInputTrack::UpdateRequestedProcessingParams() { 321 AssertOnGraphThreadOrNotRunning(); 322 Maybe<cubeb_input_processing_params> params; 323 for (const auto& listener : mListeners) { 324 if (params) { 325 *params &= listener->RequestedInputProcessingParams(mGraph); 326 } else { 327 params = Some(listener->RequestedInputProcessingParams(mGraph)); 328 } 329 } 330 331 if (auto p = params.valueOr(CUBEB_INPUT_PROCESSING_PARAM_NONE); 332 p != mProcessingParamsRequest.mParams) { 333 mProcessingParamsRequest.mParams = p; 334 mProcessingParamsRequest.mGeneration = 335 Graph()->ProcessingParamsGeneration(); 336 337 TRACK_GRAPH_LOG( 338 "%sNativeInputTrack notifying of setting requested processing params " 339 "%s (Gen %d)", 340 (AsNonNativeInputTrack() ? "Non" : ""), 341 CubebUtils::ProcessingParamsToString(mProcessingParamsRequest.mParams) 342 .get(), 343 mProcessingParamsRequest.mGeneration); 344 345 NotifySetRequestedProcessingParams(Graph(), 346 mProcessingParamsRequest.mGeneration, 347 mProcessingParamsRequest.mParams); 348 } 349 350 return mProcessingParamsRequest; 351 } 352 353 void DeviceInputTrack::DeviceChanged(MediaTrackGraph* aGraph) const { 354 AssertOnGraphThreadOrNotRunning(); 355 MOZ_ASSERT(aGraph == mGraph, 356 "Receive device changed signal from another graph"); 357 TRACK_GRAPH_LOG("DeviceChanged"); 358 for (const auto& listener : mListeners) { 359 listener->DeviceChanged(aGraph); 360 } 361 } 362 363 void DeviceInputTrack::NotifySetRequestedProcessingParams( 364 MediaTrackGraph* aGraph, int aGeneration, 365 cubeb_input_processing_params aRequestedParams) { 366 AssertOnGraphThread(); 367 for (const auto& listener : mListeners) { 368 listener->NotifySetRequestedInputProcessingParams(mGraph, aGeneration, 369 aRequestedParams); 370 } 371 } 372 373 void DeviceInputTrack::NotifySetRequestedProcessingParamsResult( 374 MediaTrackGraph* aGraph, int aGeneration, 375 const Result<cubeb_input_processing_params, int>& aResult) { 376 AssertOnGraphThread(); 377 for (const auto& listener : mListeners) { 378 listener->NotifySetRequestedInputProcessingParamsResult(mGraph, aGeneration, 379 aResult); 380 } 381 } 382 383 void DeviceInputTrack::ReevaluateInputDevice() { 384 MOZ_ASSERT(NS_IsMainThread()); 385 QueueControlMessageWithNoShutdown([self = RefPtr{this}, this] { 386 TRACE("DeviceInputTrack::ReevaluateInputDevice ControlMessage"); 387 Graph()->ReevaluateInputDevice(mDeviceId); 388 }); 389 } 390 391 void DeviceInputTrack::AddDataListener(AudioDataListener* aListener) { 392 MOZ_ASSERT(NS_IsMainThread()); 393 QueueControlMessageWithNoShutdown( 394 [self = RefPtr{this}, this, listener = RefPtr{aListener}] { 395 TRACE("DeviceInputTrack::AddDataListener ControlMessage"); 396 MOZ_ASSERT(!mListeners.Contains(listener.get()), 397 "Don't add a listener twice."); 398 mListeners.AppendElement(listener.get()); 399 }); 400 } 401 402 void DeviceInputTrack::RemoveDataListener(AudioDataListener* aListener) { 403 MOZ_ASSERT(NS_IsMainThread()); 404 QueueControlMessageWithNoShutdown( 405 [self = RefPtr{this}, this, listener = RefPtr{aListener}] { 406 TRACE("DeviceInputTrack::RemoveDataListener ControlMessage"); 407 DebugOnly<bool> wasPresent = mListeners.RemoveElement(listener.get()); 408 MOZ_ASSERT(wasPresent, "Remove an unknown listener"); 409 listener->Disconnect(Graph()); 410 }); 411 } 412 413 NativeInputTrack::NativeInputTrack(TrackRate aSampleRate, 414 CubebUtils::AudioDeviceID aDeviceId, 415 const PrincipalHandle& aPrincipalHandle) 416 : DeviceInputTrack(aSampleRate, aDeviceId, aPrincipalHandle), 417 mIsBufferingAppended(false), 418 mInputChannels(0) {} 419 420 void NativeInputTrack::DestroyImpl() { 421 AssertOnGraphThreadOrNotRunning(); 422 mPendingData.Clear(); 423 ProcessedMediaTrack::DestroyImpl(); 424 } 425 426 void NativeInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo, 427 uint32_t aFlags) { 428 AssertOnGraphThread(); 429 TRACE_COMMENT("NativeInputTrack::ProcessInput", "%p", this); 430 431 TRACK_GRAPH_LOGV("(Native) ProcessInput from %" PRId64 " to %" PRId64 432 ", needs %" PRId64 " frames", 433 aFrom, aTo, aTo - aFrom); 434 435 TrackTime from = GraphTimeToTrackTime(aFrom); 436 TrackTime to = GraphTimeToTrackTime(aTo); 437 MOZ_ASSERT(from < to); 438 439 MOZ_ASSERT_IF(!mIsBufferingAppended, mPendingData.IsEmpty()); 440 441 TrackTime need = to - from; 442 TrackTime dataNeed = std::min(mPendingData.GetDuration(), need); 443 TrackTime silenceNeed = std::max(need - dataNeed, (TrackTime)0); 444 445 // TODO (bug 1879353): Reenable assertion. 446 // MOZ_ASSERT_IF(dataNeed > 0, silenceNeed == 0); 447 448 GetData<AudioSegment>()->AppendSlice(mPendingData, 0, dataNeed); 449 mPendingData.RemoveLeading(dataNeed); 450 GetData<AudioSegment>()->AppendNullData(silenceNeed); 451 452 // TODO (bug 1879353): Remove as assertion above will hold. 453 if (dataNeed > 0 && silenceNeed > 0) { 454 NotifyInputStopped(mGraph); 455 } 456 } 457 458 uint32_t NativeInputTrack::NumberOfChannels() const { 459 AssertOnGraphThreadOrNotRunning(); 460 return mInputChannels; 461 } 462 463 void NativeInputTrack::NotifyInputStopped(MediaTrackGraph* aGraph) { 464 AssertOnGraphThreadOrNotRunning(); 465 MOZ_ASSERT(aGraph == mGraph, 466 "Receive input stopped signal from another graph"); 467 TRACK_GRAPH_LOG("(Native) NotifyInputStopped"); 468 mInputChannels = 0; 469 mIsBufferingAppended = false; 470 mPendingData.Clear(); 471 } 472 473 void NativeInputTrack::NotifyInputData(MediaTrackGraph* aGraph, 474 const AudioDataValue* aBuffer, 475 size_t aFrames, TrackRate aRate, 476 uint32_t aChannels, 477 uint32_t aAlreadyBuffered) { 478 AssertOnGraphThread(); 479 MOZ_ASSERT(aGraph == mGraph, "Receive input data from another graph"); 480 TRACK_GRAPH_LOGV( 481 "NotifyInputData: frames=%zu, rate=%d, channel=%u, alreadyBuffered=%u", 482 aFrames, aRate, aChannels, aAlreadyBuffered); 483 484 if (!mIsBufferingAppended) { 485 // First time we see live frames getting added. Use what's already buffered 486 // in the driver's scratch buffer as a starting point. 487 MOZ_ASSERT(mPendingData.IsEmpty()); 488 constexpr TrackTime buffering = WEBAUDIO_BLOCK_SIZE; 489 const TrackTime remaining = 490 buffering - static_cast<TrackTime>(aAlreadyBuffered); 491 mPendingData.AppendNullData(remaining); 492 mIsBufferingAppended = true; 493 TRACK_GRAPH_LOG("Set mIsBufferingAppended by appending %" PRId64 " frames.", 494 remaining); 495 } 496 497 MOZ_ASSERT(aChannels); 498 if (!mInputChannels) { 499 mInputChannels = aChannels; 500 } 501 mPendingData.AppendFromInterleavedBuffer(aBuffer, aFrames, aChannels, 502 mPrincipalHandle); 503 } 504 505 NonNativeInputTrack::NonNativeInputTrack( 506 TrackRate aSampleRate, CubebUtils::AudioDeviceID aDeviceId, 507 const PrincipalHandle& aPrincipalHandle) 508 : DeviceInputTrack(aSampleRate, aDeviceId, aPrincipalHandle), 509 mAudioSource(nullptr), 510 mSourceIdNumber(0) {} 511 512 void NonNativeInputTrack::DestroyImpl() { 513 AssertOnGraphThreadOrNotRunning(); 514 if (mAudioSource) { 515 mAudioSource->Stop(); 516 mAudioSource = nullptr; 517 } 518 ProcessedMediaTrack::DestroyImpl(); 519 } 520 521 void NonNativeInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo, 522 uint32_t aFlags) { 523 AssertOnGraphThread(); 524 TRACE_COMMENT("NonNativeInputTrack::ProcessInput", "%p", this); 525 526 TRACK_GRAPH_LOGV("(NonNative) ProcessInput from %" PRId64 " to %" PRId64 527 ", needs %" PRId64 " frames", 528 aFrom, aTo, aTo - aFrom); 529 530 TrackTime from = GraphTimeToTrackTime(aFrom); 531 TrackTime to = GraphTimeToTrackTime(aTo); 532 MOZ_ASSERT(from < to); 533 534 TrackTime delta = to - from; 535 if (!mAudioSource) { 536 GetData<AudioSegment>()->AppendNullData(delta); 537 return; 538 } 539 540 AudioInputSource::Consumer consumer = AudioInputSource::Consumer::Same; 541 // GraphRunner keeps the same thread. 542 MOZ_ASSERT(!HasGraphThreadChanged()); 543 544 ReevaluateProcessingParams(); 545 546 AudioSegment data = mAudioSource->GetAudioSegment(delta, consumer); 547 MOZ_ASSERT(data.GetDuration() == delta); 548 GetData<AudioSegment>()->AppendFrom(&data); 549 } 550 551 uint32_t NonNativeInputTrack::NumberOfChannels() const { 552 AssertOnGraphThreadOrNotRunning(); 553 return mAudioSource ? mAudioSource->mChannelCount : 0; 554 } 555 556 void NonNativeInputTrack::StartAudio( 557 RefPtr<AudioInputSource>&& aAudioInputSource) { 558 AssertOnGraphThread(); 559 MOZ_ASSERT(aAudioInputSource->mPrincipalHandle == mPrincipalHandle); 560 MOZ_ASSERT(aAudioInputSource->mDeviceId == mDeviceId); 561 562 TRACK_GRAPH_LOG("StartAudio with source %p", aAudioInputSource.get()); 563 #ifdef DEBUG 564 mGraphThreadId = std::this_thread::get_id(); 565 #endif 566 mAudioSource = std::move(aAudioInputSource); 567 mAudioSource->Init(); 568 ReevaluateProcessingParams(); 569 mAudioSource->Start(); 570 } 571 572 void NonNativeInputTrack::StopAudio() { 573 AssertOnGraphThread(); 574 575 TRACK_GRAPH_LOG("StopAudio from source %p", mAudioSource.get()); 576 if (!mAudioSource) { 577 return; 578 } 579 mAudioSource->Stop(); 580 mAudioSource = nullptr; 581 #ifdef DEBUG 582 mGraphThreadId = std::thread::id(); 583 #endif 584 } 585 586 AudioInputType NonNativeInputTrack::DevicePreference() const { 587 AssertOnGraphThreadOrNotRunning(); 588 return mAudioSource && mAudioSource->mIsVoice ? AudioInputType::Voice 589 : AudioInputType::Unknown; 590 } 591 592 void NonNativeInputTrack::NotifyDeviceChanged(uint32_t aSourceId) { 593 AssertOnGraphThreadOrNotRunning(); 594 595 // No need to forward the notification if the audio input has been stopped or 596 // restarted by it users. 597 if (!mAudioSource || mAudioSource->mId != aSourceId) { 598 TRACK_GRAPH_LOG("(NonNative) NotifyDeviceChanged: No need to forward"); 599 return; 600 } 601 602 TRACK_GRAPH_LOG("(NonNative) NotifyDeviceChanged"); 603 // Forward the notification. 604 DeviceInputTrack::DeviceChanged(mGraph); 605 } 606 607 void NonNativeInputTrack::NotifyInputStopped(uint32_t aSourceId) { 608 AssertOnGraphThreadOrNotRunning(); 609 610 // No need to forward the notification if the audio input has been stopped or 611 // restarted by it users. 612 if (!mAudioSource || mAudioSource->mId != aSourceId) { 613 TRACK_GRAPH_LOG("(NonNative) NotifyInputStopped: No need to forward"); 614 return; 615 } 616 617 TRACK_GRAPH_LOGE( 618 "(NonNative) NotifyInputStopped: audio unexpectedly stopped"); 619 // Destory the underlying audio stream if it's stopped unexpectedly. 620 mAudioSource->Stop(); 621 } 622 623 AudioInputSource::Id NonNativeInputTrack::GenerateSourceId() { 624 AssertOnGraphThread(); 625 return mSourceIdNumber++; 626 } 627 628 void NonNativeInputTrack::ReevaluateProcessingParams() { 629 AssertOnGraphThread(); 630 MOZ_ASSERT(mAudioSource); 631 auto request = UpdateRequestedProcessingParams(); 632 if (mRequestedProcessingParamsGeneration == request.mGeneration) { 633 return; 634 } 635 auto generation = mRequestedProcessingParamsGeneration = request.mGeneration; 636 auto params = request.mParams; 637 using Promise = AudioInputSource::SetRequestedProcessingParamsPromise; 638 mAudioSource->SetRequestedProcessingParams(params)->Then( 639 GetMainThreadSerialEventTarget(), __func__, 640 [this, self = RefPtr(this), 641 generation](Promise::ResolveOrRejectValue&& aValue) { 642 if (IsDestroyed()) { 643 return; 644 } 645 auto result = ([&]() -> Result<cubeb_input_processing_params, int> { 646 if (aValue.IsResolve()) { 647 return aValue.ResolveValue(); 648 } 649 return Err(aValue.RejectValue()); 650 })(); 651 QueueControlMessageWithNoShutdown([this, self = RefPtr(this), 652 generation, 653 result = std::move(result)] { 654 NotifySetRequestedProcessingParamsResult(Graph(), generation, result); 655 }); 656 }); 657 } 658 659 #ifdef DEBUG 660 bool NonNativeInputTrack::HasGraphThreadChanged() { 661 AssertOnGraphThread(); 662 663 std::thread::id currentId = std::this_thread::get_id(); 664 if (mGraphThreadId == currentId) { 665 return false; 666 } 667 mGraphThreadId = currentId; 668 return true; 669 } 670 #endif // DEBUG 671 672 AudioInputSourceListener::AudioInputSourceListener(NonNativeInputTrack* aOwner) 673 : mOwner(aOwner) {} 674 675 void AudioInputSourceListener::AudioDeviceChanged( 676 AudioInputSource::Id aSourceId) { 677 MOZ_ASSERT(NS_IsMainThread()); 678 MOZ_ASSERT(mOwner); 679 680 if (mOwner->IsDestroyed()) { 681 LOG("NonNativeInputTrack %p has been destroyed. No need to forward the " 682 "audio device-changed notification", 683 mOwner.get()); 684 return; 685 } 686 687 MOZ_DIAGNOSTIC_ASSERT(mOwner->Graph()); 688 mOwner->QueueControlMessageWithNoShutdown([inputTrack = mOwner, aSourceId] { 689 TRACE("NonNativeInputTrack::AudioDeviceChanged ControlMessage"); 690 inputTrack->NotifyDeviceChanged(aSourceId); 691 }); 692 } 693 694 void AudioInputSourceListener::AudioStateCallback( 695 AudioInputSource::Id aSourceId, 696 AudioInputSource::EventListener::State aState) { 697 MOZ_ASSERT(NS_IsMainThread()); 698 MOZ_ASSERT(mOwner); 699 700 const char* state = 701 aState == AudioInputSource::EventListener::State::Started ? "started" 702 : aState == AudioInputSource::EventListener::State::Stopped ? "stopped" 703 : aState == AudioInputSource::EventListener::State::Drained ? "drained" 704 : "error"; 705 706 if (mOwner->IsDestroyed()) { 707 LOG("NonNativeInputTrack %p has been destroyed. No need to forward the " 708 "audio state-changed(%s) notification", 709 mOwner.get(), state); 710 return; 711 } 712 713 if (aState == AudioInputSource::EventListener::State::Started) { 714 LOG("We can ignore %s notification for NonNativeInputTrack %p", state, 715 mOwner.get()); 716 return; 717 } 718 719 LOG("Notify audio stopped due to entering %s state", state); 720 721 MOZ_DIAGNOSTIC_ASSERT(mOwner->Graph()); 722 mOwner->QueueControlMessageWithNoShutdown([inputTrack = mOwner, aSourceId] { 723 TRACE("NonNativeInputTrack::AudioStateCallback ControlMessage"); 724 inputTrack->NotifyInputStopped(aSourceId); 725 }); 726 } 727 728 #undef LOG_INTERNAL 729 #undef LOG 730 #undef LOGE 731 #undef TRACK_GRAPH_LOG_INTERNAL 732 #undef TRACK_GRAPH_LOG 733 #undef TRACK_GRAPH_LOGV 734 #undef TRACK_GRAPH_LOGE 735 #undef CONSUMER_GRAPH_LOG_INTERNAL 736 #undef CONSUMER_GRAPH_LOGV 737 738 } // namespace mozilla