ssize_t mailstream_low_write(mailstream_low * s, const void * buf, size_t count) { ssize_t r; if (s == NULL) return -1; #ifdef STREAM_DEBUG STREAM_LOG(s, 1, ">>>>>>> send >>>>>>\n"); if (s->privacy) { STREAM_LOG_BUF(s, 1, buf, count); } else { STREAM_LOG_BUF(s, 2, buf, count); } STREAM_LOG(s, 1, "\n"); STREAM_LOG(s, 1, ">>>>>>> end send >>>>>>\n"); #endif r = s->driver->mailstream_write(s, buf, count); if (r < 0) { STREAM_LOG_ERROR(s, 4 | 1, buf, 0); } return r; }
void AudioCallbackDriver::Start() { // If this is running on the main thread, we can't open the stream directly, // because it is a blocking operation. if (NS_IsMainThread()) { STREAM_LOG(LogLevel::Debug, ("Starting audio threads for MediaStreamGraph %p from a new thread.", mGraphImpl)); RefPtr<AsyncCubebTask> initEvent = new AsyncCubebTask(this, AsyncCubebOperation::INIT); initEvent->Dispatch(); } else { STREAM_LOG(LogLevel::Debug, ("Starting audio threads for MediaStreamGraph %p from the previous driver's thread", mGraphImpl)); Init(); // Check if we need to resolve promises because the driver just got switched // because of a resuming AudioContext if (!mPromisesForOperation.IsEmpty()) { CompleteAudioContextOperations(AsyncCubebOperation::INIT); } if (mPreviousDriver) { nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutdownThreadRunnable(mPreviousDriver); mPreviousDriver = nullptr; NS_DispatchToMainThread(event); } } }
void SystemClockDriver::WaitForNextIteration() { mGraphImpl->GetMonitor().AssertCurrentThreadOwns(); PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; TimeStamp now = TimeStamp::Now(); if (mNeedAnotherIteration) { int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS - int64_t((now - mCurrentTimeStamp).ToMilliseconds()); // Make sure timeoutMS doesn't overflow 32 bits by waking up at // least once a minute, if we need to wake up at all timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000)); timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS)); STREAM_LOG(PR_LOG_DEBUG+1, ("Waiting for next iteration; at %f, timeout=%f", (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0)); mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION; } else { mWaitState = WAITSTATE_WAITING_INDEFINITELY; } if (timeout > 0) { mGraphImpl->GetMonitor().Wait(timeout); STREAM_LOG(PR_LOG_DEBUG+1, ("Resuming after timeout; at %f, elapsed=%f", (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(), (TimeStamp::Now() - now).ToSeconds())); } mWaitState = WAITSTATE_RUNNING; mNeedAnotherIteration = false; }
void ThreadedDriver::RunThread() { AutoProfilerUnregisterThread autoUnregister; bool stillProcessing = true; while (stillProcessing) { GraphTime prevCurrentTime, nextCurrentTime; GetIntervalForIteration(prevCurrentTime, nextCurrentTime); mStateComputedTime = mNextStateComputedTime; mNextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock( nextCurrentTime + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS)); STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)mStateComputedTime, (long)mNextStateComputedTime)); stillProcessing = mGraphImpl->OneIteration(prevCurrentTime, nextCurrentTime, StateComputedTime(), mNextStateComputedTime); if (mNextDriver && stillProcessing) { STREAM_LOG(PR_LOG_DEBUG, ("Switching to AudioCallbackDriver")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd, mStateComputedTime, mNextStateComputedTime); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); return; } } }
void SystemClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo) { TimeStamp now = TimeStamp::Now(); aFrom = mIterationStart = IterationEnd(); aTo = mIterationEnd = mGraphImpl->SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + IterationEnd(); mCurrentTimeStamp = now; PR_LOG(gMediaStreamGraphLog, PR_LOG_DEBUG+1, ("Updating current time to %f (real %f, mStateComputedTime %f)", mGraphImpl->MediaTimeToSeconds(aTo), (now - mInitialTimeStamp).ToSeconds(), mGraphImpl->MediaTimeToSeconds(StateComputedTime()))); if (mStateComputedTime < aTo) { STREAM_LOG(PR_LOG_WARNING, ("Media graph global underrun detected")); aTo = mIterationEnd = mStateComputedTime; } if (aFrom >= aTo) { NS_ASSERTION(aFrom == aTo , "Time can't go backwards!"); // This could happen due to low clock resolution, maybe? STREAM_LOG(PR_LOG_DEBUG, ("Time did not advance")); } }
void TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) { bool enabled = aMode == DisabledTrackMode::ENABLED; for (TrackMapEntry& entry : mTrackMap) { if (entry.mOutputTrackID == aTrackID) { STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s", this, aTrackID, enabled ? "enabled" : "disabled")); for (DirectMediaStreamTrackListener* listener : entry.mOwnedDirectListeners) { DisabledTrackMode oldMode = GetDisabledTrackMode(aTrackID); bool oldEnabled = oldMode == DisabledTrackMode::ENABLED; if (!oldEnabled && enabled) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting " "direct listener enabled", this, aTrackID)); listener->DecreaseDisabled(oldMode); } else if (oldEnabled && !enabled) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting " "direct listener disabled", this, aTrackID)); listener->IncreaseDisabled(aMode); } } } } MediaStream::SetTrackEnabledImpl(aTrackID, aMode); }
void TrackUnionStream::RemoveInput(MediaInputPort* aPort) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing input %p", this, aPort)); for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { if (mTrackMap[i].mInputPort == aPort) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing trackmap entry %d", this, i)); EndTrack(i); mTrackMap.RemoveElementAt(i); } } ProcessedMediaStream::RemoveInput(aPort); }
void ThreadedDriver::RunThread() { AutoProfilerUnregisterThread autoUnregister; bool stillProcessing = true; while (stillProcessing) { mIterationStart = IterationEnd(); mIterationEnd += GetIntervalForIteration(); GraphTime stateComputedTime = StateComputedTime(); if (stateComputedTime < mIterationEnd) { STREAM_LOG(LogLevel::Warning, ("Media graph global underrun detected")); mIterationEnd = stateComputedTime; } if (mIterationStart >= mIterationEnd) { NS_ASSERTION(mIterationStart == mIterationEnd , "Time can't go backwards!"); // This could happen due to low clock resolution, maybe? STREAM_LOG(LogLevel::Debug, ("Time did not advance")); } GraphTime nextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock( mIterationEnd + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS)); if (nextStateComputedTime < stateComputedTime) { // A previous driver may have been processing further ahead of // iterationEnd. STREAM_LOG(LogLevel::Warning, ("Prevent state from going backwards. interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)stateComputedTime, (long)nextStateComputedTime)); nextStateComputedTime = stateComputedTime; } STREAM_LOG(LogLevel::Debug, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)stateComputedTime, (long)nextStateComputedTime)); stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime); if (mNextDriver && stillProcessing) { STREAM_LOG(LogLevel::Debug, ("Switching to AudioCallbackDriver")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); return; } } }
void StreamBuffer::DumpTrackInfo() const { STREAM_LOG(PR_LOG_ALWAYS, ("DumpTracks: mTracksKnownTime %lld", mTracksKnownTime)); for (uint32_t i = 0; i < mTracks.Length(); ++i) { Track* track = mTracks[i]; if (track->IsEnded()) { STREAM_LOG(PR_LOG_ALWAYS, ("Track[%d] %d: ended", i, track->GetID())); } else { STREAM_LOG(PR_LOG_ALWAYS, ("Track[%d] %d: %lld", i, track->GetID(), track->GetEndTimeRoundDown())); } } }
void TrackUnionStream::RemoveInput(MediaInputPort* aPort) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing input %p", this, aPort)); for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { if (mTrackMap[i].mInputPort == aPort) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing trackmap entry %d", this, i)); EndTrack(i); for (auto listener : mTrackMap[i].mOwnedDirectListeners) { // Remove listeners while the entry still exists. RemoveDirectTrackListenerImpl(listener, mTrackMap[i].mOutputTrackID); } mTrackMap.RemoveElementAt(i); } } ProcessedMediaStream::RemoveInput(aPort); }
NS_IMETHOD Run() { char aLocal; STREAM_LOG(LogLevel::Debug, ("Starting system thread")); profiler_register_thread("MediaStreamGraph", &aLocal); LIFECYCLE_LOG("Starting a new system driver for graph %p\n", mDriver->mGraphImpl); if (mDriver->mPreviousDriver) { LIFECYCLE_LOG("%p releasing an AudioCallbackDriver(%p), for graph %p\n", mDriver, mDriver->mPreviousDriver.get(), mDriver->GraphImpl()); MOZ_ASSERT(!mDriver->AsAudioCallbackDriver()); // Stop and release the previous driver off-main-thread, but only if we're // not in the situation where we've fallen back to a system clock driver // because the osx audio stack is currently switching output device. if (!mDriver->mPreviousDriver->AsAudioCallbackDriver()->IsSwitchingDevice()) { RefPtr<AsyncCubebTask> releaseEvent = new AsyncCubebTask(mDriver->mPreviousDriver->AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN); mDriver->mPreviousDriver = nullptr; releaseEvent->Dispatch(); } } else { MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor()); MOZ_ASSERT(mDriver->mGraphImpl->MessagesQueued(), "Don't start a graph without messages queued."); mDriver->mGraphImpl->SwapMessageQueues(); } mDriver->RunThread(); return NS_OK; }
bool AudioCallbackDriver::OSXDeviceSwitchingWorkaround() { MonitorAutoLock mon(GraphImpl()->GetMonitor()); if (mSelfReference) { // Apparently, depending on the osx version, on device switch, the // callback is called "some" number of times, and then stops being called, // and then gets called again. 10 is to be safe, it's a low-enough number // of milliseconds anyways (< 100ms) //STREAM_LOG(LogLevel::Debug, ("Callbacks during switch: %d", mCallbackReceivedWhileSwitching+1)); if (mCallbackReceivedWhileSwitching++ >= 10) { STREAM_LOG(LogLevel::Debug, ("Got %d callbacks, switching back to CallbackDriver", mCallbackReceivedWhileSwitching)); // If we have a self reference, we have fallen back temporarily on a // system clock driver, but we just got called back, that means the osx // audio backend has switched to the new device. // Ask the graph to switch back to the previous AudioCallbackDriver // (`this`), and when the graph has effectively switched, we can drop // the self reference and unref the SystemClockDriver we fallen back on. if (GraphImpl()->CurrentDriver() == this) { mSelfReference.Drop(this); mNextDriver = nullptr; } else { GraphImpl()->CurrentDriver()->SwitchAtNextIteration(this); } } return true; } return false; }
NS_IMETHOD Run() { char aLocal; STREAM_LOG(PR_LOG_DEBUG, ("Starting system thread")); profiler_register_thread("MediaStreamGraph", &aLocal); LIFECYCLE_LOG("Starting a new system driver for graph %p\n", mDriver->mGraphImpl); if (mDriver->mPreviousDriver) { LIFECYCLE_LOG("%p releasing an AudioCallbackDriver(%p), for graph %p\n", mDriver, mDriver->mPreviousDriver.get(), mDriver->GraphImpl()); MOZ_ASSERT(!mDriver->AsAudioCallbackDriver()); // Stop and release the previous driver off-main-thread. nsRefPtr<AsyncCubebTask> releaseEvent = new AsyncCubebTask(mDriver->mPreviousDriver->AsAudioCallbackDriver(), AsyncCubebTask::SHUTDOWN); mDriver->mPreviousDriver = nullptr; releaseEvent->Dispatch(); } else { MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor()); MOZ_ASSERT(mDriver->mGraphImpl->MessagesQueued(), "Don't start a graph without messages queued."); mDriver->mGraphImpl->SwapMessageQueues(); } mDriver->RunThread(); return NS_OK; }
void AudioCallbackDriver::DeviceChangedCallback() { MonitorAutoLock mon(mGraphImpl->GetMonitor()); PanOutputIfNeeded(mMicrophoneActive); // On OSX, changing the output device causes the audio thread to no call the // audio callback, so we're unable to process real-time input data, and this // results in latency building up. // We switch to a system driver until audio callbacks are called again, so we // still pull from the input stream, so that everything works apart from the // audio output. #ifdef XP_MACOSX // Don't bother doing the device switching dance if the graph is not RUNNING // (starting up, shutting down), because we haven't started pulling from the // SourceMediaStream. if (!GraphImpl()->Running()) { return; } if (mSelfReference) { return; } STREAM_LOG(LogLevel::Error, ("Switching to SystemClockDriver during output switch")); mSelfReference.Take(this); mCallbackReceivedWhileSwitching = 0; mNextDriver = new SystemClockDriver(GraphImpl()); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); #endif }
void TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener, TrackID aTrackID) { RefPtr<DirectMediaStreamTrackListener> listener = aListener; for (TrackMapEntry& entry : mTrackMap) { if (entry.mOutputTrackID == aTrackID) { MediaStream* source = entry.mInputPort->GetSource(); STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener " "%p for track %d. Forwarding to input " "stream %p track %d.", this, listener.get(), aTrackID, source, entry.mInputTrackID)); entry.mOwnedDirectListeners.AppendElement(listener); DisabledTrackMode currentMode = GetDisabledTrackMode(aTrackID); if (currentMode != DisabledTrackMode::ENABLED) { listener->IncreaseDisabled(currentMode); } source->AddDirectTrackListenerImpl(listener.forget(), entry.mInputTrackID); return; } } TrackBound<DirectMediaStreamTrackListener>* bound = mPendingDirectTrackListeners.AppendElement(); bound->mListener = listener.forget(); bound->mTrackID = aTrackID; }
void OfflineClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo) { aFrom = mIterationStart = IterationEnd(); aTo = mIterationEnd = IterationEnd() + mGraphImpl->MillisecondsToMediaTime(mSlice); if (mStateComputedTime < aTo) { STREAM_LOG(PR_LOG_WARNING, ("Media graph global underrun detected")); aTo = mIterationEnd = mStateComputedTime; } if (aFrom >= aTo) { NS_ASSERTION(aFrom == aTo , "Time can't go backwards!"); // This could happen due to low clock resolution, maybe? STREAM_LOG(PR_LOG_DEBUG, ("Time did not advance")); } }
void TrackUnionStream::EndTrack(uint32_t aIndex) { StreamTracks::Track* outputTrack = mTracks.FindTrack(mTrackMap[aIndex].mOutputTrackID); if (!outputTrack || outputTrack->IsEnded()) return; STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p ending track %d", this, outputTrack->GetID())); outputTrack->SetEnded(); }
void AudioCallbackDriver::Resume() { STREAM_LOG(LogLevel::Debug, ("Resuming audio threads for MediaStreamGraph %p", mGraphImpl)); if (cubeb_stream_start(mAudioStream) != CUBEB_OK) { NS_WARNING("Could not start cubeb stream for MSG."); } }
AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl, dom::AudioChannel aChannel) : GraphDriver(aGraphImpl) , mStarted(false) , mAudioChannel(aChannel) , mInCallback(false) , mPauseRequested(false) { STREAM_LOG(PR_LOG_DEBUG, ("AudioCallbackDriver ctor for graph %p", aGraphImpl)); }
void TrackUnionStream::CopyTrackData(StreamBuffer::Track* aInputTrack, uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo, bool* aOutputTrackFinished) { TrackMapEntry* map = &mTrackMap[aMapIndex]; StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID); MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track"); MediaSegment* segment = map->mSegment; MediaStream* source = map->mInputPort->GetSource(); GraphTime next; *aOutputTrackFinished = false; for (GraphTime t = aFrom; t < aTo; t = next) { MediaInputPort::InputInterval interval = map->mInputPort->GetNextInputInterval(t); interval.mEnd = std::min(interval.mEnd, aTo); StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd); StreamTime inputTrackEndPoint = STREAM_TIME_MAX; if (aInputTrack->IsEnded() && aInputTrack->GetEnd() <= inputEnd) { inputTrackEndPoint = aInputTrack->GetEnd(); *aOutputTrackFinished = true; } if (interval.mStart >= interval.mEnd) { break; } StreamTime ticks = interval.mEnd - interval.mStart; next = interval.mEnd; StreamTime outputStart = outputTrack->GetEnd(); if (interval.mInputIsBlocked) { // Maybe the input track ended? segment->AppendNullData(ticks); STREAM_LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of null data to track %d", this, (long long)ticks, outputTrack->GetID())); } else if (InMutedCycle()) { segment->AppendNullData(ticks); } else { MOZ_ASSERT(outputTrack->GetEnd() == GraphTimeToStreamTime(interval.mStart), "Samples missing"); StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart); segment->AppendSlice(*aInputTrack->GetSegment(), std::min(inputTrackEndPoint, inputStart), std::min(inputTrackEndPoint, inputEnd)); } ApplyTrackDisabling(outputTrack->GetID(), segment); for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), outputStart, 0, *segment); } outputTrack->GetSegment()->AppendFrom(segment); } }
ssize_t mailstream_low_read(mailstream_low * s, void * buf, size_t count) { ssize_t r; if (s == NULL) return -1; r = s->driver->mailstream_read(s, buf, count); #ifdef STREAM_DEBUG if (r > 0) { STREAM_LOG(s, 0, "<<<<<<< read <<<<<<\n"); STREAM_LOG_BUF(s, 0, buf, r); STREAM_LOG(s, 0, "\n"); STREAM_LOG(s, 0, "<<<<<<< end read <<<<<<\n"); } #endif return r; }
void AudioCallbackDriver::Revive() { // Note: only called on MainThread, without monitor // We know were weren't in a running state STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver reviving.")); // If we were switching, switch now. Otherwise, start the audio thread again. MonitorAutoLock mon(mGraphImpl->GetMonitor()); if (mNextDriver) { mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); } else { STREAM_LOG(LogLevel::Debug, ("Starting audio threads for MediaStreamGraph %p from a new thread.", mGraphImpl)); RefPtr<AsyncCubebTask> initEvent = new AsyncCubebTask(this, AsyncCubebOperation::INIT); initEvent->Dispatch(); } }
NS_IMETHODIMP AsyncCubebTask::Run() { MOZ_ASSERT(mThread); if (NS_IsMainThread()) { mThread->Shutdown(); // can't shutdown from the thread itself, darn // don't null out mThread! // See bug 999104. we must hold a ref to the thread across Dispatch() // since the internal mthread ref could be released while processing // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it // assumes the caller does. return NS_OK; } MOZ_ASSERT(mDriver); switch(mOperation) { case AsyncCubebOperation::INIT: LIFECYCLE_LOG("AsyncCubebOperation::INIT\n"); mDriver->Init(); break; case AsyncCubebOperation::SHUTDOWN: LIFECYCLE_LOG("AsyncCubebOperation::SHUTDOWN\n"); mDriver->Stop(); mDriver = nullptr; mShutdownGrip = nullptr; break; case AsyncCubebOperation::SLEEP: { { LIFECYCLE_LOG("AsyncCubebOperation::SLEEP\n"); MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor()); // We might just have been awoken if (mDriver->mNeedAnotherIteration) { mDriver->mPauseRequested = false; mDriver->mWaitState = AudioCallbackDriver::WAITSTATE_RUNNING; break; } mDriver->Stop(); mDriver->mWaitState = AudioCallbackDriver::WAITSTATE_WAITING_INDEFINITELY; mDriver->mPauseRequested = false; mDriver->mGraphImpl->GetMonitor().Wait(PR_INTERVAL_NO_TIMEOUT); } STREAM_LOG(PR_LOG_DEBUG, ("Restarting audio stream from sleep.")); mDriver->StartStream(); break; } default: MOZ_CRASH("Operation not implemented."); } // and now kill this thread NS_DispatchToMainThread(this); return NS_OK; }
void ThreadedDriver::Stop() { NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread"); // mGraph's thread is not running so it's OK to do whatever here STREAM_LOG(PR_LOG_DEBUG, ("Stopping threads for MediaStreamGraph %p", this)); if (mThread) { mThread->Shutdown(); } }
void AudioCallbackDriver::Init() { cubeb_stream_params params; uint32_t latency; MOZ_ASSERT(!NS_IsMainThread(), "This is blocking and should never run on the main thread."); mSampleRate = params.rate = CubebUtils::PreferredSampleRate(); #if defined(__ANDROID__) #if defined(MOZ_B2G) params.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel); #else params.stream_type = CUBEB_STREAM_TYPE_MUSIC; #endif if (params.stream_type == CUBEB_STREAM_TYPE_MAX) { NS_WARNING("Bad stream type"); return; } #else (void)mAudioChannel; #endif params.channels = mGraphImpl->AudioChannelCount(); if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) { params.format = CUBEB_SAMPLE_S16NE; } else { params.format = CUBEB_SAMPLE_FLOAT32NE; } if (cubeb_get_min_latency(CubebUtils::GetCubebContext(), params, &latency) != CUBEB_OK) { NS_WARNING("Could not get minimal latency from cubeb."); return; } cubeb_stream* stream; if (cubeb_stream_init(CubebUtils::GetCubebContext(), &stream, "AudioCallbackDriver", params, latency, DataCallback_s, StateCallback_s, this) == CUBEB_OK) { mAudioStream.own(stream); } else { NS_WARNING("Could not create a cubeb stream for MediaStreamGraph."); return; } cubeb_stream_register_device_changed_callback(mAudioStream, AudioCallbackDriver::DeviceChangedCallback_s); StartStream(); STREAM_LOG(PR_LOG_DEBUG, ("AudioCallbackDriver started.")); }
uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack, GraphTime aFrom) { TrackID id = aTrack->GetID(); if (id > mNextAvailableTrackID && mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) { // Input id available. Mark it used in mUsedTracks. mUsedTracks.InsertElementSorted(id); } else { // Input id taken, allocate a new one. id = mNextAvailableTrackID; // Update mNextAvailableTrackID and prune any mUsedTracks members it now // covers. while (1) { if (!mUsedTracks.RemoveElementSorted(++mNextAvailableTrackID)) { // Not in use. We're done. break; } } } // Round up the track start time so the track, if anything, starts a // little later than the true time. This means we'll have enough // samples in our input stream to go just beyond the destination time. StreamTime outputStart = GraphTimeToStreamTimeWithBlocking(aFrom); nsAutoPtr<MediaSegment> segment; segment = aTrack->GetSegment()->CreateEmptyClone(); for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; l->NotifyQueuedTrackChanges(Graph(), id, outputStart, MediaStreamListener::TRACK_EVENT_CREATED, *segment, aPort->GetSource(), aTrack->GetID()); } segment->AppendNullData(outputStart); StreamBuffer::Track* track = &mBuffer.AddTrack(id, outputStart, segment.forget()); STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld", this, id, aPort->GetSource(), aTrack->GetID(), (long long)outputStart)); TrackMapEntry* map = mTrackMap.AppendElement(); map->mEndOfConsumedInputTicks = 0; map->mEndOfLastInputIntervalInInputStream = -1; map->mEndOfLastInputIntervalInOutputStream = -1; map->mInputPort = aPort; map->mInputTrackID = aTrack->GetID(); map->mOutputTrackID = track->GetID(); map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); return mTrackMap.Length() - 1; }
uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack, GraphTime aFrom) { // Use the ID of the source track if it's not already assigned to a track, // otherwise allocate a new unique ID. TrackID id = aTrack->GetID(); TrackID maxTrackID = 0; for (uint32_t i = 0; i < mTrackMap.Length(); ++i) { TrackID outID = mTrackMap[i].mOutputTrackID; maxTrackID = std::max(maxTrackID, outID); } // Note: we might have removed it here, but it might still be in the // StreamBuffer if the TrackUnionStream sees its input stream flip from // A to B, where both A and B have a track with the same ID while (1) { // search until we find one not in use here, and not in mBuffer if (!mBuffer.FindTrack(id)) { break; } id = ++maxTrackID; } // Round up the track start time so the track, if anything, starts a // little later than the true time. This means we'll have enough // samples in our input stream to go just beyond the destination time. StreamTime outputStart = GraphTimeToStreamTime(aFrom); nsAutoPtr<MediaSegment> segment; segment = aTrack->GetSegment()->CreateEmptyClone(); for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; l->NotifyQueuedTrackChanges(Graph(), id, outputStart, MediaStreamListener::TRACK_EVENT_CREATED, *segment); } segment->AppendNullData(outputStart); StreamBuffer::Track* track = &mBuffer.AddTrack(id, outputStart, segment.forget()); STREAM_LOG(PR_LOG_DEBUG, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld", this, id, aPort->GetSource(), aTrack->GetID(), (long long)outputStart)); TrackMapEntry* map = mTrackMap.AppendElement(); map->mEndOfConsumedInputTicks = 0; map->mEndOfLastInputIntervalInInputStream = -1; map->mEndOfLastInputIntervalInOutputStream = -1; map->mInputPort = aPort; map->mInputTrackID = aTrack->GetID(); map->mOutputTrackID = track->GetID(); map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); return mTrackMap.Length() - 1; }
void AudioCallbackDriver::Start() { // If this is running on the main thread, we can't open the stream directly, // because it is a blocking operation. if (NS_IsMainThread()) { STREAM_LOG(PR_LOG_DEBUG, ("Starting audio threads for MediaStreamGraph %p from a new thread.", mGraphImpl)); nsRefPtr<AsyncCubebTask> initEvent = new AsyncCubebTask(this, AsyncCubebTask::INIT); initEvent->Dispatch(); } else { STREAM_LOG(PR_LOG_DEBUG, ("Starting audio threads for MediaStreamGraph %p from the previous driver's thread", mGraphImpl)); Init(); if (mPreviousDriver) { nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutdownThreadRunnable(mPreviousDriver); mPreviousDriver = nullptr; NS_DispatchToMainThread(event); } } }
void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver, GraphTime aLastSwitchNextIterationStart, GraphTime aLastSwitchNextIterationEnd) { // We set mIterationEnd here, because the first thing a driver do when it // does an iteration is to update graph times, so we are in fact setting // mIterationStart of the next iteration by setting the end of the previous // iteration. mIterationStart = aLastSwitchNextIterationStart; mIterationEnd = aLastSwitchNextIterationEnd; STREAM_LOG(LogLevel::Debug, ("Setting previous driver: %p (%s)", aPreviousDriver, aPreviousDriver->AsAudioCallbackDriver() ? "AudioCallbackDriver" : "SystemClockDriver")); MOZ_ASSERT(!mPreviousDriver); mPreviousDriver = aPreviousDriver; }
AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl) : GraphDriver(aGraphImpl) , mSampleRate(0) , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS) , mStarted(false) , mAudioChannel(aGraphImpl->AudioChannel()) , mInCallback(false) , mPauseRequested(false) , mMicrophoneActive(false) #ifdef XP_MACOSX , mCallbackReceivedWhileSwitching(0) #endif { STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl)); }