Support "forced" subtitles
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 12 Apr 2013 17:23:42 +0000 (17:23 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 12 Apr 2013 17:23:42 +0000 (17:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=114460

Reviewed by Jer Noble.

Source/WebCore:

Test: media/track/track-forced-subtitles.html

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::configureTextTrackGroup): Enable a forced track if nothing else is enabled.
(WebCore::HTMLMediaElement::configureTextTracks): Include forced tracks with captions+subtitles.

* html/track/InbandTextTrack.cpp:
(WebCore::InbandTextTrack::InbandTextTrack): Deal with kind=forced.

* html/track/TextTrack.cpp:
(WebCore::TextTrack::forcedKeyword): New keyword.
(WebCore::TextTrack::isValidKindKeyword): Include forced.
(WebCore::TextTrack::platformTextTrack): Ditto.
* html/track/TextTrack.h:

* page/CaptionUserPreferences.cpp:
(WebCore::CaptionUserPreferences::primaryAudioTrackLanguageOverride): New override to support testing.
* page/CaptionUserPreferences.h:

* page/CaptionUserPreferencesMac.mm:
(WebCore::CaptionUserPreferencesMac::textTrackSelectionScore): Support forced tracks.
* platform/graphics/InbandTextTrackPrivate.h:

* platform/graphics/PlatformTextTrack.h: Add Forced, minor cleanup.

* platform/graphics/avfoundation/InbandTextTrackPrivateAVF.cpp:
(WebCore::InbandTextTrackPrivateAVF::processCue): Drive-by enhancement: log cue position.

* platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.mm:
(WebCore::InbandTextTrackPrivateAVFObjC::kind): Support forced.
(WebCore::InbandTextTrackPrivateAVFObjC::label): Drive-by cleanup.

* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::processTextTracks): Don't filter out forced tracks.
(WebCore::MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack): Log the language returned.

* testing/Internals.cpp:
(WebCore::Internals::setPrimaryAudioTrackLanguageOverride): New.
(WebCore::Internals::setCaptionDisplayMode): New.
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

* media/content/CC+Subtitles.m4v: Added.
* media/track/track-forced-subtitles-in-band-expected.txt: Added.
* media/track/track-forced-subtitles-in-band.html: Added.
* media/trackmenu-test.js:

* platform/efl/TestExpectations: Skip new test.
* platform/gtk/TestExpectations: Ditto.
* platform/mac/TestExpectations: Ditto.
* platform/qt/TestExpectations: Ditto.
* platform/win/TestExpectations: Ditto.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@148285 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
LayoutTests/ChangeLog
LayoutTests/media/track/track-forced-subtitles-in-band-expected.txt [new file with mode: 0644]
LayoutTests/media/track/track-forced-subtitles-in-band.html [new file with mode: 0644]
LayoutTests/media/trackmenu-test.js
LayoutTests/platform/efl/TestExpectations
LayoutTests/platform/gtk/TestExpectations
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/qt/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/track/InbandTextTrack.cpp
Source/WebCore/html/track/TextTrack.cpp
Source/WebCore/html/track/TextTrack.h
Source/WebCore/page/CaptionUserPreferences.cpp
Source/WebCore/page/CaptionUserPreferences.h
Source/WebCore/page/CaptionUserPreferencesMac.mm
Source/WebCore/platform/graphics/InbandTextTrackPrivate.h
Source/WebCore/platform/graphics/PlatformTextTrack.h
Source/WebCore/platform/graphics/avfoundation/InbandTextTrackPrivateAVF.cpp
Source/WebCore/platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 025f8fc..48e8010 100644 (file)
@@ -1,3 +1,21 @@
+2013-04-12  Eric Carlson  <eric.carlson@apple.com>
+
+        Support "forced" subtitles
+        https://bugs.webkit.org/show_bug.cgi?id=114460
+
+        Reviewed by Jer Noble.
+
+        * media/content/CC+Subtitles.m4v: Added.
+        * media/track/track-forced-subtitles-in-band-expected.txt: Added.
+        * media/track/track-forced-subtitles-in-band.html: Added.
+        * media/trackmenu-test.js:
+
+        * platform/efl/TestExpectations: Skip new test.
+        * platform/gtk/TestExpectations: Ditto.
+        * platform/mac/TestExpectations: Ditto.
+        * platform/qt/TestExpectations: Ditto.
+        * platform/win/TestExpectations: Ditto.
+
 2013-04-12  Ryosuke Niwa  <rniwa@webkit.org>
 
         [Mac] Enable spellchecking tests added in r141471
