diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp
index d8e012120a38..9c9e9cba4a67 100644
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -71,6 +71,8 @@
#include "mozilla/dom/MediaSource.h"
#include "MediaMetadataManager.h"
#include "MediaSourceDecoder.h"
+#include "AudioStreamTrack.h"
+#include "VideoStreamTrack.h"
#include "AudioChannelService.h"
@@ -671,7 +673,10 @@ void HTMLMediaElement::AbortExistingLoads()
mHaveQueuedSelectResource = false;
mSuspendedForPreloadNone = false;
mDownloadSuspendedByCache = false;
+ mHasAudio = false;
+ mHasVideo = false;
mSourcePointer = nullptr;
+ mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
mTags = nullptr;
@@ -902,6 +907,30 @@ void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
}
}
+void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
+{
+ if (!mSrcStream || mSrcStream != aStream) {
+ return;
+ }
+
+ bool oldHasVideo = mHasVideo;
+
+ nsAutoTArray,1> audioTracks;
+ mSrcStream->GetAudioTracks(audioTracks);
+ nsAutoTArray,1> videoTracks;
+ mSrcStream->GetVideoTracks(videoTracks);
+
+ mHasAudio = !audioTracks.IsEmpty();
+ mHasVideo = !videoTracks.IsEmpty();
+
+ if (IsVideo() && oldHasVideo != mHasVideo) {
+ // We are a video element and mHasVideo changed so update the screen wakelock
+ NotifyOwnerDocumentActivityChanged();
+ }
+
+ UpdateReadyStateForData(mLastNextFrameStatus);
+}
+
void HTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(mDelayingLoadEvent,
@@ -1987,6 +2016,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNo
mCurrentLoadID(0),
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
+ mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
mLoadWaitStatus(NOT_WAITING),
mVolume(1.0),
mPreloadAction(PRELOAD_UNDEFINED),
@@ -2823,6 +2853,25 @@ private:
bool mPendingNotifyOutput;
};
+class HTMLMediaElement::MediaStreamTracksAvailableCallback:
+ public DOMMediaStream::OnTracksAvailableCallback
+{
+public:
+ explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement,
+ DOMMediaStream::TrackTypeHints aExpectedTracks = 0):
+ DOMMediaStream::OnTracksAvailableCallback(aExpectedTracks),
+ mElement(aElement)
+ {}
+ virtual void NotifyTracksAvailable(DOMMediaStream* aStream)
+ {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+ mElement->NotifyMediaStreamTracksAvailable(aStream);
+ }
+private:
+ HTMLMediaElement* mElement;
+};
+
void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
{
NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already");
@@ -2844,22 +2893,26 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
if (mPausedForInactiveDocumentOrChannel) {
GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
}
+
+ mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this, DOMMediaStream::HINT_CONTENTS_AUDIO));
+ mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this, DOMMediaStream::HINT_CONTENTS_VIDEO));
+
+ ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
+
ChangeDelayLoadStatus(false);
GetSrcMediaStream()->AddAudioOutput(this);
- GetSrcMediaStream()->SetAudioOutputVolume(this, float(mMuted ? 0.0 : mVolume));
+ SetVolumeInternal();
VideoFrameContainer* container = GetVideoFrameContainer();
if (container) {
GetSrcMediaStream()->AddVideoOutput(container);
}
+ CheckAutoplayDataReady();
+
// Note: we must call DisconnectTrackListListeners(...) before dropping
// mSrcStream
mSrcStream->ConstructMediaTracks(AudioTracks(), VideoTracks());
- ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
- DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
- DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
- ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
// FirstFrameLoaded() will be called when the stream has current data.
}
@@ -2939,6 +2992,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
} else {
UpdateMediaSize(aInfo->mVideo.mDisplay);
}
+
+ if (IsVideo() && aInfo->HasVideo()) {
+ // We are a video element playing video so update the screen wakelock
+ NotifyOwnerDocumentActivityChanged();
+ }
}
void HTMLMediaElement::FirstFrameLoaded()
@@ -3203,18 +3261,47 @@ bool HTMLMediaElement::ShouldCheckAllowOrigin()
void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
{
- if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
+ mLastNextFrameStatus = aNextFrame;
+
+ if (mDecoder && mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
// aNextFrame might have a next frame because the decoder can advance
// on its own thread before MetadataLoaded gets a chance to run.
// The arrival of more data can't change us out of this readyState.
return;
}
+ if (mSrcStream && mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
+ if ((!mHasAudio && !mHasVideo) ||
+ (IsVideo() && mHasVideo && mMediaSize == nsIntSize(-1, -1))) {
+ return;
+ }
+
+ // We are playing a stream that has video and a video frame is now set.
+ // This means we have all metadata needed to change ready state.
+ MediaInfo mediaInfo;
+ mediaInfo.mAudio.mHasAudio = mHasAudio;
+ mediaInfo.mVideo.mHasVideo = mHasVideo;
+ if (mHasVideo) {
+ mediaInfo.mVideo.mDisplay = mMediaSize;
+ }
+ MetadataLoaded(&mediaInfo, nsAutoPtr(nullptr));
+ }
+
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
return;
}
+ if (IsVideo() && mHasVideo && !IsPlaybackEnded() &&
+ GetImageContainer() && !GetImageContainer()->HasCurrentImage()) {
+ // Don't advance if we are playing video, but don't have a video frame.
+ // Also, if video became available after advancing to HAVE_CURRENT_DATA
+ // while we are still playing, we need to revert to HAVE_METADATA until
+ // a video frame is available.
+ ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
+ return;
+ }
+
if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) {
// The decoder has signaled that the download has been suspended by the
// media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
@@ -3365,14 +3452,14 @@ void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState)
bool HTMLMediaElement::CanActivateAutoplay()
{
- // For stream inputs, we activate autoplay on HAVE_CURRENT_DATA because
+ // For stream inputs, we activate autoplay on HAVE_NOTHING because
// this element itself might be blocking the stream from making progress by
// being paused.
return !mPausedForInactiveDocumentOrChannel &&
mAutoplaying &&
mPaused &&
((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
- (mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) &&
+ mSrcStream) &&
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
mAutoplayEnabled &&
!IsEditable();
@@ -3401,24 +3488,14 @@ void HTMLMediaElement::CheckAutoplayDataReady()
VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
{
- // If we have loaded the metadata, and the size of the video is still
- // (-1, -1), the media has no video. Don't go a create a video frame
- // container.
- if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
- mMediaSize == nsIntSize(-1, -1)) {
- return nullptr;
- }
+ if (mVideoFrameContainer)
+ return mVideoFrameContainer;
// Only video frames need an image container.
if (!IsVideo()) {
return nullptr;
}
- mHasVideo = true;
-
- if (mVideoFrameContainer)
- return mVideoFrameContainer;
-
mVideoFrameContainer =
new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
@@ -3533,6 +3610,7 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged()
void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
{
mMediaSize = size;
+ UpdateReadyStateForData(mLastNextFrameStatus);
}
void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h
index 4f5d6e08fbba..61864b31202e 100644
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -281,6 +281,11 @@ public:
void NotifyMediaTrackEnabled(MediaTrack* aTrack);
+ /**
+ * Called when tracks become available to the source media stream.
+ */
+ void NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream);
+
virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
/**
@@ -612,6 +617,7 @@ protected:
virtual ~HTMLMediaElement();
class MediaLoadListener;
+ class MediaStreamTracksAvailableCallback;
class StreamListener;
virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
@@ -1036,6 +1042,9 @@ protected:
nsMediaNetworkState mNetworkState;
nsMediaReadyState mReadyState;
+ // Last value passed from codec or stream source to UpdateReadyStateForData.
+ NextFrameStatus mLastNextFrameStatus;
+
enum LoadAlgorithmState {
// No load algorithm instance is waiting for a source to be added to the
// media in order to continue loading.
diff --git a/dom/media/test/test_streams_srcObject.html b/dom/media/test/test_streams_srcObject.html
index 446c5466b41a..ed9c5866783e 100644
--- a/dom/media/test/test_streams_srcObject.html
+++ b/dom/media/test/test_streams_srcObject.html
@@ -45,6 +45,8 @@ function doTest() {
}
++step;
});
+ a.play();
+ b.play();
}