Bug 1139206 - Update AudioOffloadPlayer seek r=bholley,bwu
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1205,49 +1205,50 @@ void MediaDecoder::UpdateReadyStateForDa
if (!mOwner || mShuttingDown || !mDecoderStateMachine) {
return;
}
MediaDecoderOwner::NextFrameStatus frameStatus =
mDecoderStateMachine->GetNextFrameStatus();
mOwner->UpdateReadyStateForData(frameStatus);
}
-void MediaDecoder::OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility)
+void MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
{
MOZ_ASSERT(NS_IsMainThread());
+ mSeekRequest.Complete();
if (mShuttingDown)
return;
bool fireEnded = false;
bool seekWasAborted = false;
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
// An additional seek was requested while the current seek was
// in operation.
if (mRequestedSeekTarget.IsValid()) {
ChangeState(PLAY_STATE_SEEKING);
seekWasAborted = true;
} else {
UnpinForSeek();
- fireEnded = aAtEnd;
- if (aAtEnd) {
+ fireEnded = aVal.mAtEnd;
+ if (aVal.mAtEnd) {
ChangeState(PLAY_STATE_ENDED);
- } else if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
- ChangeState(aAtEnd ? PLAY_STATE_ENDED : mNextState);
+ } else if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
+ ChangeState(aVal.mAtEnd ? PLAY_STATE_ENDED : mNextState);
}
}
}
- PlaybackPositionChanged(aEventVisibility);
+ PlaybackPositionChanged(aVal.mEventVisibility);
if (mOwner) {
UpdateReadyStateForData();
- if (!seekWasAborted && (aEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
+ if (!seekWasAborted && (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed)) {
mOwner->SeekCompleted();
if (fireEnded) {
mOwner->PlaybackEnded();
}
}
}
}
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -800,31 +800,17 @@ public:
// Acquires the monitor. Call from any thread.
virtual bool IsExpectingMoreData();
// Called when the video has completed playing.
// Call on the main thread only.
void PlaybackEnded();
void OnSeekRejected() { mSeekRequest.Complete(); }
- void OnSeekResolvedInternal(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility);
-
- void OnSeekResolved(SeekResolveValue aVal)
- {
- mSeekRequest.Complete();
- OnSeekResolvedInternal(aVal.mAtEnd, aVal.mEventVisibility);
- }
-
-#ifdef MOZ_AUDIO_OFFLOAD
- // Temporary hack - see bug 1139206.
- void SimulateSeekResolvedForAudioOffload(MediaDecoderEventVisibility aEventVisibility)
- {
- OnSeekResolvedInternal(false, aEventVisibility);
- }
-#endif
+ void OnSeekResolved(SeekResolveValue aVal);
// Seeking has started. Inform the element on the main
// thread.
void SeekingStarted(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable);
// Called when the backend has changed the current playback
// position. It dispatches a timeupdate event and invalidates the frame.
// This must be called on the main thread only.
--- a/dom/media/omx/AudioOffloadPlayer.cpp
+++ b/dom/media/omx/AudioOffloadPlayer.cpp
@@ -52,23 +52,20 @@ PRLogModuleInfo* gAudioOffloadPlayerLog;
// maximum time in paused state when offloading audio decompression.
// When elapsed, the AudioSink is destroyed to allow the audio DSP to power down.
static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
mStarted(false),
mPlaying(false),
- mSeeking(false),
mReachedEOS(false),
- mSeekDuringPause(false),
mIsElementVisible(true),
mSampleRate(0),
mStartPosUs(0),
- mSeekTimeUs(0),
mPositionTimeMediaUs(-1),
mInputBuffer(nullptr),
mObserver(aObserver)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef PR_LOGGING
if (!gAudioOffloadPlayerLog) {
@@ -194,23 +191,16 @@ status_t AudioOffloadPlayer::ChangeState
case MediaDecoder::PLAY_STATE_PLAYING: {
status_t err = Play();
if (err != OK) {
return err;
}
StartTimeUpdate();
} break;
- case MediaDecoder::PLAY_STATE_SEEKING: {
- int64_t seekTimeUs
- = mObserver->GetSeekTime();
- SeekTo(seekTimeUs, true);
- mObserver->ResetSeekTime();
- } break;
-
case MediaDecoder::PLAY_STATE_PAUSED:
case MediaDecoder::PLAY_STATE_SHUTDOWN:
// Just pause here during play state shutdown as well to stop playing
// offload track immediately. Resources will be freed by
// MediaOmxCommonDecoder
Pause();
break;
@@ -273,18 +263,22 @@ status_t AudioOffloadPlayer::Play()
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);
+ 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;
@@ -338,62 +332,64 @@ void AudioOffloadPlayer::Reset()
mReachedEOS = false;
mStarted = false;
mPlaying = false;
mStartPosUs = 0;
WakeLockRelease();
}
-status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
+nsRefPtr<MediaDecoder::SeekPromise> AudioOffloadPlayer::Seek(SeekTarget aTarget)
{
MOZ_ASSERT(NS_IsMainThread());
- CHECK(mAudioSink.get());
-
android::Mutex::Autolock autoLock(mLock);
- AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SeekTo ( %lld )", aTimeUs));
+ mSeekPromise.RejectIfExists(true, __func__);
+ mSeekTarget = aTarget;
+ nsRefPtr<MediaDecoder::SeekPromise> p = mSeekPromise.Ensure(__func__);
+ DoSeek();
+ return p;
+}
- mSeeking = true;
+status_t AudioOffloadPlayer::DoSeek()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mSeekTarget.IsValid());
+ CHECK(mAudioSink.get());
+
+ AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("DoSeek ( %lld )", mSeekTarget.mTime));
+
mReachedEOS = false;
mPositionTimeMediaUs = -1;
- mSeekTimeUs = aTimeUs;
- mStartPosUs = aTimeUs;
- mDispatchSeekEvents = aDispatchSeekEvents;
+ mStartPosUs = mSeekTarget.mTime;
- if (mDispatchSeekEvents) {
+ if (!mSeekPromise.IsEmpty()) {
nsCOMPtr<nsIRunnable> nsEvent =
NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
mObserver,
&MediaDecoder::SeekingStarted,
- MediaDecoderEventVisibility::Observable);
+ mSeekTarget.mEventVisibility);
NS_DispatchToCurrentThread(nsEvent);
}
if (mPlaying) {
mAudioSink->Pause();
mAudioSink->Flush();
mAudioSink->Start();
} else {
- mSeekDuringPause = true;
-
if (mStarted) {
mAudioSink->Flush();
}
- if (mDispatchSeekEvents) {
- mDispatchSeekEvents = false;
+ if (!mSeekPromise.IsEmpty()) {
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Fake seek complete during pause"));
- nsCOMPtr<nsIRunnable> nsEvent =
- NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
- mObserver,
- &MediaDecoder::SimulateSeekResolvedForAudioOffload,
- MediaDecoderEventVisibility::Observable);
- NS_DispatchToCurrentThread(nsEvent);
+ // We do not reset mSeekTarget here.
+ MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+ mSeekPromise.Resolve(val, __func__);
}
}
return OK;
}
double AudioOffloadPlayer::GetMediaTimeSecs()
{
@@ -402,18 +398,18 @@ double AudioOffloadPlayer::GetMediaTimeS
static_cast<double>(USECS_PER_S));
}
int64_t AudioOffloadPlayer::GetMediaTimeUs()
{
android::Mutex::Autolock autoLock(mLock);
int64_t playPosition = 0;
- if (mSeeking) {
- return mSeekTimeUs;
+ if (mSeekTarget.IsValid()) {
+ return mSeekTarget.mTime;
}
if (!mStarted) {
return mPositionTimeMediaUs;
}
playPosition = GetOutputPlayPositionUs_l();
if (!mReachedEOS) {
mPositionTimeMediaUs = playPosition;
@@ -434,16 +430,22 @@ int64_t AudioOffloadPlayer::GetOutputPla
// HAL position is relative to the first buffer we sent at mStartPosUs
const int64_t renderedDuration = mStartPosUs + playedUs;
return renderedDuration;
}
void AudioOffloadPlayer::NotifyAudioEOS()
{
+ android::Mutex::Autolock autoLock(mLock);
+ // We do not reset mSeekTarget here.
+ if (!mSeekPromise.IsEmpty()) {
+ MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+ mSeekPromise.Resolve(val, __func__);
+ }
nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
&MediaDecoder::PlaybackEnded);
NS_DispatchToMainThread(nsEvent);
}
void AudioOffloadPlayer::NotifyPositionChanged()
{
nsCOMPtr<nsIRunnable> nsEvent =
@@ -451,16 +453,25 @@ void AudioOffloadPlayer::NotifyPositionC
mObserver,
&MediaOmxCommonDecoder::PlaybackPositionChanged,
MediaDecoderEventVisibility::Observable);
NS_DispatchToMainThread(nsEvent);
}
void AudioOffloadPlayer::NotifyAudioTearDown()
{
+ // Fallback to state machine.
+ // state machine's seeks will be done with
+ // MediaDecoderEventVisibility::Suppressed.
+ android::Mutex::Autolock autoLock(mLock);
+ // We do not reset mSeekTarget here.
+ if (!mSeekPromise.IsEmpty()) {
+ MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+ mSeekPromise.Resolve(val, __func__);
+ }
nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
&MediaOmxCommonDecoder::AudioOffloadTearDown);
NS_DispatchToMainThread(nsEvent);
}
// static
size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink,
void* aBuffer,
@@ -501,45 +512,45 @@ size_t AudioOffloadPlayer::FillBuffer(vo
CHECK(mAudioSink.get());
if (mReachedEOS) {
return 0;
}
size_t sizeDone = 0;
size_t sizeRemaining = aSize;
+ int64_t seekTimeUs = -1;
while (sizeRemaining > 0) {
MediaSource::ReadOptions options;
- bool refreshSeekTime = false;
-
{
android::Mutex::Autolock autoLock(mLock);
- if (mSeeking) {
- options.setSeekTo(mSeekTimeUs);
- refreshSeekTime = true;
+ if (mSeekTarget.IsValid()) {
+ seekTimeUs = mSeekTarget.mTime;
+ options.setSeekTo(seekTimeUs);
if (mInputBuffer) {
mInputBuffer->release();
mInputBuffer = nullptr;
}
- mSeeking = false;
}
}
if (!mInputBuffer) {
-
status_t err;
err = mSource->read(&mInputBuffer, &options);
CHECK((!err && mInputBuffer) || (err && !mInputBuffer));
android::Mutex::Autolock autoLock(mLock);
if (err != OK) {
+ if (mSeekTarget.IsValid()) {
+ mSeekTarget.Reset();
+ }
AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Error while reading media source %d "
"Ok to receive EOS error at end", err));
if (!mReachedEOS) {
// After seek there is a possible race condition if
// OffloadThread is observing state_stopping_1 before
// framesReady() > 0. Ensure sink stop is called
// after last buffer is released. This ensures the
// partial buffer is written to the driver before
@@ -559,51 +570,34 @@ size_t AudioOffloadPlayer::FillBuffer(vo
break;
}
if(mInputBuffer->range_length() != 0) {
CHECK(mInputBuffer->meta_data()->findInt64(
kKeyTime, &mPositionTimeMediaUs));
}
- if (refreshSeekTime) {
- if (mDispatchSeekEvents && !mSeekDuringPause) {
- mDispatchSeekEvents = false;
+ if (mSeekTarget.IsValid() && seekTimeUs == mSeekTarget.mTime) {
+ mSeekTarget.Reset();
+ if (!mSeekPromise.IsEmpty()) {
AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("FillBuffer posting SEEK_COMPLETE"));
- nsCOMPtr<nsIRunnable> nsEvent =
- NS_NewRunnableMethodWithArg<MediaDecoderEventVisibility>(
- mObserver,
- &MediaDecoder::SimulateSeekResolvedForAudioOffload,
- MediaDecoderEventVisibility::Observable);
- NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
-
- } else if (mSeekDuringPause) {
- // Callback is already called for seek during pause. Just reset the
- // flag
- AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Not posting seek complete as its"
- " already faked"));
- mSeekDuringPause = false;
+ MediaDecoder::SeekResolveValue val(mReachedEOS, mSeekTarget.mEventVisibility);
+ mSeekPromise.Resolve(val, __func__);
}
-
- NotifyPositionChanged();
+ } else if (mSeekTarget.IsValid()) {
+ AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("seek is updated during unlocking mLock"));
+ }
- // need to adjust the mStartPosUs for offload decoding since parser
- // might not be able to get the exact seek time requested.
- mStartPosUs = mPositionTimeMediaUs;
- AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f",
- mStartPosUs / 1E6));
+ NotifyPositionChanged();
- // clear seek time with mLock locked and once we have valid
- // mPositionTimeMediaUs
- // before clearing mSeekTimeUs check if a new seek request has been
- // received while we were reading from the source with mLock released.
- if (!mSeeking) {
- mSeekTimeUs = 0;
- }
- }
+ // need to adjust the mStartPosUs for offload decoding since parser
+ // might not be able to get the exact seek time requested.
+ mStartPosUs = mPositionTimeMediaUs;
+ AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f",
+ mStartPosUs / 1E6));
}
if (mInputBuffer->range_length() == 0) {
mInputBuffer->release();
mInputBuffer = nullptr;
continue;
}
--- a/dom/media/omx/AudioOffloadPlayer.h
+++ b/dom/media/omx/AudioOffloadPlayer.h
@@ -73,72 +73,58 @@ public:
SEEK_COMPLETE
};
AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr);
~AudioOffloadPlayer();
// Caller retains ownership of "aSource".
- void SetSource(const android::sp<MediaSource> &aSource);
+ virtual void SetSource(const android::sp<MediaSource> &aSource) override;
// Start the source if it's not already started and open the AudioSink to
// create an offloaded audio track
- status_t Start(bool aSourceAlreadyStarted = false);
+ virtual status_t Start(bool aSourceAlreadyStarted = false) override;
+
+ virtual status_t ChangeState(MediaDecoder::PlayState aState) override;
- double GetMediaTimeSecs();
+ virtual void SetVolume(double aVolume) override;
+
+ virtual double GetMediaTimeSecs() override;
// To update progress bar when the element is visible
- void SetElementVisibility(bool aIsVisible);
-
- status_t ChangeState(MediaDecoder::PlayState aState);
-
- void SetVolume(double aVolume);
+ virtual void SetElementVisibility(bool aIsVisible) override;;
// Update ready state based on current play state. Not checking data
// availability since offloading is currently done only when whole compressed
// data is available
- MediaDecoderOwner::NextFrameStatus GetNextFrameStatus();
+ virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus() override;
+
+ virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) override;
void TimeUpdate();
// Close the audio sink, stop time updates, frees the input buffers
void Reset();
private:
// Set when audio source is started and audioSink is initialized
// Used only in main thread
bool mStarted;
// Set when audio sink is started. i.e. playback started
// Used only in main thread
bool mPlaying;
- // Set when playstate is seeking and reset when FillBUffer() acknowledged
- // seeking by seeking audio source. Used in main thread and offload
- // callback thread, protected by Mutex mLock
- bool mSeeking;
-
// Once playback reached end of stream (last ~100ms), position provided by DSP
// may be reset/corrupted. This bool is used to avoid that.
// Used in main thread and offload callback thread, protected by Mutex
// mLock
bool mReachedEOS;
- // Set when there is a seek request during pause.
- // Used in main thread and offload callback thread, protected by Mutex
- // mLock
- bool mSeekDuringPause;
-
- // Seek can be triggered internally or by MediaDecoder. This bool is to
- // to track seek triggered by MediaDecoder so that we can send back
- // SeekingStarted and SeekingStopped events.
- // Used in main thread and offload callback thread, protected by Mutex mLock
- bool mDispatchSeekEvents;
-
// Set when the HTML Audio Element is visible to the user.
// Used only in main thread
bool mIsElementVisible;
// Session id given by Android::AudioSystem and used while creating audio sink
// Used only in main thread
int mSessionId;
@@ -150,20 +136,25 @@ private:
// different than given mSeekTimeUs, if audio source cannot find a frame at
// that position. Store seeked position in mStartPosUs and provide
// mStartPosUs + GetPosition() (i.e. absolute position) to
// MediaOmxCommonDecoder
// Used in main thread and offload callback thread, protected by Mutex
// mLock
int64_t mStartPosUs;
- // Given seek time when there is a request to seek
+ // The target of current seek when there is a request to seek
// Used in main thread and offload callback thread, protected by Mutex
// mLock
- int64_t mSeekTimeUs;
+ SeekTarget mSeekTarget;
+
+ // MediaPromise of current seek.
+ // Used in main thread and offload callback thread, protected by Mutex
+ // mLock
+ MediaPromiseHolder<MediaDecoder::SeekPromise> mSeekPromise;
// Positions obtained from offlaoded tracks (DSP)
// Used in main thread and offload callback thread, protected by Mutex
// mLock
int64_t mPositionTimeMediaUs;
// State obtained from MediaOmxCommonDecoder. Used only in main thread
MediaDecoder::PlayState mPlayState;
@@ -216,25 +207,25 @@ private:
static size_t AudioSinkCallback(AudioSink *aAudioSink,
void *aData,
size_t aSize,
void *aMe,
AudioSink::cb_event_t aEvent);
bool IsSeeking();
- // Set mSeekTime to the given position and restart the sink. Actual seek
- // happens in FillBuffer(). If aDispatchSeekEvents is true, send
+ // Set mSeekTarget to the given position and restart the sink. Actual seek
+ // happens in FillBuffer(). If mSeekPromise is not empty, send
// SeekingStarted event always and SeekingStopped event when the play state is
// paused to MediaDecoder.
// When decoding and playing happens separately, if there is a seek during
// pause, we can decode and keep data ready.
// In case of offload player, no way to seek during pause. So just fake that
// seek is done.
- status_t SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents = false);
+ status_t DoSeek();
// Start/Resume the audio sink so that callback will start being called to get
// compressed data
status_t Play();
// Stop the audio sink if we need to play till we drain the current buffer.
// or Pause the sink in case we should stop playing immediately
void Pause(bool aPlayPendingSamples = false);
--- a/dom/media/omx/AudioOffloadPlayerBase.h
+++ b/dom/media/omx/AudioOffloadPlayerBase.h
@@ -61,13 +61,15 @@ public:
// Update ready state based on current play state. Not checking data
// availability since offloading is currently done only when whole compressed
// data is available
virtual MediaDecoderOwner::NextFrameStatus GetNextFrameStatus()
{
return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
}
+
+ virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) = 0;
};
} // namespace mozilla
#endif // AUDIO_OFFLOAD_PLAYER_BASE_H_
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -121,18 +121,19 @@ MediaOmxCommonDecoder::ResumeStateMachin
if (!mDecoderStateMachine) {
return;
}
mFallbackToStateMachine = true;
mAudioOffloadPlayer = nullptr;
int64_t timeUsecs = 0;
SecondsToUsecs(mCurrentTime, timeUsecs);
- mRequestedSeekTarget = SeekTarget(timeUsecs, SeekTarget::Accurate);
-
+ mRequestedSeekTarget = SeekTarget(timeUsecs,
+ SeekTarget::Accurate,
+ MediaDecoderEventVisibility::Suppressed);
mNextState = mPlayState;
ChangeState(PLAY_STATE_LOADING);
// exit dormant state
RefPtr<nsRunnable> event =
NS_NewRunnableMethodWithArg<bool>(
mDecoderStateMachine,
&MediaDecoderStateMachine::SetDormant,
false);
@@ -188,20 +189,35 @@ void
MediaOmxCommonDecoder::ChangeState(PlayState aState)
{
MOZ_ASSERT(NS_IsMainThread());
// Keep MediaDecoder state in sync with MediaElement irrespective of offload
// playback so it will continue to work in normal mode when offloading fails
// in between
MediaDecoder::ChangeState(aState);
- if (mAudioOffloadPlayer) {
- status_t err = mAudioOffloadPlayer->ChangeState(aState);
- if (err != OK) {
- ResumeStateMachine();
+ if (!mAudioOffloadPlayer) {
+ return;
+ }
+
+ status_t err = mAudioOffloadPlayer->ChangeState(aState);
+ if (err != OK) {
+ ResumeStateMachine();
+ return;
+ }
+
+ switch (mPlayState) {
+ case PLAY_STATE_SEEKING:
+ mSeekRequest.Begin(mAudioOffloadPlayer->Seek(mRequestedSeekTarget)
+ ->RefableThen(NS_GetCurrentThread(), __func__, static_cast<MediaDecoder*>(this),
+ &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
+ mRequestedSeekTarget.Reset();
+ break;
+ default: {
+ break;
}
}
}
void
MediaOmxCommonDecoder::ApplyStateToStateMachine(PlayState aState)
{
MOZ_ASSERT(NS_IsMainThread());