void AudioSink::AudioLoop() { AssertOnAudioThread(); SINK_LOG("AudioLoop started"); if (NS_FAILED(InitializeAudioStream())) { NS_WARNING("Initializing AudioStream failed."); mStateMachine->DispatchOnAudioSinkError(); return; } while (1) { { ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); WaitForAudioToPlay(); if (!IsPlaybackContinuing()) { break; } } // See if there's a gap in the audio. If there is, push silence into the // audio hardware, so we can play across the gap. // Calculate the timestamp of the next chunk of audio in numbers of // samples. NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play"); CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate); // Calculate the number of frames that have been pushed onto the audio hardware. CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) + mWritten; CheckedInt64 missingFrames = sampleTime - playedFrames; if (!missingFrames.isValid() || !sampleTime.isValid()) { NS_WARNING("Int overflow adding in AudioLoop"); break; } if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { // The next audio chunk begins some time after the end of the last chunk // we pushed to the audio hardware. We must push silence into the audio // hardware so that the next audio chunk begins playback at the correct // time. missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value()); mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value())); } else { mWritten += PlayFromAudioQueue(); } int64_t endTime = GetEndTime(); if (endTime != -1) { mOnAudioEndTimeUpdateTask->Dispatch(endTime); } } ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream()); if (!mStopAudioThread && mPlaying) { Drain(); } SINK_LOG("AudioLoop complete"); Cleanup(); SINK_LOG("AudioLoop exit"); }
void DecodedAudioDataSink::AudioLoop() { AssertOnAudioThread(); mAudioLoopScheduled = false; switch (mState) { case AUDIOSINK_STATE_INIT: { SINK_LOG("AudioLoop started"); nsresult rv = InitializeAudioStream(); if (NS_FAILED(rv)) { NS_WARNING("Initializing AudioStream failed."); mEndPromise.Reject(rv, __func__); SetState(AUDIOSINK_STATE_ERROR); break; } SetState(AUDIOSINK_STATE_PLAYING); ConnectListener(); break; } case AUDIOSINK_STATE_PLAYING: { if (WaitingForAudioToPlay()) { // OnAudioQueueEvent() will schedule next loop. break; } if (!IsPlaybackContinuing()) { SetState(AUDIOSINK_STATE_COMPLETE); break; } if (!PlayAudio()) { SetState(AUDIOSINK_STATE_COMPLETE); break; } // Schedule next loop to play next sample. ScheduleNextLoop(); break; } case AUDIOSINK_STATE_COMPLETE: { DisconnectListener(); FinishAudioLoop(); SetState(AUDIOSINK_STATE_SHUTDOWN); break; } case AUDIOSINK_STATE_SHUTDOWN: break; case AUDIOSINK_STATE_ERROR: break; } // end of switch // We want mState to stay stable during AudioLoop to keep things simple. // Therefore, we only do state transition at the end of AudioLoop. if (mPendingState.isSome()) { MOZ_ASSERT(mState != mPendingState.ref()); SINK_LOG("change mState, %d -> %d", mState, mPendingState.ref()); mState = mPendingState.ref(); mPendingState.reset(); // Schedule next loop when state changes. ScheduleNextLoop(); } }