diff --git a/LayoutTests/media/track/track-forced-subtitles-in-band-expected.txt b/LayoutTests/media/track/track-forced-subtitles-in-band-expected.txt
new file mode 100644 (file)
index 0000000..dcea32a
--- /dev/null
@@ -0,0 +1,52 @@
+Tests that forced subtitles are enable automatically.
+
+RUN(internals.settings.setShouldDisplayTrackKind('Captions', false))
+RUN(internals.settings.setShouldDisplayTrackKind('Subtitles', true))
+RUN(internals.setUserPreferredLanguages(['en']))
+RUN(internals.setPrimaryAudioTrackLanguageOverride('fr'))
+RUN(internals.setCaptionDisplayMode('ForcedOnly'))
+EVENT(canplaythrough)
+
+** Forced tracks should be in .textTracks, but not in the menu
+EXPECTED (video.textTracks.length == '9') OK
+EXPECTED (trackMenuItems.length == '6') OK
+
+** Only the 'fr' forced track should be showing
+EXPECTED (video.textTracks[0].language == 'en') OK
+EXPECTED (video.textTracks[0].kind == 'subtitles') OK
+EXPECTED (video.textTracks[0].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[1].language == 'en') OK
+EXPECTED (video.textTracks[1].kind == 'forced') OK
+EXPECTED (video.textTracks[1].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[2].language == 'fr') OK
+EXPECTED (video.textTracks[2].kind == 'subtitles') OK
+EXPECTED (video.textTracks[2].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[3].language == 'fr') OK
+EXPECTED (video.textTracks[3].kind == 'forced') OK
+EXPECTED (video.textTracks[3].mode == 'showing') OK
+
+EXPECTED (video.textTracks[4].language == 'es') OK
+EXPECTED (video.textTracks[4].kind == 'subtitles') OK
+EXPECTED (video.textTracks[4].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[5].language == 'es') OK
+EXPECTED (video.textTracks[5].kind == 'forced') OK
+EXPECTED (video.textTracks[5].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[6].language == 'de') OK
+EXPECTED (video.textTracks[6].kind == 'subtitles') OK
+EXPECTED (video.textTracks[6].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[7].language == 'de') OK
+EXPECTED (video.textTracks[7].kind == 'forced') OK
+EXPECTED (video.textTracks[7].mode == 'disabled') OK
+
+EXPECTED (video.textTracks[8].language == 'en') OK
+EXPECTED (video.textTracks[8].kind == 'captions') OK
+EXPECTED (video.textTracks[8].mode == 'disabled') OK
+
+END OF TEST
+
diff --git a/LayoutTests/media/track/track-forced-subtitles-in-band.html b/LayoutTests/media/track/track-forced-subtitles-in-band.html
new file mode 100644 (file)
index 0000000..183c380
--- /dev/null
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+        <script src=../media-controls.js></script>
+        <script src=../trackmenu-test.js></script>
+        <script src=../video-test.js></script>
+        <script>
+            var test = 0;
+
+            function testForced()
+            {
+                consoleWrite("<br><i>** Forced tracks should be in .textTracks, but not in the menu<" + "/i>");
+                testExpected("video.textTracks.length", 9);
+
+                // Click the CC button to show the menu because it isn't created until it becomes visible.
+                clickCCButton();
+
+                trackMenuItems = trackMenuList();
+                if (!trackMenuItems)
+                    return;
+                testExpected("trackMenuItems.length", 6);
+
+                consoleWrite("<br><i>** Only the 'fr' forced track should be showing<" + "/i>");
+                testExpected("video.textTracks[0].language", "en");
+                testExpected("video.textTracks[0].kind", "subtitles");
+                testExpected("video.textTracks[0].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[1].language", "en");
+                testExpected("video.textTracks[1].kind", "forced");
+                testExpected("video.textTracks[1].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[2].language", "fr");
+                testExpected("video.textTracks[2].kind", "subtitles");
+                testExpected("video.textTracks[2].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[3].language", "fr");
+                testExpected("video.textTracks[3].kind", "forced");
+                testExpected("video.textTracks[3].mode", "showing");
+                consoleWrite("");
+
+                testExpected("video.textTracks[4].language", "es");
+                testExpected("video.textTracks[4].kind", "subtitles");
+                testExpected("video.textTracks[4].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[5].language", "es");
+                testExpected("video.textTracks[5].kind", "forced");
+                testExpected("video.textTracks[5].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[6].language", "de");
+                testExpected("video.textTracks[6].kind", "subtitles");
+                testExpected("video.textTracks[6].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[7].language", "de");
+                testExpected("video.textTracks[7].kind", "forced");
+                testExpected("video.textTracks[7].mode", "disabled");
+                consoleWrite("");
+
+                testExpected("video.textTracks[8].language", "en");
+                testExpected("video.textTracks[8].kind", "captions");
+                testExpected("video.textTracks[8].mode", "disabled");
+                consoleWrite("");
+
+                endTest();
+            }
+
+            function setup()
+            {
+                findMediaElement();
+                run("internals.settings.setShouldDisplayTrackKind('Captions', false)");
+                run("internals.settings.setShouldDisplayTrackKind('Subtitles', true)");
+                run("internals.setUserPreferredLanguages(['en'])");
+                run("internals.setPrimaryAudioTrackLanguageOverride('fr')");
+                run("internals.setCaptionDisplayMode('ForcedOnly')");
+                video.src = '../content/CC+Subtitles.m4v';
+                video.volume = 0;
+                waitForEvent('canplaythrough', testForced);
+            }
+
+        </script>
+    </head>
+    <body onload="setup()">
+        <p>Tests that forced subtitles are enable automatically.</p>
+        <video width="640" height="360" controls>
+        </video>
+    </body>
+</html>
index 2fcd4b5..5810714 100644 (file)
@@ -1,7 +1,16 @@
-var captionsButtonCoordinates;
+var captionsButtonCoordinates = null;
 
 function clickCCButton()
 {
+    if (!captionsButtonCoordinates) {
+        try {
+            captionsButtonCoordinates = mediaControlsButtonCoordinates(video, "toggle-closed-captions-button");
+        } catch (exception) {
+            failTest(exception.description);
+            return;
+        }
+    }
+
     eventSender.mouseMoveTo(captionsButtonCoordinates[0], captionsButtonCoordinates[1]);
     eventSender.mouseDown();
     eventSender.mouseUp();
@@ -13,12 +22,6 @@ function startTrackMenuTest(testFunction)
         consoleWrite("<br>*** Set the user language preference.");
         run("internals.setUserPreferredLanguages(['en'])");
 
-        try {
-            captionsButtonCoordinates = mediaControlsButtonCoordinates(video, "toggle-closed-captions-button");
-        } catch (exception) {
-            failTest(exception.description);
-            return;
-        }
         clickCCButton();
         window.setTimeout(testFunction, 100);
     }
