Bug 1310548 - Delay firing of 'waitingforkey' until all decoded data has been rendered. r=jya
This means 'waitingforkey' will be fired at a predictable time.
MozReview-Commit-ID: HMt1RbgrbuR
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -973,17 +973,17 @@ void HTMLMediaElement::AbortExistingLoad
mSuspendedAfterFirstFrame = false;
mAllowSuspendAfterFirstFrame = true;
mHaveQueuedSelectResource = false;
mSuspendedForPreloadNone = false;
mDownloadSuspendedByCache = false;
mMediaInfo = MediaInfo();
mIsEncrypted = false;
mPendingEncryptedInitData.mInitDatas.Clear();
- mWaitingForKey = false;
+ mWaitingForKey = NOT_WAITING_FOR_KEY;
mSourcePointer = nullptr;
mTags = nullptr;
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
// ChangeNetworkState() will call UpdateAudioChannelPlayingState()
// indirectly which depends on mPaused. So we need to update mPaused first.
@@ -2886,17 +2886,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
mHasPlayedOrSeeked(false),
mHasSelfReference(false),
mShuttingDown(false),
mSuspendedForPreloadNone(false),
mSrcStreamIsPlaying(false),
mMediaSecurityVerified(false),
mCORSMode(CORS_NONE),
mIsEncrypted(false),
- mWaitingForKey(false),
+ mWaitingForKey(NOT_WAITING_FOR_KEY),
mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
mAudioChannelVolume(1.0),
mPlayingThroughTheAudioChannel(false),
mDisableVideo(false),
mHasUserInteraction(false),
mFirstFrameLoaded(false),
mDefaultPlaybackStartPosition(0.0),
mIsAudioTrackAudible(false),
@@ -4349,16 +4349,19 @@ void HTMLMediaElement::MetadataLoaded(co
}
}
}
}
}
void HTMLMediaElement::FirstFrameLoaded()
{
+ LOG(LogLevel::Debug, ("%p, FirstFrameLoaded() mFirstFrameLoaded=%d mWaitingForKey=%d",
+ this, mFirstFrameLoaded, mWaitingForKey));
+
NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
if (!mFirstFrameLoaded) {
mFirstFrameLoaded = true;
UpdateReadyStateInternal();
}
ChangeDelayLoadStatus(false);
@@ -4704,18 +4707,36 @@ HTMLMediaElement::UpdateReadyStateIntern
}
if (hasVideoTracks) {
mediaInfo.EnableVideo();
}
MetadataLoaded(&mediaInfo, nsAutoPtr<const MetadataTags>(nullptr));
}
enum NextFrameStatus nextFrameStatus = NextFrameStatus();
- if (mDecoder && nextFrameStatus == NEXT_FRAME_UNAVAILABLE) {
- nextFrameStatus = mDecoder->NextFrameBufferedStatus();
+ if (nextFrameStatus == NEXT_FRAME_UNAVAILABLE) {
+ if (mWaitingForKey != NOT_WAITING_FOR_KEY) {
+ // http://w3c.github.io/encrypted-media/#wait-for-key
+ // Continuing 7.3.4 Queue a "waitingforkey" Event
+ // 4. Queue a task to fire a simple event named waitingforkey
+ // at the media element.
+ if (mWaitingForKey == WAITING_FOR_KEY) {
+ mWaitingForKey = WAITING_FOR_KEY_DISPATCHED;
+ DispatchAsyncEvent(NS_LITERAL_STRING("waitingforkey"));
+ }
+ // 5. Set the readyState of media element to HAVE_METADATA.
+ // NOTE: We'll change to HAVE_CURRENT_DATA or HAVE_METADATA
+ // depending on whether we've loaded the first frame or not
+ // below.
+ // 6. Suspend playback.
+ // Note: Playback will already be stalled, as the next frame is
+ // unavailable.
+ } else if (mDecoder) {
+ nextFrameStatus = mDecoder->NextFrameBufferedStatus();
+ }
}
if (nextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
LOG(LogLevel::Debug, ("MediaElement %p UpdateReadyStateInternal() "
"NEXT_FRAME_UNAVAILABLE_SEEKING; Forcing HAVE_METADATA", this));
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
return;
}
@@ -4840,17 +4861,17 @@ void HTMLMediaElement::ChangeReadyState(
DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
mLoadedDataFired = true;
}
if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
if (!mPaused) {
- mWaitingForKey = false;
+ mWaitingForKey = NOT_WAITING_FOR_KEY;
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
}
}
CheckAutoplayDataReady();
if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
@@ -6233,31 +6254,28 @@ HTMLMediaElement::GetTopLevelPrincipal()
}
principal = doc->NodePrincipal();
return principal.forget();
}
void
HTMLMediaElement::CannotDecryptWaitingForKey()
{
- // See: http://w3c.github.io/encrypted-media/#dom-evt-waitingforkey
- // Spec: 7.5.4 Queue a "waitingforkey" Event
- // Spec: 1. Let the media element be the specified HTMLMediaElement object.
-
- // Note, existing code will handle the ready state of this element, as
- // such this function does not handle changing or checking mReadyState.
-
- // Spec: 2. If the media element's waiting for key value is true, abort these steps.
- if (!mWaitingForKey) {
- // Spec: 3. Set the media element's waiting for key value to true.
- // Spec: 4. Queue a task to fire a simple event named waitingforkey at the media element.
- DispatchAsyncEvent(NS_LITERAL_STRING("waitingforkey"));
- mWaitingForKey = true;
- // No need to explicitly suspend playback, it happens automatically when
- // it's starving for decoded frames.
+ LOG(LogLevel::Debug, ("%p, CannotDecryptWaitingForKey()", this));
+
+ // http://w3c.github.io/encrypted-media/#wait-for-key
+ // 7.3.4 Queue a "waitingforkey" Event
+ // 1. Let the media element be the specified HTMLMediaElement object.
+ // 2. If the media element's waiting for key value is true, abort these steps.
+ if (mWaitingForKey == NOT_WAITING_FOR_KEY) {
+ // 3. Set the media element's waiting for key value to true.
+ // Note: algorithm continues in UpdateReadyStateInternal() when all decoded
+ // data enqueued in the MDSM is consumed.
+ mWaitingForKey = WAITING_FOR_KEY;
+ UpdateReadyStateInternal();
}
}
NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture)
{
MOZ_ASSERT(mAudioChannelAgent);
if (mAudioCapturedByWindow != aCapture) {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1584,20 +1584,26 @@ protected:
CORSMode mCORSMode;
// Info about the played media.
MediaInfo mMediaInfo;
// True if the media has encryption information.
bool mIsEncrypted;
- // True when the CDM cannot decrypt the current block, and the
- // waitingforkey event has been fired. Back to false when keys have become
- // available and we can advance the current playback position.
- bool mWaitingForKey;
+ enum WaitingForKeyState {
+ NOT_WAITING_FOR_KEY = 0,
+ WAITING_FOR_KEY = 1,
+ WAITING_FOR_KEY_DISPATCHED = 2
+ };
+
+ // True when the CDM cannot decrypt the current block due to lacking a key.
+ // Note: the "waitingforkey" event is not dispatched until all decoded data
+ // has been rendered.
+ WaitingForKeyState mWaitingForKey;
// Listens for waitingForKey events from the owned decoder.
MediaEventListener mWaitingForKeyListener;
// Init Data that needs to be sent in 'encrypted' events in MetadataLoaded().
EncryptionInfo mPendingEncryptedInitData;
// True if the media's channel's download has been suspended.