Index: Source/core/html/track/AutomaticTrackSelection.cpp |
diff --git a/Source/core/html/track/AutomaticTrackSelection.cpp b/Source/core/html/track/AutomaticTrackSelection.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..925a3bea2e6ab542bdcb36d668a97a59d13ab080 |
--- /dev/null |
+++ b/Source/core/html/track/AutomaticTrackSelection.cpp |
@@ -0,0 +1,205 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "config.h" |
+#include "core/html/track/AutomaticTrackSelection.h" |
+ |
+#include "core/html/track/TextTrack.h" |
+#include "core/html/track/TextTrackList.h" |
+#include "platform/Language.h" |
+ |
+namespace blink { |
+ |
+class TrackGroup { |
+ STACK_ALLOCATED(); |
+public: |
+ enum GroupKind { |
+ CaptionsAndSubtitles, |
+ Description, |
+ Chapter, |
+ Metadata |
+ }; |
+ |
+ explicit TrackGroup(GroupKind kind) |
+ : visibleTrack(nullptr) |
+ , defaultTrack(nullptr) |
+ , kind(kind) |
+ , hasSrcLang(false) |
+ { |
+ } |
+ |
+ WillBeHeapVector<RefPtrWillBeMember<TextTrack>> tracks; |
+ RefPtrWillBeMember<TextTrack> visibleTrack; |
+ RefPtrWillBeMember<TextTrack> defaultTrack; |
+ GroupKind kind; |
+ bool hasSrcLang; |
+}; |
+ |
+static int textTrackLanguageSelectionScore(const TextTrack& track) |
+{ |
+ if (track.language().isEmpty()) |
+ return 0; |
+ |
+ Vector<AtomicString> languages = userPreferredLanguages(); |
+ size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track.language(), languages); |
+ if (languageMatchIndex >= languages.size()) |
+ return 0; |
+ |
+ return languages.size() - languageMatchIndex; |
+} |
+ |
+static int textTrackSelectionScore(const TextTrack& track) |
+{ |
+ if (track.kind() != TextTrack::captionsKeyword() && track.kind() != TextTrack::subtitlesKeyword()) |
+ return 0; |
+ |
+ return textTrackLanguageSelectionScore(track); |
+} |
+ |
+AutomaticTrackSelection::AutomaticTrackSelection(const Configuration& configuration) |
+ : m_configuration(configuration) |
+{ |
+} |
+ |
+void AutomaticTrackSelection::performAutomaticTextTrackSelection(const TrackGroup& group) |
+{ |
+ ASSERT(group.tracks.size()); |
+ |
+ // First, find the track in the group that should be enabled (if any). |
+ WillBeHeapVector<RefPtrWillBeMember<TextTrack>> currentlyEnabledTracks; |
+ RefPtrWillBeRawPtr<TextTrack> trackToEnable = nullptr; |
+ RefPtrWillBeRawPtr<TextTrack> defaultTrack = nullptr; |
+ RefPtrWillBeRawPtr<TextTrack> fallbackTrack = nullptr; |
+ int highestTrackScore = 0; |
+ for (size_t i = 0; i < group.tracks.size(); ++i) { |
+ RefPtrWillBeRawPtr<TextTrack> textTrack = group.tracks[i]; |
+ |
+ if (m_configuration.disableCurrentlyEnabledTracks && textTrack->mode() == TextTrack::showingKeyword()) |
+ currentlyEnabledTracks.append(textTrack); |
+ |
+ int trackScore = textTrackSelectionScore(*textTrack); |
+ if (trackScore) { |
+ // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a |
+ // track with this text track kind, text track language, and text track label enabled, and there is no |
+ // other text track in the media element's list of text tracks with a text track kind of either subtitles |
+ // or captions whose text track mode is showing |
+ // ... |
+ // * If the text track kind is chapters and the text track language is one that the user agent has reason |
+ // to believe is appropriate for the user, and there is no other text track in the media element's list of |
+ // text tracks with a text track kind of chapters whose text track mode is showing |
+ // Let the text track mode be showing. |
+ if (trackScore > highestTrackScore) { |
+ highestTrackScore = trackScore; |
+ trackToEnable = textTrack; |
+ } |
+ |
+ if (!defaultTrack && textTrack->isDefault()) |
+ defaultTrack = textTrack; |
+ if (!defaultTrack && !fallbackTrack) |
+ fallbackTrack = textTrack; |
+ } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) { |
+ // * If the track element has a default attribute specified, and there is no other text track in the media |
+ // element's list of text tracks whose text track mode is showing or showing by default |
+ // Let the text track mode be showing by default. |
+ defaultTrack = textTrack; |
+ } |
+ } |
+ |
+ if (!trackToEnable && defaultTrack) |
+ trackToEnable = defaultTrack; |
+ |
+ // If no track matches the user's preferred language and non was marked 'default', enable the first track |
+ // because the user has explicitly stated a preference for this kind of track. |
+ if (!fallbackTrack && m_configuration.forceEnableSubtitleOrCaptionTrack && group.kind == TrackGroup::CaptionsAndSubtitles) |
+ fallbackTrack = group.tracks[0]; |
+ |
+ if (!trackToEnable && fallbackTrack) |
+ trackToEnable = fallbackTrack; |
+ |
+ if (currentlyEnabledTracks.size()) { |
+ for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) { |
+ RefPtrWillBeRawPtr<TextTrack> textTrack = currentlyEnabledTracks[i]; |
+ if (textTrack != trackToEnable) |
+ textTrack->setMode(TextTrack::disabledKeyword()); |
+ } |
+ } |
+ |
+ if (trackToEnable) |
+ trackToEnable->setMode(TextTrack::showingKeyword()); |
+} |
+ |
+void AutomaticTrackSelection::enableDefaultMetadataTextTracks(const TrackGroup& group) |
+{ |
+ ASSERT(group.tracks.size()); |
+ |
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#honor-user-preferences-for-automatic-text-track-selection |
+ |
+ // 4. If there are any text tracks in the media element's list of text |
+ // tracks whose text track kind is metadata that correspond to track |
+ // elements with a default attribute set whose text track mode is set to |
+ // disabled, then set the text track mode of all such tracks to hidden |
+ for (auto& textTrack : group.tracks) { |
+ if (textTrack->mode() != TextTrack::disabledKeyword()) |
+ continue; |
+ if (!textTrack->isDefault()) |
+ continue; |
+ textTrack->setMode(TextTrack::hiddenKeyword()); |
+ } |
+} |
+ |
+void AutomaticTrackSelection::perform(TextTrackList& textTracks) |
+{ |
+ TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles); |
+ TrackGroup descriptionTracks(TrackGroup::Description); |
+ TrackGroup chapterTracks(TrackGroup::Chapter); |
+ TrackGroup metadataTracks(TrackGroup::Metadata); |
+ |
+ for (size_t i = 0; i < textTracks.length(); ++i) { |
+ RefPtrWillBeRawPtr<TextTrack> textTrack = textTracks.item(i); |
+ if (!textTrack) |
+ continue; |
+ |
+ String kind = textTrack->kind(); |
+ TrackGroup* currentGroup; |
+ if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) { |
+ currentGroup = &captionAndSubtitleTracks; |
+ } else if (kind == TextTrack::descriptionsKeyword()) { |
+ currentGroup = &descriptionTracks; |
+ } else if (kind == TextTrack::chaptersKeyword()) { |
+ currentGroup = &chapterTracks; |
+ } else { |
+ ASSERT(kind == TextTrack::metadataKeyword()); |
+ currentGroup = &metadataTracks; |
+ } |
+ |
+ if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword()) |
+ currentGroup->visibleTrack = textTrack; |
+ if (!currentGroup->defaultTrack && textTrack->isDefault()) |
+ currentGroup->defaultTrack = textTrack; |
+ |
+ // Do not add this track to the group if it has already been automatically configured |
+ // as we only want to perform selection once per track so that adding another track |
+ // after the initial configuration doesn't reconfigure every track - only those that |
+ // should be changed by the new addition. For example all metadata tracks are |
+ // disabled by default, and we don't want a track that has been enabled by script |
+ // to be disabled automatically when a new metadata track is added later. |
+ if (textTrack->hasBeenConfigured()) |
+ continue; |
+ |
+ if (textTrack->language().length()) |
+ currentGroup->hasSrcLang = true; |
+ currentGroup->tracks.append(textTrack); |
+ } |
+ |
+ if (captionAndSubtitleTracks.tracks.size()) |
+ performAutomaticTextTrackSelection(captionAndSubtitleTracks); |
+ if (descriptionTracks.tracks.size()) |
+ performAutomaticTextTrackSelection(descriptionTracks); |
+ if (chapterTracks.tracks.size()) |
+ performAutomaticTextTrackSelection(chapterTracks); |
+ if (metadataTracks.tracks.size()) |
+ enableDefaultMetadataTextTracks(metadataTracks); |
+} |
+ |
+} // namespace blink |