index 438b1c3..d12d496 100644 (file)
@@ -1637,6 +1637,7 @@ Bug(EFL) media/track/track-in-band.html [ Skip ]
 Bug(EFL) media/track/track-in-band-cues-added-once.html [ Skip ]
 Bug(EFL) media/track/track-in-band-style.html [ Skip ]
 Bug(EFL) media/track/track-in-band-legacy-api.html [ Skip ]
+Bug(EFL) media/track/track-forced-subtitles-in-band.html [ Skip ]
 
 # All debug bots timeout (crash) on this one
 webkit.org/b/56496 [ Debug ] fast/js/array-sort-modifying-tostring.html [ Crash Pass ]
index 9c8f778..92c7ce7 100644 (file)
@@ -403,6 +403,7 @@ webkit.org/b/103771 media/track/track-in-band.html [ Failure ]
 webkit.org/b/103771 media/track/track-in-band-cues-added-once.html [ Timeout ]
 webkit.org/b/103771 media/track/track-in-band-style.html [ Timeout ]
 webkit.org/b/103771 media/track/track-in-band-legacy-api.html [ Failure ]
+webkit.org/b/103771 media/track/track-forced-subtitles-in-band.html [ Failure ]
 
 #Incorrect rebaseline
 webkit.org/b/107818 media/controls-styling.html [ Failure ]
index b87e7c0..30d9625 100644 (file)
@@ -1366,6 +1366,7 @@ webkit.org/b/103663 [ MountainLion Lion SnowLeopard ] media/track/track-in-band.
 webkit.org/b/103663 [ MountainLion Lion SnowLeopard ] media/track/track-in-band-cues-added-once.html
 webkit.org/b/103663 [ MountainLion Lion SnowLeopard ] media/track/track-in-band-style.html
 webkit.org/b/103663 [ MountainLion Lion SnowLeopard ] media/track/track-in-band-legacy-api.html
+webkit.org/b/103663 [ MountainLion Lion SnowLeopard ] media/track/track-forced-subtitles-in-band.html
 
 # These two tests sometimes timeout
 webkit.org/b/112492 media/track/track-language-preference.html [ Failure Pass ]
index c57bce2..2f80b01 100644 (file)
@@ -2623,6 +2623,7 @@ webkit.org/b/103769 media/track/track-in-band.html [ Skip ]
 webkit.org/b/103769 media/track/track-in-band-cues-added-once.html [ Skip ]
 webkit.org/b/103769 media/track/track-in-band-style.html [ Skip ]
 webkit.org/b/103769 media/track/track-in-band-legacy-api.html [ Skip ]
+webkit.org/b/103769 media/track/track-forced-subtitles-in-band.html [ Skip ]
 
 webkit.org/b/104150 fast/media/implicit-media-all.html [ ImageOnlyFailure ]
 
index 8d818b3..e90f90d 100644 (file)
@@ -2539,6 +2539,7 @@ webkit.org/b/103770 media/track/track-in-band.html [ Skip ]
 webkit.org/b/103770 media/track/track-in-band-cues-added-once.html [ Skip ]
 webkit.org/b/103770 media/track/track-in-band-style.html [ Skip ]
 webkit.org/b/103770 media/track/track-in-band-legacy-api.html [ Skip ]
+webkit.org/b/103770 media/track/track-forced-subtitles-in-band.html [ Skip ]
 
 # https://bugs.webkit.org/show_bug.cgi?id=97026
 fast/events/keydown-leftright-keys.html
index f3a8180..312df46 100644 (file)
@@ -1,3 +1,52 @@
+2013-04-12  Eric Carlson  <eric.carlson@apple.com>
+
+        Support "forced" subtitles
+        https://bugs.webkit.org/show_bug.cgi?id=114460
+
+        Reviewed by Jer Noble.
+
+        Test: media/track/track-forced-subtitles.html
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::configureTextTrackGroup): Enable a forced track if nothing else is enabled.
+        (WebCore::HTMLMediaElement::configureTextTracks): Include forced tracks with captions+subtitles.
+
+        * html/track/InbandTextTrack.cpp:
+        (WebCore::InbandTextTrack::InbandTextTrack): Deal with kind=forced.
+
+        * html/track/TextTrack.cpp:
+        (WebCore::TextTrack::forcedKeyword): New keyword.
+        (WebCore::TextTrack::isValidKindKeyword): Include forced.
+        (WebCore::TextTrack::platformTextTrack): Ditto.
+        * html/track/TextTrack.h:
+
+        * page/CaptionUserPreferences.cpp:
+        (WebCore::CaptionUserPreferences::primaryAudioTrackLanguageOverride): New override to support testing.
+        * page/CaptionUserPreferences.h:
+
+        * page/CaptionUserPreferencesMac.mm:
+        (WebCore::CaptionUserPreferencesMac::textTrackSelectionScore): Support forced tracks.
+        * platform/graphics/InbandTextTrackPrivate.h:
+
+        * platform/graphics/PlatformTextTrack.h: Add Forced, minor cleanup.
+
+        * platform/graphics/avfoundation/InbandTextTrackPrivateAVF.cpp:
+        (WebCore::InbandTextTrackPrivateAVF::processCue): Drive-by enhancement: log cue position.
+
+        * platform/graphics/avfoundation/objc/InbandTextTrackPrivateAVFObjC.mm:
+        (WebCore::InbandTextTrackPrivateAVFObjC::kind): Support forced.
+        (WebCore::InbandTextTrackPrivateAVFObjC::label): Drive-by cleanup.
+
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::processTextTracks): Don't filter out forced tracks.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack): Log the language returned.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::setPrimaryAudioTrackLanguageOverride): New.
+        (WebCore::Internals::setCaptionDisplayMode): New.
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2013-04-12  Ryosuke Niwa  <rniwa@webkit.org>
 
         [Mac] Some ligatures are applied across different fronts
index c5bd6e0..a7b51a6 100644 (file)
@@ -3074,7 +3074,9 @@ void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
     RefPtr<TextTrack> trackToEnable;
     RefPtr<TextTrack> defaultTrack;
     RefPtr<TextTrack> fallbackTrack;
+    RefPtr<TextTrack> forcedSubitleTrack;
     int highestTrackScore = 0;
