зеркало из https://github.com/mozilla/gecko-dev.git
Bug 879717 - Part 1 - Delay entering HAVE_CURRENT_DATA state until a video frame has been stored in the image container. r=roc
This commit is contained in:
Родитель
c1b3e5deb9
Коммит
d92e123dc4
|
@ -71,6 +71,8 @@
|
||||||
#include "mozilla/dom/MediaSource.h"
|
#include "mozilla/dom/MediaSource.h"
|
||||||
#include "MediaMetadataManager.h"
|
#include "MediaMetadataManager.h"
|
||||||
#include "MediaSourceDecoder.h"
|
#include "MediaSourceDecoder.h"
|
||||||
|
#include "AudioStreamTrack.h"
|
||||||
|
#include "VideoStreamTrack.h"
|
||||||
|
|
||||||
#include "AudioChannelService.h"
|
#include "AudioChannelService.h"
|
||||||
|
|
||||||
|
@ -663,7 +665,10 @@ void HTMLMediaElement::AbortExistingLoads()
|
||||||
mHaveQueuedSelectResource = false;
|
mHaveQueuedSelectResource = false;
|
||||||
mSuspendedForPreloadNone = false;
|
mSuspendedForPreloadNone = false;
|
||||||
mDownloadSuspendedByCache = false;
|
mDownloadSuspendedByCache = false;
|
||||||
|
mHasAudio = false;
|
||||||
|
mHasVideo = false;
|
||||||
mSourcePointer = nullptr;
|
mSourcePointer = nullptr;
|
||||||
|
mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
|
||||||
|
|
||||||
mTags = nullptr;
|
mTags = nullptr;
|
||||||
|
|
||||||
|
@ -897,6 +902,30 @@ void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
|
||||||
|
{
|
||||||
|
if (!mSrcStream || mSrcStream != aStream) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oldHasVideo = mHasVideo;
|
||||||
|
|
||||||
|
nsAutoTArray<nsRefPtr<AudioStreamTrack>,1> audioTracks;
|
||||||
|
mSrcStream->GetAudioTracks(audioTracks);
|
||||||
|
nsAutoTArray<nsRefPtr<VideoStreamTrack>,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()
|
void HTMLMediaElement::LoadFromSourceChildren()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mDelayingLoadEvent,
|
NS_ASSERTION(mDelayingLoadEvent,
|
||||||
|
@ -1981,6 +2010,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
||||||
mCurrentLoadID(0),
|
mCurrentLoadID(0),
|
||||||
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
|
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
|
||||||
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
|
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
|
||||||
|
mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
|
||||||
mLoadWaitStatus(NOT_WAITING),
|
mLoadWaitStatus(NOT_WAITING),
|
||||||
mVolume(1.0),
|
mVolume(1.0),
|
||||||
mPreloadAction(PRELOAD_UNDEFINED),
|
mPreloadAction(PRELOAD_UNDEFINED),
|
||||||
|
@ -2818,6 +2848,25 @@ private:
|
||||||
bool mPendingNotifyOutput;
|
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)
|
void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already");
|
NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already");
|
||||||
|
@ -2839,22 +2888,26 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
||||||
if (mPausedForInactiveDocumentOrChannel) {
|
if (mPausedForInactiveDocumentOrChannel) {
|
||||||
GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
|
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);
|
ChangeDelayLoadStatus(false);
|
||||||
GetSrcMediaStream()->AddAudioOutput(this);
|
GetSrcMediaStream()->AddAudioOutput(this);
|
||||||
GetSrcMediaStream()->SetAudioOutputVolume(this, float(mMuted ? 0.0 : mVolume));
|
SetVolumeInternal();
|
||||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||||
if (container) {
|
if (container) {
|
||||||
GetSrcMediaStream()->AddVideoOutput(container);
|
GetSrcMediaStream()->AddVideoOutput(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckAutoplayDataReady();
|
||||||
|
|
||||||
// Note: we must call DisconnectTrackListListeners(...) before dropping
|
// Note: we must call DisconnectTrackListListeners(...) before dropping
|
||||||
// mSrcStream
|
// mSrcStream
|
||||||
mSrcStream->ConstructMediaTracks(AudioTracks(), VideoTracks());
|
mSrcStream->ConstructMediaTracks(AudioTracks(), VideoTracks());
|
||||||
|
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
|
|
||||||
DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
|
|
||||||
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
|
|
||||||
AddRemoveSelfReference();
|
AddRemoveSelfReference();
|
||||||
// FirstFrameLoaded() will be called when the stream has current data.
|
// FirstFrameLoaded() will be called when the stream has current data.
|
||||||
}
|
}
|
||||||
|
@ -2935,6 +2988,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
||||||
} else {
|
} else {
|
||||||
UpdateMediaSize(aInfo->mVideo.mDisplay);
|
UpdateMediaSize(aInfo->mVideo.mDisplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsVideo() && aInfo->HasVideo()) {
|
||||||
|
// We are a video element playing video so update the screen wakelock
|
||||||
|
NotifyOwnerDocumentActivityChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::FirstFrameLoaded()
|
void HTMLMediaElement::FirstFrameLoaded()
|
||||||
|
@ -3195,18 +3253,47 @@ bool HTMLMediaElement::ShouldCheckAllowOrigin()
|
||||||
|
|
||||||
void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
|
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
|
// aNextFrame might have a next frame because the decoder can advance
|
||||||
// on its own thread before MetadataLoaded gets a chance to run.
|
// on its own thread before MetadataLoaded gets a chance to run.
|
||||||
// The arrival of more data can't change us out of this readyState.
|
// The arrival of more data can't change us out of this readyState.
|
||||||
return;
|
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<const MetadataTags>(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
|
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
|
||||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
||||||
return;
|
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()) {
|
if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) {
|
||||||
// The decoder has signaled that the download has been suspended by the
|
// 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
|
// media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
|
||||||
|
@ -3354,14 +3441,14 @@ void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState)
|
||||||
|
|
||||||
bool HTMLMediaElement::CanActivateAutoplay()
|
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
|
// this element itself might be blocking the stream from making progress by
|
||||||
// being paused.
|
// being paused.
|
||||||
return !mPausedForInactiveDocumentOrChannel &&
|
return !mPausedForInactiveDocumentOrChannel &&
|
||||||
mAutoplaying &&
|
mAutoplaying &&
|
||||||
mPaused &&
|
mPaused &&
|
||||||
((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
|
((mDecoder && mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) ||
|
||||||
(mSrcStream && mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA)) &&
|
mSrcStream) &&
|
||||||
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
||||||
mAutoplayEnabled &&
|
mAutoplayEnabled &&
|
||||||
!IsEditable();
|
!IsEditable();
|
||||||
|
@ -3390,24 +3477,14 @@ void HTMLMediaElement::CheckAutoplayDataReady()
|
||||||
|
|
||||||
VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
|
VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
|
||||||
{
|
{
|
||||||
// If we have loaded the metadata, and the size of the video is still
|
if (mVideoFrameContainer)
|
||||||
// (-1, -1), the media has no video. Don't go a create a video frame
|
return mVideoFrameContainer;
|
||||||
// container.
|
|
||||||
if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
|
||||||
mMediaSize == nsIntSize(-1, -1)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only video frames need an image container.
|
// Only video frames need an image container.
|
||||||
if (!IsVideo()) {
|
if (!IsVideo()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
mHasVideo = true;
|
|
||||||
|
|
||||||
if (mVideoFrameContainer)
|
|
||||||
return mVideoFrameContainer;
|
|
||||||
|
|
||||||
mVideoFrameContainer =
|
mVideoFrameContainer =
|
||||||
new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
|
new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
|
||||||
|
|
||||||
|
@ -3520,6 +3597,7 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged()
|
||||||
void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
|
void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
|
||||||
{
|
{
|
||||||
mMediaSize = size;
|
mMediaSize = size;
|
||||||
|
UpdateReadyStateForData(mLastNextFrameStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
|
void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
|
||||||
|
|
|
@ -281,6 +281,11 @@ public:
|
||||||
|
|
||||||
void NotifyMediaTrackEnabled(MediaTrack* aTrack);
|
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;
|
virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -612,6 +617,7 @@ protected:
|
||||||
virtual ~HTMLMediaElement();
|
virtual ~HTMLMediaElement();
|
||||||
|
|
||||||
class MediaLoadListener;
|
class MediaLoadListener;
|
||||||
|
class MediaStreamTracksAvailableCallback;
|
||||||
class StreamListener;
|
class StreamListener;
|
||||||
|
|
||||||
virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
|
virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
|
||||||
|
@ -1036,6 +1042,9 @@ protected:
|
||||||
nsMediaNetworkState mNetworkState;
|
nsMediaNetworkState mNetworkState;
|
||||||
nsMediaReadyState mReadyState;
|
nsMediaReadyState mReadyState;
|
||||||
|
|
||||||
|
// Last value passed from codec or stream source to UpdateReadyStateForData.
|
||||||
|
NextFrameStatus mLastNextFrameStatus;
|
||||||
|
|
||||||
enum LoadAlgorithmState {
|
enum LoadAlgorithmState {
|
||||||
// No load algorithm instance is waiting for a source to be added to the
|
// No load algorithm instance is waiting for a source to be added to the
|
||||||
// media in order to continue loading.
|
// media in order to continue loading.
|
||||||
|
|
|
@ -45,6 +45,8 @@ function doTest() {
|
||||||
}
|
}
|
||||||
++step;
|
++step;
|
||||||
});
|
});
|
||||||
|
a.play();
|
||||||
|
b.play();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче