Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(901)

Unified Diff: Source/core/html/HTMLMediaElement.cpp

Issue 170233009: Initial implementation of AudioTrack, AudioTrackList, VideoTrack, and VideoTrackList. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@blink-master
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/html/HTMLMediaElement.cpp
diff --git a/Source/core/html/HTMLMediaElement.cpp b/Source/core/html/HTMLMediaElement.cpp
index cec3e8f28f9a7b68d9fdd7a07390770f9473bf65..c0269508f989d9d66968ca6cfa968e0b0e3dbb9d 100644
--- a/Source/core/html/HTMLMediaElement.cpp
+++ b/Source/core/html/HTMLMediaElement.cpp
@@ -54,9 +54,13 @@
#include "core/html/MediaKeyEvent.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/RenderLayerCompositor.h"
#include "core/rendering/RenderVideo.h"
@@ -302,6 +306,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
, m_haveVisibleTextTrack(false)
, m_processingPreferenceChange(false)
, m_lastTextTrackUpdateTime(-1)
+ , m_audioTracks(nullptr)
+ , m_videoTracks(nullptr)
, m_textTracks(nullptr)
, m_ignoreTrackDisplayUpdate(0)
#if ENABLE(WEB_AUDIO)
@@ -335,12 +341,12 @@ HTMLMediaElement::~HTMLMediaElement()
m_asyncEventQueue->close();
setShouldDelayLoadEvent(false);
+ if (m_audioTracks)
+ m_audioTracks->clearOwner();
+ if (m_videoTracks)
+ m_videoTracks->clearOwner();
if (m_textTracks)
- m_textTracks->clearOwner();
- if (m_textTracks) {
- for (unsigned i = 0; i < m_textTracks->length(); ++i)
- m_textTracks->item(i)->clearClient();
- }
+ m_textTracks->clearOwnerAndClients();
if (m_mediaController) {
m_mediaController->removeMediaElement(this);
@@ -663,14 +669,38 @@ void HTMLMediaElement::prepareForLoad()
// 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
if (m_networkState != NETWORK_EMPTY) {
+ // 4.1 - Queue a task to fire a simple event named emptied at the media element.
+ scheduleEvent(EventTypeNames::emptied);
+
+ // 4.2 - If a fetching process is in progress for the media element, the user agent should stop it.
m_networkState = NETWORK_EMPTY;
+
+ // 4.3 - Forget the media element's media-resource-specific tracks.
+ forgetResourceSpecificTracks();
+
+ // 4.4 - If readyState is not set to HAVE_NOTHING, then set it to that state.
m_readyState = HAVE_NOTHING;
m_readyStateMaximum = HAVE_NOTHING;
- refreshCachedTime();
+
+ // 4.5 - If the paused attribute is false, then set it to true.
m_paused = true;
+
+ // 4.6 - If seeking is true, set it to false.
m_seeking = false;
+
+ // 4.7 - Set the current playback position to 0.
+ // Set the official playback position to 0.
+ // If this changed the official playback position, then queue a task to fire a simple event named timeupdate at the media element.
+
+ // 4.8 - Set the initial playback position to 0.
+ // FIXME: Make this less subtle. The position only becomes 0 because of the createMediaPlayer() call
+ // above.
+ refreshCachedTime();
invalidateCachedTime();
- scheduleEvent(EventTypeNames::emptied);
+
+ // 4.9 - Set the timeline offset to Not-a-Number (NaN).
+ // 4.10 - Update the duration attribute to Not-a-Number (NaN).
+
updateMediaController();
if (RuntimeEnabledFeatures::videoTrackEnabled())
updateActiveTextTrackCues(0);
@@ -694,6 +724,9 @@ void HTMLMediaElement::prepareForLoad()
// 2 - Asynchronously await a stable state.
m_playedTimeRanges = TimeRanges::create();
+
+ // FIXME: Investigate whether these can be moved into m_networkState != NETWORK_EMPTY block above
+ // so they are closer to the relevant spec steps.
m_lastSeekTime = 0;
m_duration = numeric_limits<double>::quiet_NaN();
@@ -1364,6 +1397,7 @@ void HTMLMediaElement::noneSupported()
m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
// 6.2 - Forget the media element's media-resource-specific text tracks.
+ forgetResourceSpecificTracks();
// 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
m_networkState = NETWORK_NO_SOURCE;
@@ -1437,12 +1471,18 @@ void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
// If we failed while trying to load a <source> element, the movie was never parsed, and there are more
// <source> children, schedule the next one
if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
-
+ // resource selection algorithm
+ // Step 9.Otherwise.9 - Failed with elements: Queue a task, using the DOM manipulation task source, to fire a simple event named error at the candidate element.
if (m_currentSourceNode)
m_currentSourceNode->scheduleErrorEvent();
else
WTF_LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
+ // 9.Otherwise.10 - Asynchronously await a stable state. The synchronous section consists of all the remaining steps of this algorithm until the algorithm says the synchronous section has ended. (Steps in synchronous sections are marked with ⌛.)
+
+ // 9.Otherwise.11 Forget the media element's media-resource-specific tracks.
+ forgetResourceSpecificTracks();
+
if (havePotentialSourceChild()) {
WTF_LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
scheduleNextSourceChild();
@@ -1574,7 +1614,12 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
}
if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
+ createPlaceholderTracksIfNecessary();
+
prepareMediaFragmentURI();
+
+ selectInitialTracksIfNecessary();
+
scheduleEvent(EventTypeNames::durationchange);
if (isVideo())
scheduleEvent(EventTypeNames::resize);
@@ -2452,7 +2497,99 @@ bool HTMLMediaElement::canPlay() const
return paused() || ended() || m_readyState < HAVE_METADATA;
}
-void HTMLMediaElement::mediaPlayerDidAddTrack(WebInbandTextTrack* webTrack)
+AudioTrackList* HTMLMediaElement::audioTracks()
+{
+ ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
+
+ if (!m_audioTracks)
+ m_audioTracks = AudioTrackList::create(this);
+
+ return m_audioTracks.get();
+}
+
+void HTMLMediaElement::didEnabledAudioTrackChange(const AtomicString& audioTrackID, bool enabled)
+{
+ WTF_LOG(Media, "HTMLMediaElement::didEnabledAudioTrackChange('%s', %d)", audioTrackID.ascii().data(), enabled);
+ ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
+ ASSERT(!audioTrackID.isEmpty());
+
+ if (webMediaPlayer())
+ webMediaPlayer()->enabledAudioTrackChange(audioTrackID, enabled);
+}
+
+VideoTrackList* HTMLMediaElement::videoTracks()
+{
+ ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
+
+ if (!m_videoTracks)
+ m_videoTracks = VideoTrackList::create(this);
+
+ return m_videoTracks.get();
+}
+
+void HTMLMediaElement::didSelectedVideoTrackChange(const AtomicString& unselectedTrackID, const AtomicString& selectedTrackID)
+{
+ WTF_LOG(Media, "HTMLMediaElement::didSelectedVideoTrackChange('%s', '%s')", unselectedTrackID.ascii().data(), selectedTrackID.ascii().data());
+ ASSERT(RuntimeEnabledFeatures::audioVideoTracksEnabled());
+ ASSERT(!unselectedTrackID.isEmpty() || !selectedTrackID.isEmpty());
+
+ if (webMediaPlayer())
+ webMediaPlayer()->selectedVideoTrackChange(unselectedTrackID, selectedTrackID);
+}
+
+void HTMLMediaElement::mediaPlayerDidAddVideoTrack(const AtomicString& id, const AtomicString& kind, const AtomicString& label, const AtomicString& language, bool selected)
+{
+ WTF_LOG(Media, "HTMLMediaElement::mediaPlayerDidAddVideoTrack('%s', '%s', '%s', '%s', %d)",
+ id.ascii().data(), kind.ascii().data(), label.ascii().data(), language.ascii().data(), selected);
+ ASSERT(!id.isEmpty());
+
+ RefPtr<VideoTrack> videoTrack = VideoTrack::create(videoTracks(), id, kind, label, language);
+ videoTracks()->add(videoTrack.get());
+
+ if (selected)
+ videoTrack->setSelected(true);
+}
+
+void HTMLMediaElement::mediaPlayerDidRemoveVideoTrack(const AtomicString& id)
+{
+ WTF_LOG(Media, "HTMLMediaElement::mediaPlayerDidRevmoeVideoTrack('%s')", id.ascii().data());
+ ASSERT(!id.isEmpty());
+
+ if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
+ return;
+
+ videoTracks()->remove(id);
+}
+
+void HTMLMediaElement::mediaPlayerDidAddAudioTrack(const AtomicString& id, const AtomicString& kind, const AtomicString& label, const AtomicString& language, bool enabled)
+{
+ WTF_LOG(Media, "HTMLMediaElement::mediaPlayerDidAddAudioTrack('%s', '%s', '%s', '%s', %d)",
+ id.ascii().data(), kind.ascii().data(), label.ascii().data(), language.ascii().data(), enabled);
+ ASSERT(!id.isEmpty());
+
+ if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
+ return;
+
+ AtomicString kindString;
+ RefPtr<AudioTrack> audioTrack = AudioTrack::create(audioTracks(), id, kind, label, language);
+ audioTracks()->add(audioTrack.get());
+
+ if (enabled)
+ audioTrack->setEnabled(true);
+}
+
+void HTMLMediaElement::mediaPlayerDidRemoveAudioTrack(const AtomicString& id)
+{
+ WTF_LOG(Media, "HTMLMediaElement::mediaPlayerDidRevmoeAudioTrack('%s')", id.ascii().data());
+ ASSERT(!id.isEmpty());
+
+ if (!RuntimeEnabledFeatures::audioVideoTracksEnabled())
+ return;
+
+ audioTracks()->remove(id);
+}
+
+void HTMLMediaElement::mediaPlayerDidAddTextTrack(WebInbandTextTrack* webTrack)
{
if (!RuntimeEnabledFeatures::videoTrackEnabled())
return;
@@ -2484,10 +2621,10 @@ void HTMLMediaElement::mediaPlayerDidAddTrack(WebInbandTextTrack* webTrack)
// 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent
// interface, with the track attribute initialized to the text track's TextTrack object, at the media element's
// textTracks attribute's TextTrackList object.
- addTrack(textTrack.get());
+ addTextTrack(textTrack.get());
}
-void HTMLMediaElement::mediaPlayerDidRemoveTrack(WebInbandTextTrack* webTrack)
+void HTMLMediaElement::mediaPlayerDidRemoveTextTrack(WebInbandTextTrack* webTrack)
{
if (!RuntimeEnabledFeatures::videoTrackEnabled())
return;
@@ -2501,7 +2638,7 @@ void HTMLMediaElement::mediaPlayerDidRemoveTrack(WebInbandTextTrack* webTrack)
if (!textTrack)
return;
- removeTrack(textTrack.get());
+ removeTextTrack(textTrack.get(), true);
}
void HTMLMediaElement::closeCaptionTracksChanged()
@@ -2510,36 +2647,44 @@ void HTMLMediaElement::closeCaptionTracksChanged()
mediaControls()->closedCaptionTracksChanged();
}
-void HTMLMediaElement::addTrack(TextTrack* track)
+void HTMLMediaElement::addTextTrack(TextTrack* track)
{
textTracks()->append(track);
closeCaptionTracksChanged();
}
-void HTMLMediaElement::removeTrack(TextTrack* track)
+void HTMLMediaElement::removeTextTrack(TextTrack* track, bool fireRemoveTrackEvent)
{
TrackDisplayUpdateScope scope(this);
TextTrackCueList* cues = track->cues();
if (cues)
textTrackRemoveCues(track, cues);
- m_textTracks->remove(track);
+ m_textTracks->remove(track, fireRemoveTrackEvent);
closeCaptionTracksChanged();
}
-void HTMLMediaElement::removeAllInbandTracks()
+void HTMLMediaElement::forgetResourceSpecificTracks()
{
- if (!m_textTracks)
- return;
-
- TrackDisplayUpdateScope scope(this);
- for (int i = m_textTracks->length() - 1; i >= 0; --i) {
- TextTrack* track = m_textTracks->item(i);
+ // 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);
+ for (int i = m_textTracks->length() - 1; i >= 0; --i) {
+ TextTrack* track = m_textTracks->item(i);
- if (track->trackType() == TextTrack::InBand)
- removeTrack(track);
+ if (track->trackType() == TextTrack::InBand)
+ removeTextTrack(track, false);
+ }
}
+
+ if (m_audioTracks)
+ m_audioTracks->removeAll();
+
+ if (m_videoTracks)
+ m_videoTracks->removeAll();
}
PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const AtomicString& kind, const AtomicString& label, const AtomicString& language, ExceptionState& exceptionState)
@@ -2567,7 +2712,7 @@ PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const AtomicString& kind, c
// first append the track to the text track list.
// 6. Add the new text track to the media element's list of text tracks.
- addTrack(textTrack.get());
+ addTextTrack(textTrack.get());
// ... its text track readiness state to the text track loaded state ...
textTrack->setReadinessState(TextTrack::Loaded);
@@ -2603,7 +2748,7 @@ void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
if (!textTrack)
return;
- addTrack(textTrack.get());
+ addTextTrack(textTrack.get());
// Do not schedule the track loading until parsing finishes so we don't start before all tracks
// in the markup have been added.
@@ -2641,7 +2786,7 @@ void HTMLMediaElement::didRemoveTrack(HTMLTrackElement* trackElement)
// When a track element's parent element changes and the old parent was a media element,
// then the user agent must remove the track element's corresponding text track from the
// media element's list of text tracks.
- removeTrack(textTrack.get());
+ removeTextTrack(textTrack.get(), true);
size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
if (index != kNotFound)
@@ -3394,7 +3539,7 @@ void HTMLMediaElement::clearMediaPlayerAndAudioSourceProviderClient()
void HTMLMediaElement::clearMediaPlayer(int flags)
{
- removeAllInbandTracks();
+ forgetResourceSpecificTracks();
closeMediaSource();
@@ -3861,6 +4006,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();
}
@@ -3923,4 +4070,32 @@ 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())
+ mediaPlayerDidAddAudioTrack("audio", AudioTrack::mainKeyword(), "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())
+ mediaPlayerDidAddVideoTrack("video", VideoTrack::mainKeyword(), "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);
+}
+
}

Powered by Google App Engine
This is Rietveld 408576698