+    int highestForcedScore = 0;
     for (size_t i = 0; i < group.tracks.size(); ++i) {
         RefPtr<TextTrack> textTrack = group.tracks[i];
 
@@ -3101,6 +3103,10 @@ void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
                 defaultTrack = textTrack;
             if (!defaultTrack && !fallbackTrack)
                 fallbackTrack = textTrack;
+            if (textTrack->containsOnlyForcedSubtitles() && trackScore > highestForcedScore) {
+                forcedSubitleTrack = textTrack;
+                highestForcedScore = trackScore;
+            }
         } 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
@@ -3112,6 +3118,11 @@ void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
     if (!trackToEnable && defaultTrack)
         trackToEnable = defaultTrack;
 
+    // If no track matches the user's preferred language, none was marked as 'default', and there is a forced subtitle track
+    // in the same language as the language of the primary audio track, enable it.
+    if (!trackToEnable && forcedSubitleTrack)
+        trackToEnable = forcedSubitleTrack;
+
     // 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 (!trackToEnable && fallbackTrack)
@@ -3189,7 +3200,7 @@ void HTMLMediaElement::configureTextTracks()
 
         String kind = textTrack->kind();
         TrackGroup* currentGroup;
-        if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
+        if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword() || kind == TextTrack::forcedKeyword())
             currentGroup = &captionAndSubtitleTracks;
         else if (kind == TextTrack::descriptionsKeyword())
             currentGroup = &descriptionTracks;
index d252f53..f9b93a1 100644 (file)
@@ -69,6 +69,9 @@ InbandTextTrack::InbandTextTrack(ScriptExecutionContext* context, TextTrackClien
     case InbandTextTrackPrivate::Metadata:
         setKind(TextTrack::metadataKeyword());
         break;
+    case InbandTextTrackPrivate::Forced:
+        setKind(TextTrack::forcedKeyword());
+        break;
     case InbandTextTrackPrivate::None:
     default:
         ASSERT_NOT_REACHED();
index 3c274b1..319ddea 100644 (file)
@@ -76,6 +76,12 @@ const AtomicString& TextTrack::metadataKeyword()
     DEFINE_STATIC_LOCAL(const AtomicString, metadata, ("metadata", AtomicString::ConstructFromLiteral));
     return metadata;
 }
+    
+const AtomicString& TextTrack::forcedKeyword()
+{
+    DEFINE_STATIC_LOCAL(const AtomicString, forced, ("forced", AtomicString::ConstructFromLiteral));
+    return forced;
+}
 
 const AtomicString& TextTrack::disabledKeyword()
 {
@@ -155,6 +161,8 @@ bool TextTrack::isValidKindKeyword(const AtomicString& value)
         return true;
     if (value == metadataKeyword())
         return true;
+    if (value == forcedKeyword())
+        return true;
 
     return false;
 }
@@ -513,6 +521,8 @@ PassRefPtr<PlatformTextTrack> TextTrack::platformTextTrack()
         kind = PlatformTextTrack::Chapter;
     else if (m_kind == metadataKeyword())
         kind = PlatformTextTrack::MetaData;
+    else if (m_kind == forcedKeyword())
+        kind = PlatformTextTrack::Forced;
 
     PlatformTextTrack::TrackType type = PlatformTextTrack::OutOfBand;
     if (m_trackType == TrackElement)
index 576945b..e2eb663 100644 (file)
@@ -87,6 +87,7 @@ public:
     static const AtomicString& descriptionsKeyword();
     static const AtomicString& chaptersKeyword();
     static const AtomicString& metadataKeyword();
+    static const AtomicString& forcedKeyword();
     static bool isValidKindKeyword(const AtomicString&);
 
     AtomicString label() const { return m_label; }
index 383d996..0021b86 100644 (file)
@@ -244,6 +244,12 @@ void CaptionUserPreferences::updateCaptionStyleSheetOveride()
         Vector<String>(), InjectInAllFrames, UserStyleAuthorLevel, InjectInExistingDocuments);
 }
 
+String CaptionUserPreferences::primaryAudioTrackLanguageOverride() const
+{
+    if (!m_primaryAudioTrackLanguageOverride.isEmpty())
+        return m_primaryAudioTrackLanguageOverride;
+    return defaultLanguage();
+}
     
 }
 
