Example #1
0
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();
  }
}