| Index: Source/core/html/HTMLMediaElement.cpp
|
| diff --git a/Source/core/html/HTMLMediaElement.cpp b/Source/core/html/HTMLMediaElement.cpp
|
| index 22612f5dd6175836b9271abc2ce8af76b83d82fb..42b79a83d1a768af624e2b1439dde7d3eb7c4ce0 100644
|
| --- a/Source/core/html/HTMLMediaElement.cpp
|
| +++ b/Source/core/html/HTMLMediaElement.cpp
|
| @@ -52,9 +52,13 @@
|
| #include "core/html/MediaFragmentURIParser.h"
|
| #include "core/html/TimeRanges.h"
|
| #include "core/html/shadow/MediaControls.h"
|
| +#include "core/html/track/AudioTrack.h"
|
| +#include "core/html/track/AudioTrackList.h"
|
| #include "core/html/track/InbandTextTrack.h"
|
| #include "core/html/track/TextTrackCueList.h"
|
| #include "core/html/track/TextTrackList.h"
|
| +#include "core/html/track/VideoTrack.h"
|
| +#include "core/html/track/VideoTrackList.h"
|
| #include "core/loader/FrameLoader.h"
|
| #include "core/rendering/RenderVideo.h"
|
| #include "core/rendering/RenderView.h"
|
| @@ -85,6 +89,7 @@
|
| using namespace std;
|
| using blink::WebInbandTextTrack;
|
| using blink::WebMimeRegistry;
|
| +using blink::WebMediaPlayerClient;
|
|
|
| namespace WebCore {
|
|
|
| @@ -163,6 +168,52 @@ private:
|
| HTMLMediaElement* m_mediaElement;
|
| };
|
|
|
| +static const AtomicString& AudioKindToString(WebMediaPlayerClient::AudioTrackKind kind)
|
| +{
|
| + switch (kind) {
|
| + case WebMediaPlayerClient::AudioTrackKindNone:
|
| + return emptyAtom;
|
| + case WebMediaPlayerClient::AudioTrackKindAlternative:
|
| + return AudioTrack::alternativeKeyword();
|
| + case WebMediaPlayerClient::AudioTrackKindDescriptions:
|
| + return AudioTrack::descriptionsKeyword();
|
| + case WebMediaPlayerClient::AudioTrackKindMain:
|
| + return AudioTrack::mainKeyword();
|
| + case WebMediaPlayerClient::AudioTrackKindMainDescriptions:
|
| + return AudioTrack::mainDescriptionsKeyword();
|
| + case WebMediaPlayerClient::AudioTrackKindTranslation:
|
| + return AudioTrack::translationKeyword();
|
| + case WebMediaPlayerClient::AudioTrackKindCommentary:
|
| + return AudioTrack::commentaryKeyword();
|
| + }
|
| +
|
| + ASSERT_NOT_REACHED();
|
| + return emptyAtom;
|
| +}
|
| +
|
| +static const AtomicString& VideoKindToString(WebMediaPlayerClient::VideoTrackKind kind)
|
| +{
|
| + switch (kind) {
|
| + case WebMediaPlayerClient::VideoTrackKindNone:
|
| + return emptyAtom;
|
| + case WebMediaPlayerClient::VideoTrackKindAlternative:
|
| + return VideoTrack::alternativeKeyword();
|
| + case WebMediaPlayerClient::VideoTrackKindCaptions:
|
| + return VideoTrack::captionsKeyword();
|
| + case WebMediaPlayerClient::VideoTrackKindMain:
|
| + return VideoTrack::mainKeyword();
|
| + case WebMediaPlayerClient::VideoTrackKindSign:
|
| + return VideoTrack::signKeyword();
|
| + case WebMediaPlayerClient::VideoTrackKindSubtitles:
|
| + return VideoTrack::subtitlesKeyword();
|
| + case WebMediaPlayerClient::VideoTrackKindCommentary:
|
| + return VideoTrack::commentaryKeyword();
|
| + }
|
| +
|
| + ASSERT_NOT_REACHED();
|
| + return emptyAtom;
|
| +}
|
| +
|
| static bool canLoadURL(const KURL& url, const ContentType& contentType, const String& keySystem)
|
| {
|
| DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
|
| @@ -234,6 +285,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
|
| , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
|
| , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
|
| , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
|
| + , m_audioTracksTimer(this, &HTMLMediaElement::audioTracksTimerFired)
|
| , m_playedTimeRanges()
|
| , m_asyncEventQueue(GenericEventQueue::create(this))
|
| , m_playbackRate(1.0f)
|
| @@ -277,6 +329,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
|
| , m_haveVisibleTextTrack(false)
|
| , m_processingPreferenceChange(false)
|
| , m_lastTextTrackUpdateTime(-1)
|
| + , m_audioTracks(AudioTrackList::create(*this))
|
| + , m_videoTracks(VideoTrackList::create(*this))
|
| , m_textTracks(nullptr)
|
| , m_ignoreTrackDisplayUpdate(0)
|
| #if ENABLE(WEB_AUDIO)
|
| @@ -309,6 +363,9 @@ HTMLMediaElement::~HTMLMediaElement()
|
| if (m_textTracks)
|
| m_textTracks->clearOwner();
|
|
|
| + m_audioTracks->shutdown();
|
| + m_videoTracks->shutdown();
|
| +
|
| if (m_mediaController) {
|
| m_mediaController->removeMediaElement(this);
|
| m_mediaController = nullptr;
|
| @@ -368,6 +425,7 @@ void HTMLMediaElement::didMoveToNewDocument(Document& oldDocument)
|
| oldDocument.decrementLoadEventDelayCount();
|
|
|
| ActiveDOMObject::didMoveToNewExecutionContext(&document());
|
| +
|
| HTMLElement::didMoveToNewDocument(oldDocument);
|
| }
|
|
|
| @@ -604,6 +662,7 @@ void HTMLMediaElement::prepareForLoad()
|
| // Perform the cleanup required for the resource load algorithm to run.
|
| stopPeriodicTimers();
|
| m_loadTimer.stop();
|
| + m_audioTracksTimer.stop();
|
| m_sentEndEvent = false;
|
| m_sentStalledEvent = false;
|
| m_haveFiredLoadedData = false;
|
| @@ -1566,7 +1625,12 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
|
| }
|
|
|
| if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
|
| + createPlaceholderTracksIfNecessary();
|
| +
|
| prepareMediaFragmentURI();
|
| +
|
| + selectInitialTracksIfNecessary();
|
| +
|
| scheduleEvent(EventTypeNames::durationchange);
|
| if (isHTMLVideoElement(*this))
|
| scheduleEvent(EventTypeNames::resize);
|
| @@ -2189,6 +2253,107 @@ bool HTMLMediaElement::canPlay() const
|
| return paused() || ended() || m_readyState < HAVE_METADATA;
|
| }
|
|
|
| +AudioTrackList& HTMLMediaElement::audioTracks()
|
| +{
|
| + ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
|
| + return *m_audioTracks;
|
| +}
|
| +
|
| +void HTMLMediaElement::audioTrackChanged(const String& audioTrackID)
|
| +{
|
| + WTF_LOG(Media, "HTMLMediaElement::audioTrackChanged('%s')", audioTrackID.ascii().data());
|
| + ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
|
| + ASSERT(!audioTrackID.isEmpty());
|
| +
|
| + audioTracks().scheduleChangeEvent();
|
| +
|
| + // FIXME: Add call on m_mediaSource to notify it of track changes once the SourceBuffer.audioTracks attribute is added.
|
| +
|
| + if (!m_audioTracksTimer.isActive())
|
| + m_audioTracksTimer.startOneShot(0, FROM_HERE);
|
| +}
|
| +
|
| +void HTMLMediaElement::addAudioTrack(const String& id, blink::WebMediaPlayerClient::AudioTrackKind kind, const AtomicString& label, const AtomicString& language, bool enabled)
|
| +{
|
| + AtomicString kindString = AudioKindToString(kind);
|
| + WTF_LOG(Media, "HTMLMediaElement::addAudioTrack('%s', '%s', '%s', '%s', %d)",
|
| + id.ascii().data(), kindString.ascii().data(), label.ascii().data(), language.ascii().data(), enabled);
|
| + ASSERT(!id.isEmpty());
|
| +
|
| + if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
|
| + return;
|
| +
|
| + RefPtr<AudioTrack> audioTrack = AudioTrack::create(this, id, kindString, label, language);
|
| + audioTracks().add(audioTrack.get());
|
| +
|
| + if (enabled)
|
| + audioTrack->setEnabled(true);
|
| +}
|
| +
|
| +void HTMLMediaElement::removeAudioTrack(const String& id)
|
| +{
|
| + WTF_LOG(Media, "HTMLMediaElement::removeAudioTrack('%s')", id.ascii().data());
|
| + ASSERT(!id.isEmpty());
|
| +
|
| + if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
|
| + return;
|
| +
|
| + audioTracks().remove(id);
|
| +}
|
| +
|
| +VideoTrackList& HTMLMediaElement::videoTracks()
|
| +{
|
| + ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
|
| + return *m_videoTracks;
|
| +}
|
| +
|
| +void HTMLMediaElement::selectedVideoTrackChanged(const String& selectedTrackID)
|
| +{
|
| + WTF_LOG(Media, "HTMLMediaElement::selectedVideoTrackChanged('%s')", selectedTrackID.ascii().data());
|
| + ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
|
| +
|
| + String unselectedTrackID;
|
| +
|
| + int oldSelectedIndex = videoTracks().selectedIndex();
|
| + if (oldSelectedIndex != -1)
|
| + unselectedTrackID = videoTracks().anonymousIndexedGetter(oldSelectedIndex)->id();
|
| +
|
| + ASSERT(unselectedTrackID != selectedTrackID);
|
| + ASSERT(!unselectedTrackID.isEmpty() || !selectedTrackID.isEmpty());
|
| +
|
| + videoTracks().trackSelected(selectedTrackID);
|
| +
|
| + // FIXME: Add call on m_mediaSource to notify it of track changes once the SourceBuffer.videoTracks attribute is added.
|
| +
|
| + if (webMediaPlayer())
|
| + webMediaPlayer()->selectedVideoTrackChanged(selectedTrackID);
|
| +}
|
| +
|
| +void HTMLMediaElement::addVideoTrack(const String& id, blink::WebMediaPlayerClient::VideoTrackKind kind, const AtomicString& label, const AtomicString& language, bool selected)
|
| +{
|
| + AtomicString kindString = VideoKindToString(kind);
|
| + WTF_LOG(Media, "HTMLMediaElement::addVideoTrack('%s', '%s', '%s', '%s', %d)",
|
| + id.ascii().data(), kindString.ascii().data(), label.ascii().data(), language.ascii().data(), selected);
|
| + ASSERT(!id.isEmpty());
|
| +
|
| + RefPtr<VideoTrack> videoTrack = VideoTrack::create(this, id, kindString, label, language);
|
| + videoTracks().add(videoTrack.get());
|
| +
|
| + if (selected)
|
| + videoTrack->setSelected(true);
|
| +}
|
| +
|
| +void HTMLMediaElement::removeVideoTrack(const String& id)
|
| +{
|
| + WTF_LOG(Media, "HTMLMediaElement::removeVideoTrack('%s')", id.ascii().data());
|
| + ASSERT(!id.isEmpty());
|
| +
|
| + if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
|
| + return;
|
| +
|
| + videoTracks().remove(id);
|
| +}
|
| +
|
| void HTMLMediaElement::mediaPlayerDidAddTextTrack(WebInbandTextTrack* webTrack)
|
| {
|
| if (!RuntimeEnabledFeatures::videoTrackEnabled())
|
| @@ -2264,11 +2429,17 @@ void HTMLMediaElement::removeTextTrack(TextTrack* track)
|
|
|
| void HTMLMediaElement::forgetResourceSpecificTracks()
|
| {
|
| + // Implements the "forget the media element's media-resource-specific tracks" algorithm.
|
| + // The order is explicitly specified as text, then audio, and finally video. Also
|
| + // 'removetrack' events should not be fired.
|
| if (m_textTracks) {
|
| TrackDisplayUpdateScope scope(this);
|
| m_textTracks->removeAllInbandTracks();
|
| closeCaptionTracksChanged();
|
| }
|
| +
|
| + m_audioTracks->removeAll();
|
| + m_videoTracks->removeAll();
|
| }
|
|
|
| PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const AtomicString& kind, const AtomicString& label, const AtomicString& language, ExceptionState& exceptionState)
|
| @@ -3123,6 +3294,7 @@ void HTMLMediaElement::clearMediaPlayer(int flags)
|
|
|
| stopPeriodicTimers();
|
| m_loadTimer.stop();
|
| + m_audioTracksTimer.stop();
|
|
|
| m_pendingActionFlags &= ~flags;
|
| m_loadState = WaitingForSource;
|
| @@ -3566,6 +3738,8 @@ void HTMLMediaElement::prepareMediaFragmentURI()
|
| } else
|
| m_fragmentEndTime = MediaPlayer::invalidTime();
|
|
|
| + // FIXME: Add support for selecting tracks by ID with the Media Fragments track dimension.
|
| +
|
| if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA)
|
| prepareToPlay();
|
| }
|
| @@ -3624,4 +3798,47 @@ bool HTMLMediaElement::isInteractiveContent() const
|
| return fastHasAttribute(controlsAttr);
|
| }
|
|
|
| +void HTMLMediaElement::createPlaceholderTracksIfNecessary()
|
| +{
|
| + if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
|
| + return;
|
| +
|
| + // Create a placeholder audio track if |m_player| says it has audio but it didn't explicitly announce the tracks.
|
| + if (m_player->hasAudio() && !audioTracks().length())
|
| + addAudioTrack("audio", WebMediaPlayerClient::AudioTrackKindMain, "Audio Track", "", true);
|
| +
|
| + // Create a placeholder video track if |m_player| says it has video but it didn't explicitly announce the tracks.
|
| + if (m_player->hasVideo() && !videoTracks().length())
|
| + addVideoTrack("video", WebMediaPlayerClient::VideoTrackKindMain, "Video Track", "", true);
|
| +}
|
| +
|
| +void HTMLMediaElement::selectInitialTracksIfNecessary()
|
| +{
|
| + if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
|
| + return;
|
| +
|
| + // Enable the first audio track if an audio track hasn't been enabled yet.
|
| + if (audioTracks().length() > 0 && !audioTracks().hasEnabledTrack())
|
| + audioTracks().anonymousIndexedGetter(0)->setEnabled(true);
|
| +
|
| + // Select the first video track if a video track hasn't been selected yet.
|
| + if (videoTracks().length() > 0 && videoTracks().selectedIndex() == -1)
|
| + videoTracks().anonymousIndexedGetter(0)->setSelected(true);
|
| +}
|
| +
|
| +void HTMLMediaElement::audioTracksTimerFired(Timer<HTMLMediaElement>*)
|
| +{
|
| + if (!webMediaPlayer())
|
| + return;
|
| +
|
| + Vector<String> enabledTrackIDs;
|
| + for (unsigned i = 0; i < audioTracks().length(); ++i) {
|
| + RefPtr<AudioTrack> track = audioTracks().anonymousIndexedGetter(i);
|
| + if (track->enabled())
|
| + enabledTrackIDs.append(track->id());
|
| + }
|
| +
|
| + webMediaPlayer()->enabledAudioTracksChanged(enabledTrackIDs);
|
| +}
|
| +
|
| }
|
|
|