index f367a49..cdb2e02 100644 (file)
@@ -81,6 +81,9 @@ public:
     virtual String displayNameForTrack(TextTrack*) const;
     virtual Vector<RefPtr<TextTrack> > sortedTrackListForMenu(TextTrackList*);
 
+    void setPrimaryAudioTrackLanguageOverride(const String& language) { m_primaryAudioTrackLanguageOverride = language;  }
+    String primaryAudioTrackLanguageOverride() const;
+
     virtual bool testingMode() const { return m_testingMode; }
     virtual void setTestingMode(bool override) { m_testingMode = override; }
     
@@ -99,6 +102,7 @@ private:
     Timer<CaptionUserPreferences> m_timer;
     String m_userPreferredLanguage;
     String m_captionsStyleSheetOverride;
+    String m_primaryAudioTrackLanguageOverride;
     bool m_testingMode;
     bool m_havePreferences;
 };
index c326f2e..bf5c4d1 100644 (file)
@@ -619,37 +619,54 @@ String CaptionUserPreferencesMac::displayNameForTrack(TextTrack* track) const
 int CaptionUserPreferencesMac::textTrackSelectionScore(TextTrack* track, HTMLMediaElement* mediaElement) const
 {
     CaptionDisplayMode displayMode = captionDisplayMode();
-    if (displayMode == ForcedOnly)
-        return 0;
     if (displayMode == AlwaysOn && (!userPrefersSubtitles() && !userPrefersCaptions()))
         return 0;
-    if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword())
-        return 0;
-    if (track->containsOnlyForcedSubtitles())
+    if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->kind() != TextTrack::forcedKeyword())
         return 0;
     if (!track->isMainProgramContent())
         return 0;
+    if (displayMode == ForcedOnly && !track->containsOnlyForcedSubtitles())
+        return 0;
 
     Vector<String> userPreferredCaptionLanguages = preferredLanguages();
 
