status_t AudioOffloadPlayer::Play()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (mResetTimer) {
    mResetTimer->Cancel();
    mResetTimer = nullptr;
    WakeLockRelease();
  }

  status_t err = OK;

  if (!mStarted) {
    // Last pause timed out and offloaded audio sink was reset. Start it again
    err = Start(false);
    if (err != OK) {
      return err;
    }
    // Seek to last play position only when there was no seek during last pause
    if (!mSeeking) {
      SeekTo(mPositionTimeMediaUs);
    }
  }

  if (!mPlaying) {
    CHECK(mAudioSink.get());
    err = mAudioSink->Start();
    if (err == OK) {
      mPlaying = true;
    }
  }

  return err;
}
void AudioOffloadPlayer::Reset()
{
  MOZ_ASSERT(NS_IsMainThread());
  if (!mStarted) {
    return;
  }

  CHECK(mAudioSink.get());

  AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("reset: mPlaying=%d mReachedEOS=%d",
      mPlaying, mReachedEOS));

  mAudioSink->Stop();
  // If we're closing and have reached EOS, we don't want to flush
  // the track because if it is offloaded there could be a small
  // amount of residual data in the hardware buffer which we must
  // play to give gapless playback.
  // But if we're resetting when paused or before we've reached EOS
  // we can't be doing a gapless playback and there could be a large
  // amount of data queued in the hardware if the track is offloaded,
  // so we must flush to prevent a track switch being delayed playing
  // the buffered data that we don't want now
  if (!mPlaying || !mReachedEOS) {
    mAudioSink->Flush();
  }

  mAudioSink->Close();
  // Make sure to release any buffer we hold onto so that the
  // source is able to stop().

  if (mInputBuffer) {
    AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Releasing input buffer"));

    mInputBuffer->release();
    mInputBuffer = nullptr;
  }
  mSource->stop();

  IPCThreadState::self()->flushCommands();
  StopTimeUpdate();

  mReachedEOS = false;
  mStarted = false;
  mPlaying = false;
  mStartPosUs = 0;

  WakeLockRelease();
}
status_t AudioOffloadPlayer::Play()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (mResetTimer) {
    mResetTimer->Cancel();
    mResetTimer = nullptr;
    WakeLockRelease();
  }

  status_t err = OK;

  if (!mStarted) {
    // Last pause timed out and offloaded audio sink was reset. Start it again
    err = Start(false);
    if (err != OK) {
      return err;
    }
    // Seek to last play position only when there was no seek during last pause
    android::Mutex::Autolock autoLock(mLock);
    if (!mSeekTarget.IsValid()) {
      mSeekTarget = SeekTarget(mPositionTimeMediaUs,
                               SeekTarget::Accurate,
                               MediaDecoderEventVisibility::Suppressed);
      DoSeek();
    }
  }

  if (!mPlaying) {
    CHECK(mAudioSink.get());
    err = mAudioSink->Start();
    if (err == OK) {
      mPlaying = true;
    }
  }

  return err;
}