Bug 1474149 - Allow media seek amount so be configured draft
authorh-h <henrik.hank@aol.de>
Mon, 09 Jul 2018 06:28:20 +0200
changeset 815527 ca20160475fdcf131994e8256a619008a1009d6f
parent 815516 ffb7b5015fc331bdc4c5e6ab52b9de669faa8864
child 815528 ac31505c57a5af2d129b783f3c36164f911fcebf
push id115529
push userbmo:henrik.hank@aol.de
push dateMon, 09 Jul 2018 04:36:12 +0000
bugs1474149, 15000
milestone63.0a1
Bug 1474149 - Allow media seek amount so be configured When you play audio or video in Firefox, you can jump by a fixed amount of 15 seconds, as this help page explains: https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_media-shortcuts. Here are media files for testing. You may have to click on the pause/play button, before the keystrokes will be recognized: - Audio: http://wilwheaton.typepad.com/files/wil_wheaton_vs_text_2_speech.mp3 - Video: http://www.belleslettres.eu/video/konjunktiv-irrealis.mp4 15 seconds is an unusual big seek step. These apps all have 5 seconds as their default seek step: - YouTube - foobar2000 (popular audio player for Windows) - MPC-BE (video player for Windows) So, a user should be able to modify the seek step length. To accomplish that, this patch introduces the config option `media.seekStepLength`. I changed `HTMLMediaElement`, because, in `videocontrols.xml`, unlike in other XBL files, - `Services` (for `Services.prefs.getIntPref()`), - `Cc` (for `Cc["@mozilla.org/preferences-service;1"]`) or - `Components`/`Components.classes` (for `Components.classes["@mozilla.org/preferences-service;1"]`) ...were not available. In accordance with the existing `HTMLMediaElement` API, the newly introduced property `mozSeekStepLength` has seconds as its unit. The `media.seekStepLength` config option is in milliseconds, because floats are converted from strings and milliseconds are already a popular unit in Firefox's preferences. Should I add `, 15000` like in the following code, in case the config option was deleted? ```c++ double HTMLMediaElement::MozSeekStepLength() const { return Preferences::GetInt("media.seekStepLength", 15000) /*ms*/ / 1000.0; } ``` MozReview-Commit-ID: 97wsTqRC26M
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/webidl/HTMLMediaElement.webidl
modules/libpref/init/all.js
toolkit/content/widgets/videocontrols.xml
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3092,16 +3092,22 @@ HTMLMediaElement::SetMuted(bool aMuted)
 
   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
 
   // We allow inaudible autoplay. But changing our mute status may make this
   // media audible. So pause if we are no longer supposed to be autoplaying.
   PauseIfShouldNotBePlaying();
 }
 
+double
+HTMLMediaElement::MozSeekStepLength() const
+{
+  return Preferences::GetInt("media.seekStepLength") /*ms*/ / 1000.0;
+}
+
 class HTMLMediaElement::StreamCaptureTrackSource
   : public MediaStreamTrackSource
   , public MediaStreamTrackSource::Sink
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(StreamCaptureTrackSource,
                                            MediaStreamTrackSource)
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -637,16 +637,18 @@ public:
     return mIsCasting;
   }
 
   void SetMozIsCasting(bool aShow)
   {
     mIsCasting = aShow;
   }
 
