diff --git a/src/i18n/en-US.properties b/src/i18n/en-US.properties index 57a54c9d7..1b61941f6 100644 --- a/src/i18n/en-US.properties +++ b/src/i18n/en-US.properties @@ -60,6 +60,12 @@ error_try_again_later=We're sorry the preview didn't load. Please try again late error_bad_file=We're sorry the preivew didn't load. This file may be corrupted. # Media Preview +# Label for autoplay in media player +media_autoplay=Autoplay +# Label for disabled autoplay in media player +media_autoplay_disabled=Disabled +# Label for enabled autoplay in media player +media_autoplay_enabled=Enabled # Label for changing speed in media player media_speed=Speed # Label for normal speed in media player diff --git a/src/lib/viewers/media/DashViewer.js b/src/lib/viewers/media/DashViewer.js index 236a6d46f..ce513b563 100644 --- a/src/lib/viewers/media/DashViewer.js +++ b/src/lib/viewers/media/DashViewer.js @@ -411,6 +411,7 @@ class DashViewer extends VideoBaseViewer { return; } + this.checkAutoplay(); this.calculateVideoDimensions(); this.loadUI(); this.loadFilmStrip(); diff --git a/src/lib/viewers/media/MediaBaseViewer.js b/src/lib/viewers/media/MediaBaseViewer.js index 7a4646749..2a67479a7 100644 --- a/src/lib/viewers/media/MediaBaseViewer.js +++ b/src/lib/viewers/media/MediaBaseViewer.js @@ -10,6 +10,7 @@ const CSS_CLASS_MEDIA = 'bp-media'; const CSS_CLASS_MEDIA_CONTAINER = 'bp-media-container'; const DEFAULT_VOLUME = 1; const MEDIA_VOLUME_CACHE_KEY = 'media-volume'; +const MEDIA_AUTOPLAY_CACHE_KEY = 'media-autoplay'; const MEDIA_VOLUME_INCREMENT = 0.05; const EMIT_WAIT_TIME_IN_MILLIS = 100; @@ -28,7 +29,7 @@ class MediaBaseViewer extends BaseViewer { this.wrapperEl = this.containerEl.appendChild(document.createElement('div')); this.wrapperEl.className = CSS_CLASS_MEDIA; - // Media Wrapper + // Media Container this.mediaContainerEl = this.wrapperEl.appendChild(document.createElement('div')); this.mediaContainerEl.setAttribute('tabindex', '-1'); this.mediaContainerEl.className = CSS_CLASS_MEDIA_CONTAINER; @@ -98,7 +99,7 @@ class MediaBaseViewer extends BaseViewer { if (Browser.isIOS()) { // iOS doesn't fire loadeddata event until some data loads - // Adding autoplay helps with that and itself won't autoplay. + // Adding autoplay prevents this but won't actually autoplay the video. // https://webkit.org/blog/6784/new-video-policies-for-ios/ this.mediaEl.autoplay = true; } @@ -107,6 +108,7 @@ class MediaBaseViewer extends BaseViewer { .getPromise() .then(() => { this.mediaEl.src = this.mediaUrl; + this.checkAutoplay(); }) .catch(this.handleAssetError); } @@ -211,6 +213,50 @@ class MediaBaseViewer extends BaseViewer { } } + /** + * Handler for autoplay + * + * @private + * @emits autoplay + * @return {void} + */ + handleAutoplay() { + const isAutoplayEnabled = this.cache.get(MEDIA_AUTOPLAY_CACHE_KEY) === 'Enabled'; + this.emit('autoplay', isAutoplayEnabled); + } + + /** + * Determines if media should autoplay based on cached settings value. + * + * @private + * @emits volume + * @return {void} + */ + checkAutoplay() { + if (this.cache.get(MEDIA_AUTOPLAY_CACHE_KEY) !== 'Enabled') { + return; + } + + // Play may return a promise depening on browser support. This promise + // will resolve when playback starts. If it fails, pause UI should be shown. + // https://webkit.org/blog/7734/auto-play-policy-changes-for-macos/ + const autoPlayPromise = this.mediaEl.play(); + + if (autoPlayPromise && typeof autoPlayPromise.then === 'function') { + autoPlayPromise + .then(() => { + this.handleRate(); + this.handleVolume(); + }) + .catch(() => { + this.pause(); + }); + } else { + // Fallback to traditional autoplay tag if play does not return a promise + this.mediaEl.autoplay = true; + } + } + /** * Resize handler * @@ -262,6 +308,7 @@ class MediaBaseViewer extends BaseViewer { this.mediaControls.addListener('toggleplayback', this.togglePlay); this.mediaControls.addListener('togglemute', this.toggleMute); this.mediaControls.addListener('ratechange', this.handleRate); + this.mediaControls.addListener('autoplaychange', this.handleAutoplay); } /** @@ -321,6 +368,7 @@ class MediaBaseViewer extends BaseViewer { this.hideLoadingIcon(); this.handleRate(); this.handleVolume(); + this.emit('play'); } /** @@ -435,7 +483,6 @@ class MediaBaseViewer extends BaseViewer { } if (arguments.length === 0 || hasValidStart) { this.mediaEl.play(); - this.emit('play'); this.handleRate(); this.handleVolume(); } diff --git a/src/lib/viewers/media/MediaControls.js b/src/lib/viewers/media/MediaControls.js index ff6e21a10..04176b452 100644 --- a/src/lib/viewers/media/MediaControls.js +++ b/src/lib/viewers/media/MediaControls.js @@ -102,6 +102,7 @@ class MediaControls extends EventEmitter { this.settings.removeListener('quality', this.handleQuality); this.settings.removeListener('speed', this.handleRate); this.settings.removeListener('subtitles', this.handleSubtitle); + this.settings.removeListener('autoplay', this.handleAutoplay); this.settings.destroy(); this.settings = undefined; } @@ -189,6 +190,16 @@ class MediaControls extends EventEmitter { this.emit('audiochange'); } + /** + * Autoplay handler + * + * @private + * @return {void} + */ + handleAutoplay() { + this.emit('autoplaychange'); + } + /** * Attaches settings menu * @@ -199,6 +210,7 @@ class MediaControls extends EventEmitter { this.settings = new Settings(this.containerEl, this.cache); this.settings.addListener('quality', this.handleQuality); this.settings.addListener('speed', this.handleRate); + this.settings.addListener('autoplay', this.handleAutoplay); } /** diff --git a/src/lib/viewers/media/README.md b/src/lib/viewers/media/README.md index 309297134..71f042b2f 100755 --- a/src/lib/viewers/media/README.md +++ b/src/lib/viewers/media/README.md @@ -46,6 +46,7 @@ The DASH viewer fires the following events | switchhistory | Gives quality switching history when the preview is destroyed | {array} quality switch objects | | adaptation | Quality adapts to a change in bandwidth | {number} bandwidth | | play | The video plays || +| autoplay | the autoplay option has been enabled/disabled | {boolean} new autoplay value| | pause | The video pauses || | seeked | The video skips to a time | {number} time | @@ -103,6 +104,7 @@ The MP3 viewer fires the following events | ratechange | Media speed changes | {string} playback speed | | volumechange | The media volume changes | {number} volume between 0 and 1| | play | The audio plays || +| autoplay | the autoplay option has been enabled/disabled | {boolean} new autoplay value| | pause | The audio pauses || | seeked | The audio skips to a time | {number} time | @@ -163,6 +165,7 @@ The MP4 viewer fires the following events | ratechange | Media speed changes | {string} playback speed | | volumechange | The media volume changes | {number} volume between 0 and 1 | | play | The video plays || +| autoplay | the autoplay option has been enabled/disabled | {boolean} new autoplay value| | pause | The video pauses || | seeked | The video skips to a time | {number} time | diff --git a/src/lib/viewers/media/Settings.js b/src/lib/viewers/media/Settings.js index 8a2beaf7b..a0f7dccfb 100644 --- a/src/lib/viewers/media/Settings.js +++ b/src/lib/viewers/media/Settings.js @@ -3,14 +3,17 @@ import EventEmitter from 'events'; import { addActivationListener, removeActivationListener, decodeKeydown, insertTemplate } from '../../util'; import { ICON_ARROW_LEFT, ICON_ARROW_RIGHT, ICON_CHECK_MARK } from '../../icons/icons'; import { CLASS_ELEM_KEYBOARD_FOCUS } from '../../constants'; +import Browser from '../../Browser'; const TYPE_SPEED = 'speed'; const TYPE_QUALITY = 'quality'; +const TYPE_AUTOPLAY = 'autoplay'; const CLASS_SETTINGS = 'bp-media-settings'; const CLASS_SETTINGS_SELECTED = 'bp-media-settings-selected'; const CLASS_SETTINGS_OPEN = 'bp-media-settings-is-open'; const CLASS_SETTINGS_SUBTITLES_UNAVAILABLE = 'bp-media-settings-subtitles-unavailable'; const CLASS_SETTINGS_AUDIOTRACKS_UNAVAILABLE = 'bp-media-settings-audiotracks-unavailable'; +const CLASS_SETTINGS_AUTOPLAY_UNAVAILABLE = 'bp-media-settings-autoplay-unavailable'; const CLASS_SETTINGS_SUBTITLES_ON = 'bp-media-settings-subtitles-on'; const SELECTOR_SETTINGS_SUB_ITEM = '.bp-media-settings-sub-item'; const SELECTOR_SETTINGS_VALUE = '.bp-media-settings-value'; @@ -18,6 +21,11 @@ const MEDIA_SPEEDS = ['0.5', '1.0', '1.25', '1.5', '2.0']; const SETTINGS_TEMPLATE = `