-    if (displayMode == Automatic) {
+    if (displayMode == Automatic || track->containsOnlyForcedSubtitles()) {
 
-        // Only enable a text track if the current audio track is not in the user's preferred language.
         if (!mediaElement || !mediaElement->player())
             return 0;
 
-        String audioTrackLanguage = mediaElement->player()->languageOfPrimaryAudioTrack();
+        String audioTrackLanguage;
+
+        Vector<String> languageList;
+        languageList.reserveCapacity(1);
+
+        if (testingMode())
+            audioTrackLanguage = primaryAudioTrackLanguageOverride();
+        else
+            audioTrackLanguage = mediaElement->player()->languageOfPrimaryAudioTrack();
 
         if (audioTrackLanguage.isEmpty())
             return 0;
 
-        Vector<String> languages;
-        languages.append(defaultLanguage());
-        size_t offset = indexOfBestMatchingLanguageInList(audioTrackLanguage, languages);
-        if (!offset)
-            return 0;
+        if (displayMode == Automatic) {
+            // Only enable a text track if the current audio track is not in the user's preferred language.
+            languageList.append(defaultLanguage());
+            size_t offset = indexOfBestMatchingLanguageInList(audioTrackLanguage, languageList);
+            if (offset)
+                return 0;
+        } else {
+            // Only consider a forced-only track if it is in the same language as the primary audio track.
+            String trackLanguage = track->language();
+            if (trackLanguage.isEmpty())
+                return 0;
+
+            languageList.append(audioTrackLanguage);
+            size_t offset = indexOfBestMatchingLanguageInList(trackLanguage, languageList);
+            if (offset)
+                return 0;
+        }
 
-        userPreferredCaptionLanguages = languages;
+        userPreferredCaptionLanguages = languageList;
     }
 
     int trackScore = 0;
index b3f7b2e..3eeccfa 100644 (file)
@@ -49,11 +49,23 @@ public:
     void setClient(InbandTextTrackPrivateClient* client) { m_client = client; }
     InbandTextTrackPrivateClient* client() { return m_client; }
 
-    enum Mode { Disabled, Hidden, Showing };
+    enum Mode {
+        Disabled,
+        Hidden,
+        Showing
+    };
     virtual void setMode(Mode mode) { m_mode = mode; };
     virtual InbandTextTrackPrivate::Mode mode() const { return m_mode; }
 
-    enum Kind { Subtitles, Captions, Descriptions, Chapters, Metadata, None };
+    enum Kind {
+        Subtitles,
+        Captions,
+        Descriptions,
+        Chapters,
+        Metadata,
+        Forced,
+        None
+    };
     virtual Kind kind() const { return Subtitles; }
     virtual bool isClosedCaptions() const { return false; }
     virtual bool containsOnlyForcedSubtitles() const { return false; }
index 91e14fb..d23799e 100644 (file)
@@ -46,8 +46,19 @@ public:
 
 class PlatformTextTrack : public RefCounted<PlatformTextTrack> {
 public:
-    enum TrackKind { Subtitle = 0, Caption = 1, Description = 2, Chapter = 3, MetaData = 4 };
-    enum TrackType { InBand = 0, OutOfBand = 1, Script = 2 };
+    enum TrackKind {
+        Subtitle = 0,
+        Caption = 1,
+        Description = 2,
+        Chapter = 3,
+        MetaData = 4,
+        Forced = 5,
+    };
+    enum TrackType {
+        InBand = 0,
+        OutOfBand = 1,
+        Script = 2
+    };
 
     static PassRefPtr<PlatformTextTrack> create(PlatformTextTrackClient* client, const String& label, const String& language, TrackKind kind, TrackType type)
     {
index 3e2a3d5..eb85d7d 100644 (file)
@@ -356,7 +356,7 @@ void InbandTextTrackPrivateAVF::processCue(CFArrayRef attributedStrings, double
                 if (cueData->position() >= 0 && cueData->size() > 0)
                     cueData->setPosition(cueData->position() - cueData->size() / 2);
                 
-                LOG(Media, "InbandTextTrackPrivateAVF::processCue(%p) - adding cue for time %.2f", this, cueData->startTime());
+                LOG(Media, "InbandTextTrackPrivateAVF::processCue(%p) - adding cue for time = %.2f, position =  %.2f, line =  %.2f", this, cueData->startTime(), cueData->position(), cueData->line());
                 client()->addGenericCue(this, cueData);
             }
         } else
index 6e7e8f3..1fb64f1 100644 (file)
@@ -99,10 +99,14 @@ InbandTextTrackPrivate::Kind InbandTextTrackPrivateAVFObjC::kind() const
         return None;
 
     NSString *mediaType = [m_mediaSelectionOption mediaType];
+    
     if ([mediaType isEqualToString:AVMediaTypeClosedCaption])
         return Captions;
     if ([mediaType isEqualToString:AVMediaTypeSubtitle]) {
 
+        if ([m_mediaSelectionOption hasMediaCharacteristic:AVMediaCharacteristicContainsOnlyForcedSubtitles])
+            return Forced;
+
         // An "SDH" track is a subtitle track created for the deaf or hard-of-hearing. "captions" in WebVTT are
         // "labeled as appropriate for the hard-of-hearing", so tag SDH sutitles as "captions".
         if ([m_mediaSelectionOption hasMediaCharacteristic:AVMediaCharacteristicTranscribesSpokenDialogForAccessibility])
@@ -153,17 +157,20 @@ AtomicString InbandTextTrackPrivateAVFObjC::label() const
     if (!m_mediaSelectionOption)
         return emptyAtom;
 
+    NSString *title = 0;
+
     NSArray *titles = [AVMetadataItem metadataItemsFromArray:[m_mediaSelectionOption.get() commonMetadata] withKey:AVMetadataCommonKeyTitle keySpace:AVMetadataKeySpaceCommon];
     if ([titles count]) {
         // If possible, return a title in one of the user's preferred languages.
         NSArray *titlesForPreferredLanguages = [AVMetadataItem metadataItemsFromArray:titles filteredAndSortedAccordingToPreferredLanguages:[NSLocale preferredLanguages]];
         if ([titlesForPreferredLanguages count])
-            return [[titlesForPreferredLanguages objectAtIndex:0] stringValue];
+            title = [[titlesForPreferredLanguages objectAtIndex:0] stringValue];
 
-        return [[titles objectAtIndex:0] stringValue];
+        if (!title)
+            title = [[titles objectAtIndex:0] stringValue];
     }
 
-    return emptyAtom;
+    return title ? AtomicString(title) : emptyAtom;
 }
 
 AtomicString InbandTextTrackPrivateAVFObjC::language() const
index 3b487bb..7e43f76 100644 (file)
@@ -1331,9 +1331,6 @@ void MediaPlayerPrivateAVFoundationObjC::processTextTracks()
         if (!newTrack)
             continue;
 
-        if ([[option mediaType] isEqualToString:AVMediaTypeSubtitle] && [option hasMediaCharacteristic:AVMediaCharacteristicContainsOnlyForcedSubtitles])
-            continue;
-
         m_textTracks.append(InbandTextTrackPrivateAVFObjC::create(this, option));
     }
 
@@ -1404,6 +1401,8 @@ String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
     AVMediaSelectionOptionType *currentlySelectedAudibleOption = [m_avPlayerItem.get() selectedMediaOptionInMediaSelectionGroup:audibleGroup];
     if (currentlySelectedAudibleOption) {
         m_languageOfPrimaryAudioTrack = [[currentlySelectedAudibleOption locale] localeIdentifier];
+        LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of selected audible option: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
+
         return m_languageOfPrimaryAudioTrack;
     }
 #endif // HAVE(AVFOUNDATION_TEXT_TRACK_SUPPORT)
@@ -1413,6 +1412,7 @@ String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
     NSArray *tracks = [m_avAsset.get() tracksWithMediaType:AVMediaTypeAudio];
     if (!tracks || [tracks count] != 1) {
         m_languageOfPrimaryAudioTrack = emptyString();
+        LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - %i audio tracks, returning emptyString()", this, (tracks ? [tracks count] : 0));
         return m_languageOfPrimaryAudioTrack;
     }
 
@@ -1422,9 +1422,11 @@ String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
     // Some legacy tracks have "und" as a language, treat that the same as no language at all.
     if (language && ![language isEqualToString:@"und"]) {
         m_languageOfPrimaryAudioTrack = language;
+        LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of single audio track: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
         return m_languageOfPrimaryAudioTrack;
     }
 
+    LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - single audio track has no language, returning emptyString()", this);
     m_languageOfPrimaryAudioTrack = emptyString();
     return m_languageOfPrimaryAudioTrack;
 }