+  double MozSeekStepLength() const;
+
   already_AddRefed<MediaSource> GetMozMediaSourceObject() const;
   // Returns a string describing the state of the media player internal
   // data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   // Returns a promise which will be resolved after collecting debugging
   // data from decoder/reader/MDSM. Used for debugging purposes.
   already_AddRefed<Promise> MozRequestDebugInfo(ErrorResult& aRv);
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -115,18 +115,21 @@ partial interface HTMLMediaElement {
   [Pref="media.test.dumpDebugInfo"]
   Promise<void> mozDumpDebugInfo();
 
   attribute MediaStream? srcObject;
 
   attribute boolean mozPreservesPitch;
 
   // NB: for internal use with the video controls:
-  [Func="IsChromeOrXBL"] attribute boolean mozAllowCasting;
-  [Func="IsChromeOrXBL"] attribute boolean mozIsCasting;
+  [Func="IsChromeOrXBL"]
+  attribute boolean mozAllowCasting;
+  [Func="IsChromeOrXBL"]
+  attribute boolean mozIsCasting;
+  readonly attribute double mozSeekStepLength;
 
   // Mozilla extension: stream capture
   [Throws]
   MediaStream mozCaptureStream();
   [Throws]
   MediaStream mozCaptureStreamUntilEnded();
   readonly attribute boolean mozAudioCaptured;
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5718,16 +5718,20 @@ pref("layout.css.color-adjust.enabled", 
 pref("dom.audiochannel.audioCompeting", false);
 pref("dom.audiochannel.audioCompeting.allAgents", false);
 
 // Default media volume
 pref("media.default_volume", "1.0");
 
 pref("media.seekToNextFrame.enabled", true);
 
+// The amount of milliseconds to seek backward or forward when using a fixed
+// (non-relative) seek step.
+pref("media.seekStepLength", 15000);
+
 // return the maximum number of cores that navigator.hardwareCurrency returns
 pref("dom.maxHardwareConcurrency", 16);
 
 // Shutdown the osfile worker if its no longer needed.
 #if !defined(RELEASE_OR_BETA)
 pref("osfile.reset_worker_delay", 30000);
 #endif
 
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1433,17 +1433,17 @@
       },
 
       keyHandler(event) {
         // Ignore keys when content might be providing its own.
         if (!this.video.hasAttribute("controls")) {
           return;
         }
 
-        var keystroke = "";
+        let keystroke = "";
         if (event.altKey) {
           keystroke += "alt-";
         }
         if (event.shiftKey) {
           keystroke += "shift-";
         }
         if (navigator.platform.startsWith("Mac")) {
           if (event.metaKey) {
@@ -1481,17 +1481,17 @@
             break;
         }
 
         if (String.fromCharCode(event.charCode) == " ") {
           keystroke += "space";
         }
 
         this.log("Got keystroke: " + keystroke);
-        var oldval, newval;
+        let oldval, newval;
 
         try {
           switch (keystroke) {
             case "space": /* Play */
               let target = event.originalTarget;
               if (target.localName === "button" && !target.disabled) {
                 break;
               }
@@ -1509,43 +1509,37 @@
               this.video.muted = false;
               break;
             case "accel-downArrow": /* Mute */
               this.video.muted = true;
               break;
             case "accel-upArrow": /* Unmute */
               this.video.muted = false;
               break;
-            case "leftArrow": /* Seek back 15 seconds */
-            case "accel-leftArrow": /* Seek back 10% */
-              oldval = this.video.currentTime;
-              if (keystroke == "leftArrow") {
-                newval = oldval - 15;
-              } else {
-                newval = oldval - (this.video.duration || this.maxCurrentTimeSeen / 1000) / 10;
-              }
-              this.video.currentTime = (newval >= 0 ? newval : 0);
+            case "leftArrow":          /* Seek backward...             */
+            case "rightArrow":         /* ...or forward a fixed amount */
+            case "accel-leftArrow":    /* Seek backward...             */
+            case "accel-rightArrow": { /* ...or forward 10%            */
+              let maxTime = this.video.duration /*s*/ ||
+                            this.maxCurrentTimeSeen /*ms*/ / 1000;
+              let seekStepLength = keystroke.startsWith("accel-") ?
+                  maxTime * 0.10
+                : this.video.mozSeekStepLength /*s*/;
+              this.video.currentTime = keystroke.endsWith("leftArrow") ?
+                  Math.max(this.video.currentTime - seekStepLength, 0)       // <-
+                : Math.min(this.video.currentTime + seekStepLength, maxTime) // ->
               break;
-            case "rightArrow": /* Seek forward 15 seconds */
-            case "accel-rightArrow": /* Seek forward 10% */
-              oldval = this.video.currentTime;
-              var maxtime = (this.video.duration || this.maxCurrentTimeSeen / 1000);
-              if (keystroke == "rightArrow") {
-                newval = oldval + 15;
-              } else {
-                newval = oldval + maxtime / 10;
-              }
-              this.video.currentTime = (newval <= maxtime ? newval : maxtime);
-              break;
+            }
             case "home": /* Seek to beginning */
               this.video.currentTime = 0;
               break;
             case "end": /* Seek to end */
-              if (this.video.currentTime != this.video.duration) {
-                this.video.currentTime = (this.video.duration || this.maxCurrentTimeSeen / 1000);
+              if (this.video.currentTime /*s*/ != this.video.duration /*s*/) {
+                this.video.currentTime = this.video.duration /*s*/ ||
+                                         this.maxCurrentTimeSeen /*ms*/ / 1000;
               }
               break;
             default:
               return;
           }
         } catch (e) { /* ignore any exception from setting .currentTime */ }
 
         event.preventDefault(); // Prevent page scrolling