index 8420ce8..b9ea3f7 100644 (file)
@@ -2202,4 +2202,43 @@ void Internals::setCaptionsStyleSheetOverride(const String& override, ExceptionC
 #endif
 }
 
+void Internals::setPrimaryAudioTrackLanguageOverride(const String& language, ExceptionCode& ec)
+{
+    Document* document = contextDocument();
+    if (!document || !document->page()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+
+#if ENABLE(VIDEO_TRACK) && !PLATFORM(WIN)
+    document->page()->group().captionPreferences()->setPrimaryAudioTrackLanguageOverride(language);
+#else
+    UNUSED_PARAM(language);
+#endif
+}
+
+void Internals::setCaptionDisplayMode(const String& mode, ExceptionCode& ec)
+{
+    Document* document = contextDocument();
+    if (!document || !document->page()) {
+        ec = INVALID_ACCESS_ERR;
+        return;
+    }
+    
+#if ENABLE(VIDEO_TRACK) && !PLATFORM(WIN)
+    CaptionUserPreferences* captionPreferences = document->page()->group().captionPreferences();
+    
+    if (equalIgnoringCase(mode, "Automatic"))
+        captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::Automatic);
+    else if (equalIgnoringCase(mode, "ForcedOnly"))
+        captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::ForcedOnly);
+    else if (equalIgnoringCase(mode, "AlwaysOn"))
+        captionPreferences->setCaptionDisplayMode(CaptionUserPreferences::AlwaysOn);
+    else
+        ec = SYNTAX_ERR;
+#else
+    UNUSED_PARAM(mode);
+#endif
+}
+
 }
index 69400de..5b83acb 100644 (file)
@@ -318,6 +318,8 @@ public:
 
     String captionsStyleSheetOverride(ExceptionCode&);
     void setCaptionsStyleSheetOverride(const String&, ExceptionCode&);
+    void setPrimaryAudioTrackLanguageOverride(const String&, ExceptionCode&);
+    void setCaptionDisplayMode(const String&, ExceptionCode&);
 
 private:
     explicit Internals(Document*);
index 0fc98a6..34b6e59 100644 (file)
 
     [Conditional=VIDEO_TRACK] DOMString captionsStyleSheetOverride() raises (DOMException);
     [Conditional=VIDEO_TRACK] void setCaptionsStyleSheetOverride(in DOMString override) raises (DOMException);
+    [Conditional=VIDEO_TRACK] void setPrimaryAudioTrackLanguageOverride(in DOMString language) raises(DOMException);
+    [Conditional=VIDEO_TRACK] void setCaptionDisplayMode(in DOMString mode) raises (DOMException);
 
     boolean isSelectPopupVisible(in Node node);
 };