Commit 441eb369 by Valera Rozuvan

Merge pull request #869 from edx/valera/video_js_console_log_useful_debug_info

Adding debugging messages for Video player.
parents bd4b4c16 73a9c43e
/** /**
* @file Initialize module works with the JSON config, and sets up various settings, parameters, * @file Initialize module works with the JSON config, and sets up various
* variables. After all setup actions are performed, it invokes the video player to play the * settings, parameters, variables. After all setup actions are performed, it
* specified video. This module must be invoked first. It provides several functions which do not * invokes the video player to play the specified video. This module must be
* fit in with other modules. * invoked first. It provides several functions which do not fit in with other
* modules.
* *
* @external VideoPlayer * @external VideoPlayer
* *
...@@ -16,8 +17,17 @@ define( ...@@ -16,8 +17,17 @@ define(
['video/03_video_player.js'], ['video/03_video_player.js'],
function (VideoPlayer) { function (VideoPlayer) {
if (typeof(window.gettext) == "undefined") { // window.console.log() is expected to be available. We do not support
window.gettext = function(s){return s;}; // browsers which lack this functionality.
// The function gettext() is defined by a vendor library. If, however, it
// is undefined, it is a simple wrapper. It is used to return a different
// version of the string passed (translated string, etc.). In the basic
// case, the original string is returned.
if (typeof(window.gettext) == 'undefined') {
window.gettext = function (s) {
return s;
};
} }
/** /**
...@@ -50,43 +60,54 @@ function (VideoPlayer) { ...@@ -50,43 +60,54 @@ function (VideoPlayer) {
* methods, modules) of the Video player. * methods, modules) of the Video player.
*/ */
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
state.setSpeed = _.bind(setSpeed, state); state.setSpeed = _.bind(setSpeed, state);
state.youtubeId = _.bind(youtubeId, state); state.youtubeId = _.bind(youtubeId, state);
state.getDuration = _.bind(getDuration, state); state.getDuration = _.bind(getDuration, state);
state.trigger = _.bind(trigger, state); state.trigger = _.bind(trigger, state);
state.stopBuffering = _.bind(stopBuffering, state); state.stopBuffering = _.bind(stopBuffering, state);
// Old private functions. Now also public so that can be // Old private functions. Now also public so that can be
// tested by Jasmine. // tested by Jasmine.
state.initialize = _.bind(initialize, state); state.initialize = _.bind(initialize, state);
state.parseSpeed = _.bind(parseSpeed, state); state.parseSpeed = _.bind(parseSpeed, state);
state.fetchMetadata = _.bind(fetchMetadata, state); state.fetchMetadata = _.bind(fetchMetadata, state);
state.parseYoutubeStreams = _.bind(parseYoutubeStreams, state); state.parseYoutubeStreams = _.bind(parseYoutubeStreams, state);
state.parseVideoSources = _.bind(parseVideoSources, state); state.parseVideoSources = _.bind(parseVideoSources, state);
state.getVideoMetadata = _.bind(getVideoMetadata, state); state.getVideoMetadata = _.bind(getVideoMetadata, state);
} }
// function _renderElements(state) // function _renderElements(state)
// //
// Create any necessary DOM elements, attach them, and set their initial configuration. Also // Create any necessary DOM elements, attach them, and set their
// make the created DOM elements available via the 'state' object. Much easier to work this // initial configuration. Also make the created DOM elements available
// way - you don't have to do repeated jQuery element selects. // via the 'state' object. Much easier to work this way - you don't
// have to do repeated jQuery element selects.
function _renderElements(state) { function _renderElements(state) {
// Launch embedding of actual video content, or set it up so that it will be done as soon as the // Launch embedding of actual video content, or set it up so that it
// appropriate video player (YouTube or stand alone HTML5) is loaded, and can handle embedding. // will be done as soon as the appropriate video player (YouTube or
// stand-alone HTML5) is loaded, and can handle embedding.
// //
// Note that the loading of stand alone HTML5 player API is handled by Require JS. At the time // Note that the loading of stand alone HTML5 player API is handled by
// when we reach this code, the stand alone HTML5 player is already loaded, so no further testing // Require JS. At the time when we reach this code, the stand alone
// in that case is required. // HTML5 player is already loaded, so no further testing in that case
// is required.
var onPlayerReadyFunc; var onPlayerReadyFunc;
if ( if (
((state.videoType === 'youtube') && (window.YT) && (window.YT.Player)) || (
(state.videoType === 'youtube') &&
(window.YT) &&
(window.YT.Player)
) ||
(state.videoType === 'html5') (state.videoType === 'html5')
) { ) {
VideoPlayer(state); VideoPlayer(state);
} else { } else {
onPlayerReadyFunc = (state.videoType === 'youtube') ? 'onYouTubePlayerAPIReady' : 'onHTML5PlayerAPIReady'; if (state.videoType === 'youtube') {
onPlayerReadyFunc = 'onYouTubePlayerAPIReady';
} else {
onPlayerReadyFunc = 'onHTML5PlayerAPIReady';
}
window[onPlayerReadyFunc] = _.bind(VideoPlayer, window, state); window[onPlayerReadyFunc] = _.bind(VideoPlayer, window, state);
} }
} }
...@@ -102,7 +123,8 @@ function (VideoPlayer) { ...@@ -102,7 +123,8 @@ function (VideoPlayer) {
// Option // Option
// this.hide_captions = true | false // this.hide_captions = true | false
// //
// represents the user's choice of having the subtitles shown or hidden. This choice is stored in cookies. // represents the user's choice of having the subtitles shown or
// hidden. This choice is stored in cookies.
function _configureCaptions(state) { function _configureCaptions(state) {
if (state.config.show_captions) { if (state.config.show_captions) {
state.hide_captions = ($.cookie('hide_captions') === 'true'); state.hide_captions = ($.cookie('hide_captions') === 'true');
...@@ -119,14 +141,19 @@ function (VideoPlayer) { ...@@ -119,14 +141,19 @@ function (VideoPlayer) {
} }
// function _setPlayerMode(state) // function _setPlayerMode(state)
// By default we will be forcing HTML5 player mode. Only in the case when, after initializtion, we will // By default we will be forcing HTML5 player mode. Only in the case
// get one available playback rate, we will change to Flash player mode. There is a need to store this // when, after initializtion, we will get one available playback rate,
// setting in cookies because otherwise we will have to change from HTML5 to Flash on every page load // we will change to Flash player mode. There is a need to store this
// in a browser that doesn't fully support HTML5. When we have this setting in cookies, we can select // setting in cookies because otherwise we will have to change from
// HTML5 to Flash on every page load in a browser that doesn't fully
// support HTML5. When we have this setting in cookies, we can select
// the proper mode from the start (not having to change mode later on). // the proper mode from the start (not having to change mode later on).
function _setPlayerMode(state) { function _setPlayerMode(state) {
(function (currentPlayerMode) { (function (currentPlayerMode) {
if ((currentPlayerMode === 'html5') || (currentPlayerMode === 'flash')) { if (
(currentPlayerMode === 'html5') ||
(currentPlayerMode === 'flash')
) {
state.currentPlayerMode = currentPlayerMode; state.currentPlayerMode = currentPlayerMode;
} else { } else {
$.cookie('current_player_mode', 'html5', { $.cookie('current_player_mode', 'html5', {
...@@ -135,20 +162,32 @@ function (VideoPlayer) { ...@@ -135,20 +162,32 @@ function (VideoPlayer) {
}); });
state.currentPlayerMode = 'html5'; state.currentPlayerMode = 'html5';
} }
console.log(
'[Video info]: YouTube player mode is "' +
state.currentPlayerMode + '".'
);
}($.cookie('current_player_mode'))); }($.cookie('current_player_mode')));
} }
// function _parseYouTubeIDs(state) // function _parseYouTubeIDs(state)
// The function parse YouTube stream ID's. // The function parse YouTube stream ID's.
// @return // @return
// false: We don't have YouTube video IDs to work with; most likely we have HTML5 video sources. // false: We don't have YouTube video IDs to work with; most likely
// true: Parsing of YouTube video IDs went OK, and we can proceed onwards to play YouTube videos. // we have HTML5 video sources.
// true: Parsing of YouTube video IDs went OK, and we can proceed
// onwards to play YouTube videos.
function _parseYouTubeIDs(state) { function _parseYouTubeIDs(state) {
if (state.parseYoutubeStreams(state.config.youtubeStreams)) { if (state.parseYoutubeStreams(state.config.youtubeStreams)) {
state.videoType = 'youtube'; state.videoType = 'youtube';
return true; return true;
} }
console.log(
'[Video info]: Youtube Video IDs are incorrect or absent.'
);
return false; return false;
} }
...@@ -184,6 +223,10 @@ function (VideoPlayer) { ...@@ -184,6 +223,10 @@ function (VideoPlayer) {
state.el.find('.video-player div').addClass('hidden'); state.el.find('.video-player div').addClass('hidden');
state.el.find('.video-player h3').removeClass('hidden'); state.el.find('.video-player h3').removeClass('hidden');
console.log(
'[Video info]: Non-youtube video sources aren\'t available.'
);
return false; return false;
} }
...@@ -212,8 +255,9 @@ function (VideoPlayer) { ...@@ -212,8 +255,9 @@ function (VideoPlayer) {
// *************************************************************** // ***************************************************************
// Public functions start here. // Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object. // These are available via the 'state' object. Their context ('this'
// The magic private function that makes them available and sets up their context is makeFunctionsPublic(). // keyword) is the 'state' object. The magic private function that makes
// them available and sets up their context is makeFunctionsPublic().
// *************************************************************** // ***************************************************************
// function initialize(element) // function initialize(element)
...@@ -221,15 +265,21 @@ function (VideoPlayer) { ...@@ -221,15 +265,21 @@ function (VideoPlayer) {
function initialize(element) { function initialize(element) {
var _this = this, tempYtTestTimeout; var _this = this, tempYtTestTimeout;
// This is used in places where we instead would have to check if an element has a CSS class 'fullscreen'. // This is used in places where we instead would have to check if an
// element has a CSS class 'fullscreen'.
this.isFullScreen = false; this.isFullScreen = false;
// The parent element of the video, and the ID. // The parent element of the video, and the ID.
this.el = $(element).find('.video'); this.el = $(element).find('.video');
this.id = this.el.attr('id').replace(/video_/, ''); this.id = this.el.attr('id').replace(/video_/, '');
// We store all settings passed to us by the server in one place. These are "read only", so don't console.log(
// modify them. All variable content lives in 'state' object. '[Video info]: Initializing video with id "' + this.id + '".'
);
// We store all settings passed to us by the server in one place. These
// are "read only", so don't modify them. All variable content lives in
// 'state' object.
this.config = { this.config = {
element: element, element: element,
...@@ -238,7 +288,10 @@ function (VideoPlayer) { ...@@ -238,7 +288,10 @@ function (VideoPlayer) {
caption_data_dir: this.el.data('caption-data-dir'), caption_data_dir: this.el.data('caption-data-dir'),
caption_asset_path: this.el.data('caption-asset-path'), caption_asset_path: this.el.data('caption-asset-path'),
show_captions: (this.el.data('show-captions').toString().toLowerCase() === 'true'), show_captions: (
this.el.data('show-captions')
.toString().toLowerCase() === 'true'
),
youtubeStreams: this.el.data('streams'), youtubeStreams: this.el.data('streams'),
sub: this.el.data('sub'), sub: this.el.data('sub'),
...@@ -262,12 +315,16 @@ function (VideoPlayer) { ...@@ -262,12 +315,16 @@ function (VideoPlayer) {
this.config.ytTestTimeout = tempYtTestTimeout; this.config.ytTestTimeout = tempYtTestTimeout;
if (!(_parseYouTubeIDs(this))) { if (!(_parseYouTubeIDs(this))) {
// If we do not have YouTube ID's, try parsing HTML5 video sources. // If we do not have YouTube ID's, try parsing HTML5 video sources.
if (!_prepareHTML5Video(this, true)) { if (!_prepareHTML5Video(this, true)) {
// Non-YouTube sources were not found either. // Non-YouTube sources were not found either.
return; return;
} }
console.log('[Video info]: Start player in HTML5 mode.');
_setConfigurations(this); _setConfigurations(this);
_renderElements(this); _renderElements(this);
} else { } else {
...@@ -276,29 +333,52 @@ function (VideoPlayer) { ...@@ -276,29 +333,52 @@ function (VideoPlayer) {
} }
this.youtubeXhr this.youtubeXhr
.always(function(json, status) { .always(function (json, status) {
var err = $.isPlainObject(json.error) || var err = $.isPlainObject(json.error) ||
(status !== 'success' && status !== 'notmodified'); (
status !== 'success' &&
status !== 'notmodified'
);
if (err) { if (err) {
console.log(
'[Video info]: YouTube returned an error for ' +
'video with id "' + _this.id + '".'
);
// When the youtube link doesn't work for any reason // When the youtube link doesn't work for any reason
// (for example, the great firewall in china) any // (for example, the great firewall in china) any
// alternate sources should automatically play. // alternate sources should automatically play.
if (!_prepareHTML5Video(_this)) { if (!_prepareHTML5Video(_this)) {
console.log(
'[Video info]: Continue loading ' +
'YouTube video.'
);
// Non-YouTube sources were not found either. // Non-YouTube sources were not found either.
_this.el.find('.video-player div').removeClass('hidden'); _this.el.find('.video-player div')
_this.el.find('.video-player h3').addClass('hidden'); .removeClass('hidden');
_this.el.find('.video-player h3')
.addClass('hidden');
// If in reality the timeout was to short, try to // If in reality the timeout was to short, try to
// continue loading the YouTube video anyways. // continue loading the YouTube video anyways.
_this.fetchMetadata(); _this.fetchMetadata();
_this.parseSpeed(); _this.parseSpeed();
} else { } else {
console.log(
'[Video info]: Change player mode to HTML5.'
);
// In-browser HTML5 player does not support quality // In-browser HTML5 player does not support quality
// control. // control.
_this.el.find('a.quality_control').hide(); _this.el.find('a.quality_control').hide();
} }
} else { } else {
console.log(
'[Video info]: Start player in YouTube mode.'
);
_this.fetchMetadata(); _this.fetchMetadata();
_this.parseSpeed(); _this.parseSpeed();
} }
...@@ -313,23 +393,28 @@ function (VideoPlayer) { ...@@ -313,23 +393,28 @@ function (VideoPlayer) {
// //
// Take a string in the form: // Take a string in the form:
// "iCawTYPtehk:0.75,KgpclqP-LBA:1.0,9-2670d5nvU:1.5" // "iCawTYPtehk:0.75,KgpclqP-LBA:1.0,9-2670d5nvU:1.5"
// parse it, and make it available via the 'state' object. If we are not given a string, or // parse it, and make it available via the 'state' object. If we are
// it's length is zero, then we return false. // not given a string, or it's length is zero, then we return false.
// //
// @return // @return
// false: We don't have YouTube video IDs to work with; most likely we have HTML5 video sources. // false: We don't have YouTube video IDs to work with; most likely
// true: Parsing of YouTube video IDs went OK, and we can proceed onwards to play YouTube videos. // we have HTML5 video sources.
// true: Parsing of YouTube video IDs went OK, and we can proceed
// onwards to play YouTube videos.
function parseYoutubeStreams(youtubeStreams) { function parseYoutubeStreams(youtubeStreams) {
var _this; var _this;
if (typeof youtubeStreams === 'undefined' || youtubeStreams.length === 0) { if (
typeof youtubeStreams === 'undefined' ||
youtubeStreams.length === 0
) {
return false; return false;
} }
_this = this; _this = this;
this.videos = {}; this.videos = {};
$.each(youtubeStreams.split(/,/), function(index, video) { $.each(youtubeStreams.split(/,/), function (index, video) {
var speed; var speed;
video = video.split(/:/); video = video.split(/:/);
...@@ -343,8 +428,8 @@ function (VideoPlayer) { ...@@ -343,8 +428,8 @@ function (VideoPlayer) {
// function parseVideoSources(, mp4Source, webmSource, oggSource) // function parseVideoSources(, mp4Source, webmSource, oggSource)
// //
// Take the HTML5 sources (URLs of videos), and make them available explictly for each type // Take the HTML5 sources (URLs of videos), and make them available
// of video format (mp4, webm, ogg). // explictly for each type of video format (mp4, webm, ogg).
function parseVideoSources(sources) { function parseVideoSources(sources) {
var _this = this, var _this = this,
v = document.createElement('video'), v = document.createElement('video'),
...@@ -376,16 +461,17 @@ function (VideoPlayer) { ...@@ -376,16 +461,17 @@ function (VideoPlayer) {
// function fetchMetadata() // function fetchMetadata()
// //
// When dealing with YouTube videos, we must fetch meta data that has certain key facts // When dealing with YouTube videos, we must fetch meta data that has
// not available while the video is loading. For example the length of the video can be // certain key facts not available while the video is loading. For
// determined from the meta data. // example the length of the video can be determined from the meta
// data.
function fetchMetadata() { function fetchMetadata() {
var _this = this; var _this = this;
this.metadata = {}; this.metadata = {};
$.each(this.videos, function (speed, url) { $.each(this.videos, function (speed, url) {
_this.getVideoMetadata(url, function(data) { _this.getVideoMetadata(url, function (data) {
if (data.data) { if (data.data) {
_this.metadata[data.data.id] = data.data; _this.metadata[data.data.id] = data.data;
} }
...@@ -397,7 +483,7 @@ function (VideoPlayer) { ...@@ -397,7 +483,7 @@ function (VideoPlayer) {
// //
// Create a separate array of available speeds. // Create a separate array of available speeds.
function parseSpeed() { function parseSpeed() {
this.speeds = ($.map(this.videos, function(url, speed) { this.speeds = ($.map(this.videos, function (url, speed) {
return speed; return speed;
})).sort(); })).sort();
...@@ -439,7 +525,7 @@ function (VideoPlayer) { ...@@ -439,7 +525,7 @@ function (VideoPlayer) {
function stopBuffering() { function stopBuffering() {
var video; var video;
if (this.videoType === 'html5'){ if (this.videoType === 'html5') {
// HTML5 player haven't default way to abort bufferization. // HTML5 player haven't default way to abort bufferization.
// In this case we simply resetting source and call load(). // In this case we simply resetting source and call load().
video = this.videoPlayer.player.video; video = this.videoPlayer.player.video;
...@@ -457,9 +543,9 @@ function (VideoPlayer) { ...@@ -457,9 +543,9 @@ function (VideoPlayer) {
} }
/* /*
* The trigger() function will assume that the @objChain is a complete chain with a method * The trigger() function will assume that the @objChain is a complete
* (function) at the end. It will call this function. So for example, when trigger() is * chain with a method (function) at the end. It will call this function.
* called like so: * So for example, when trigger() is called like so:
* *
* state.trigger('videoPlayer.pause', {'param1': 10}); * state.trigger('videoPlayer.pause', {'param1': 10});
* *
...@@ -474,10 +560,10 @@ function (VideoPlayer) { ...@@ -474,10 +560,10 @@ function (VideoPlayer) {
tmpObj = this; tmpObj = this;
chain = objChain.split('.'); chain = objChain.split('.');
// At the end of the loop the variable 'tmpObj' will either be the correct // At the end of the loop the variable 'tmpObj' will either be the
// object/function to trigger/invoke. If the 'chain' chain of object is // correct object/function to trigger/invoke. If the 'chain' chain of
// incorrect (one of the link is non-existent), then the loop will immediately // object is incorrect (one of the link is non-existent), then the loop
// exit. // will immediately exit.
while (chain.length) { while (chain.length) {
i = chain.shift(); i = chain.shift();
......
...@@ -21,35 +21,46 @@ function (HTML5Video) { ...@@ -21,35 +21,46 @@ function (HTML5Video) {
// function _makeFunctionsPublic(state) // function _makeFunctionsPublic(state)
// //
// Functions which will be accessible via 'state' object. When called, these functions will // Functions which will be accessible via 'state' object. When called,
// get the 'state' object as a context. // these functions will get the 'state' object as a context.
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
state.videoPlayer.pause = _.bind(pause, state); state.videoPlayer.pause = _.bind(pause, state);
state.videoPlayer.play = _.bind(play, state); state.videoPlayer.play = _.bind(play, state);
state.videoPlayer.update = _.bind(update, state); state.videoPlayer.update = _.bind(update, state);
state.videoPlayer.onSpeedChange = _.bind(onSpeedChange, state); state.videoPlayer.onSpeedChange = _.bind(onSpeedChange, state);
state.videoPlayer.onCaptionSeek = _.bind(onSeek, state); state.videoPlayer.onCaptionSeek = _.bind(onSeek, state);
state.videoPlayer.onSlideSeek = _.bind(onSeek, state); state.videoPlayer.onSlideSeek = _.bind(onSeek, state);
state.videoPlayer.onEnded = _.bind(onEnded, state); state.videoPlayer.onEnded = _.bind(onEnded, state);
state.videoPlayer.onPause = _.bind(onPause, state); state.videoPlayer.onPause = _.bind(onPause, state);
state.videoPlayer.onPlay = _.bind(onPlay, state); state.videoPlayer.onPlay = _.bind(onPlay, state);
state.videoPlayer.onUnstarted = _.bind(onUnstarted, state);
state.videoPlayer.handlePlaybackQualityChange = _.bind(handlePlaybackQualityChange, state); state.videoPlayer.onUnstarted = _.bind(
state.videoPlayer.onPlaybackQualityChange = _.bind(onPlaybackQualityChange, state); onUnstarted, state
state.videoPlayer.onStateChange = _.bind(onStateChange, state); );
state.videoPlayer.onReady = _.bind(onReady, state);
state.videoPlayer.updatePlayTime = _.bind(updatePlayTime, state); state.videoPlayer.handlePlaybackQualityChange = _.bind(
state.videoPlayer.isPlaying = _.bind(isPlaying, state); handlePlaybackQualityChange, state
state.videoPlayer.log = _.bind(log, state); );
state.videoPlayer.duration = _.bind(duration, state);
state.videoPlayer.onVolumeChange = _.bind(onVolumeChange, state); state.videoPlayer.onPlaybackQualityChange = _.bind(
onPlaybackQualityChange, state
);
state.videoPlayer.onStateChange = _.bind(onStateChange, state);
state.videoPlayer.onReady = _.bind(onReady, state);
state.videoPlayer.updatePlayTime = _.bind(updatePlayTime, state);
state.videoPlayer.isPlaying = _.bind(isPlaying, state);
state.videoPlayer.log = _.bind(log, state);
state.videoPlayer.duration = _.bind(duration, state);
state.videoPlayer.onVolumeChange = _.bind(onVolumeChange, state);
} }
// function _initialize(state) // function _initialize(state)
// //
// Create any necessary DOM elements, attach them, and set their initial configuration. Also // Create any necessary DOM elements, attach them, and set their
// make the created DOM elements available via the 'state' object. Much easier to work this // initial configuration. Also make the created DOM elements available
// way - you don't have to do repeated jQuery element selects. // via the 'state' object. Much easier to work this way - you don't
// have to do repeated jQuery element selects.
function _initialize(state) { function _initialize(state) {
var youTubeId; var youTubeId;
...@@ -92,9 +103,10 @@ function (HTML5Video) { ...@@ -92,9 +103,10 @@ function (HTML5Video) {
// //
// TODO: Check the status of // TODO: Check the status of
// http://code.google.com/p/gdata-issues/issues/detail?id=4654 // http://code.google.com/p/gdata-issues/issues/detail?id=4654
// When the YouTube team fixes the API bug, we can remove this temporary // When the YouTube team fixes the API bug, we can remove this
// bug fix. // temporary bug fix.
state.browserIsFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; state.browserIsFirefox = navigator.userAgent
.toLowerCase().indexOf('firefox') > -1;
if (state.videoType === 'html5') { if (state.videoType === 'html5') {
state.videoPlayer.player = new HTML5Video.Player(state.el, { state.videoPlayer.player = new HTML5Video.Player(state.el, {
...@@ -117,7 +129,8 @@ function (HTML5Video) { ...@@ -117,7 +129,8 @@ function (HTML5Video) {
events: { events: {
onReady: state.videoPlayer.onReady, onReady: state.videoPlayer.onReady,
onStateChange: state.videoPlayer.onStateChange, onStateChange: state.videoPlayer.onStateChange,
onPlaybackQualityChange: state.videoPlayer.onPlaybackQualityChange onPlaybackQualityChange: state.videoPlayer
.onPlaybackQualityChange
} }
}); });
} }
...@@ -125,9 +138,10 @@ function (HTML5Video) { ...@@ -125,9 +138,10 @@ function (HTML5Video) {
// function _restartUsingFlash(state) // function _restartUsingFlash(state)
// //
// When we are about to play a YouTube video in HTML5 mode and discover that we only // When we are about to play a YouTube video in HTML5 mode and discover
// have one available playback rate, we will switch to Flash mode. In Flash speed // that we only have one available playback rate, we will switch to
// switching is done by reloading videos recorded at different frame rates. // Flash mode. In Flash speed switching is done by reloading videos
// recorded at different frame rates.
function _restartUsingFlash(state) { function _restartUsingFlash(state) {
// Remove from the page current iFrame with HTML5 video. // Remove from the page current iFrame with HTML5 video.
state.videoPlayer.player.destroy(); state.videoPlayer.player.destroy();
...@@ -139,25 +153,29 @@ function (HTML5Video) { ...@@ -139,25 +153,29 @@ function (HTML5Video) {
}); });
state.currentPlayerMode = 'flash'; state.currentPlayerMode = 'flash';
console.log('[Video info]: Changing YouTube player mode to "flash".');
// Removed configuration option that requests the HTML5 mode. // Removed configuration option that requests the HTML5 mode.
delete state.videoPlayer.playerVars.html5; delete state.videoPlayer.playerVars.html5;
// Request for the creation of a new Flash player // Request for the creation of a new Flash player
state.videoPlayer.player = new YT.Player(state.id, { state.videoPlayer.player = new YT.Player(state.id, {
'playerVars': state.videoPlayer.playerVars, playerVars: state.videoPlayer.playerVars,
'videoId': state.youtubeId(), videoId: state.youtubeId(),
'events': { events: {
'onReady': state.videoPlayer.onReady, onReady: state.videoPlayer.onReady,
'onStateChange': state.videoPlayer.onStateChange, onStateChange: state.videoPlayer.onStateChange,
'onPlaybackQualityChange': state.videoPlayer.onPlaybackQualityChange onPlaybackQualityChange: state.videoPlayer
.onPlaybackQualityChange
} }
}); });
} }
// *************************************************************** // ***************************************************************
// Public functions start here. // Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object. // These are available via the 'state' object. Their context ('this'
// The magic private function that makes them available and sets up their context is makeFunctionsPublic(). // keyword) is the 'state' object. The magic private function that makes
// them available and sets up their context is makeFunctionsPublic().
// *************************************************************** // ***************************************************************
function pause() { function pause() {
...@@ -174,9 +192,11 @@ function (HTML5Video) { ...@@ -174,9 +192,11 @@ function (HTML5Video) {
// This function gets the video's current play position in time // This function gets the video's current play position in time
// (currentTime) and its duration. // (currentTime) and its duration.
// It is called at a regular interval when the video is playing (see below). // It is called at a regular interval when the video is playing (see
// below).
function update() { function update() {
this.videoPlayer.currentTime = this.videoPlayer.player.getCurrentTime(); this.videoPlayer.currentTime = this.videoPlayer.player
.getCurrentTime();
if (isFinite(this.videoPlayer.currentTime)) { if (isFinite(this.videoPlayer.currentTime)) {
this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime); this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime);
...@@ -206,19 +226,27 @@ function (HTML5Video) { ...@@ -206,19 +226,27 @@ function (HTML5Video) {
if ( if (
this.currentPlayerMode === 'html5' && this.currentPlayerMode === 'html5' &&
!(this.browserIsFirefox && newSpeed === '1.0' && this.videoType === 'youtube') !(
this.browserIsFirefox &&
newSpeed === '1.0' &&
this.videoType === 'youtube'
)
) { ) {
this.videoPlayer.player.setPlaybackRate(newSpeed); this.videoPlayer.player.setPlaybackRate(newSpeed);
} else { } else {
// We request the reloading of the video in the case when YouTube is in // We request the reloading of the video in the case when YouTube
// Flash player mode, or when we are in Firefox, and the new speed is 1.0. // is in Flash player mode, or when we are in Firefox, and the new
// The second case is necessary to avoid the bug where in Firefox speed // speed is 1.0. The second case is necessary to avoid the bug
// switching to 1.0 in HTML5 player mode is handled incorrectly by YouTube // where in Firefox speed switching to 1.0 in HTML5 player mode is
// API. // handled incorrectly by YouTube API.
if (this.videoPlayer.isPlaying()) { if (this.videoPlayer.isPlaying()) {
this.videoPlayer.player.loadVideoById(this.youtubeId(), this.videoPlayer.currentTime); this.videoPlayer.player.loadVideoById(
this.youtubeId(), this.videoPlayer.currentTime
);
} else { } else {
this.videoPlayer.player.cueVideoById(this.youtubeId(), this.videoPlayer.currentTime); this.videoPlayer.player.cueVideoById(
this.youtubeId(), this.videoPlayer.currentTime
);
} }
this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime); this.videoPlayer.updatePlayTime(this.videoPlayer.currentTime);
...@@ -243,7 +271,9 @@ function (HTML5Video) { ...@@ -243,7 +271,9 @@ function (HTML5Video) {
if (this.videoPlayer.isPlaying()) { if (this.videoPlayer.isPlaying()) {
clearInterval(this.videoPlayer.updateInterval); clearInterval(this.videoPlayer.updateInterval);
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200); this.videoPlayer.updateInterval = setInterval(
this.videoPlayer.update, 200
);
} else { } else {
this.videoPlayer.currentTime = params.time; this.videoPlayer.currentTime = params.time;
} }
...@@ -286,7 +316,9 @@ function (HTML5Video) { ...@@ -286,7 +316,9 @@ function (HTML5Video) {
); );
if (!this.videoPlayer.updateInterval) { if (!this.videoPlayer.updateInterval) {
this.videoPlayer.updateInterval = setInterval(this.videoPlayer.update, 200); this.videoPlayer.updateInterval = setInterval(
this.videoPlayer.update, 200
);
} }
this.trigger('videoControl.play', null); this.trigger('videoControl.play', null);
...@@ -325,19 +357,26 @@ function (HTML5Video) { ...@@ -325,19 +357,26 @@ function (HTML5Video) {
// https://bugzilla.mozilla.org/show_bug.cgi?id=840745 // https://bugzilla.mozilla.org/show_bug.cgi?id=840745
// https://developer.mozilla.org/en-US/docs/DOM/HTMLMediaElement // https://developer.mozilla.org/en-US/docs/DOM/HTMLMediaElement
availablePlaybackRates = _.filter(availablePlaybackRates, function(item){ availablePlaybackRates = _.filter(
var speed = Number(item); availablePlaybackRates,
return speed > 0.25 && speed <= 5; function (item) {
}); var speed = Number(item);
return speed > 0.25 && speed <= 5;
}
);
if ((this.currentPlayerMode === 'html5') && (this.videoType === 'youtube')) { if (
this.currentPlayerMode === 'html5' &&
this.videoType === 'youtube'
) {
if (availablePlaybackRates.length === 1) { if (availablePlaybackRates.length === 1) {
// This condition is needed in cases when Firefox version is less than 20. In those versions // This condition is needed in cases when Firefox version is
// HTML5 playback could only happen at 1 speed (no speed changing). Therefore, in this case, // less than 20. In those versions HTML5 playback could only
// we need to switch back to Flash. // happen at 1 speed (no speed changing). Therefore, in this
// case, we need to switch back to Flash.
// //
// This might also happen in other browsers, therefore when we have 1 speed available, we fall // This might also happen in other browsers, therefore when we
// back to Flash. // have 1 speed available, we fall back to Flash.
_restartUsingFlash(this); _restartUsingFlash(this);
...@@ -352,18 +391,26 @@ function (HTML5Video) { ...@@ -352,18 +391,26 @@ function (HTML5Video) {
// and their associated subs. // and their associated subs.
// First clear the dictionary. // First clear the dictionary.
$.each(this.videos, function(index, value) { $.each(this.videos, function (index, value) {
delete _this.videos[index]; delete _this.videos[index];
}); });
this.speeds = []; this.speeds = [];
// Recreate it with the supplied frame rates. // Recreate it with the supplied frame rates.
$.each(availablePlaybackRates, function(index, value) { $.each(availablePlaybackRates, function (index, value) {
_this.videos[value.toFixed(2).replace(/\.00$/, '.0')] = baseSpeedSubs; var key = value.toFixed(2).replace(/\.00$/, '.0');
_this.videos[key] = baseSpeedSubs;
_this.speeds.push(value.toFixed(2).replace(/\.00$/, '.0')); _this.speeds.push(key);
}); });
this.trigger('videoSpeedControl.reRender', {'newSpeeds': this.speeds, 'currentSpeed': this.speed}); this.trigger(
'videoSpeedControl.reRender',
{
newSpeeds: this.speeds,
currentSpeed: this.speed
}
);
this.setSpeed($.cookie('video_speed')); this.setSpeed($.cookie('video_speed'));
} }
...@@ -373,7 +420,10 @@ function (HTML5Video) { ...@@ -373,7 +420,10 @@ function (HTML5Video) {
this.videoPlayer.player.setPlaybackRate(this.speed); this.videoPlayer.player.setPlaybackRate(this.speed);
} }
if (!onTouchBasedDevice() && $('.video:first').data('autoplay') === 'True') { if (
!onTouchBasedDevice() &&
$('.video:first').data('autoplay') === 'True'
) {
this.videoPlayer.play(); this.videoPlayer.play();
} }
} }
...@@ -400,19 +450,37 @@ function (HTML5Video) { ...@@ -400,19 +450,37 @@ function (HTML5Video) {
duration = this.videoPlayer.duration(); duration = this.videoPlayer.duration();
this.trigger('videoProgressSlider.updatePlayTime', {'time': time, 'duration': duration}); this.trigger(
this.trigger('videoControl.updateVcrVidTime', {'time': time, 'duration': duration}); 'videoProgressSlider.updatePlayTime',
{
time: time,
duration: duration
}
);
this.trigger(
'videoControl.updateVcrVidTime',
{
time: time,
duration: duration
}
);
this.trigger('videoCaption.updatePlayTime', time); this.trigger('videoCaption.updatePlayTime', time);
} }
function isPlaying() { function isPlaying() {
return this.videoPlayer.player.getPlayerState() === this.videoPlayer.PlayerState.PLAYING; var playerState = this.videoPlayer.player.getPlayerState(),
PLAYING = this.videoPlayer.PlayerState.PLAYING;
return playerState === PLAYING;
} }
function duration() { function duration() {
var dur; var dur;
dur = this.videoPlayer.player.getDuration(); dur = this.videoPlayer.player.getDuration();
if (!isFinite(dur)) { if (!isFinite(dur)) {
dur = this.getDuration(); dur = this.getDuration();
} }
...@@ -431,7 +499,7 @@ function (HTML5Video) { ...@@ -431,7 +499,7 @@ function (HTML5Video) {
// If extra parameters were passed to the log. // If extra parameters were passed to the log.
if (data) { if (data) {
$.each(data, function(paramName, value) { $.each(data, function (paramName, value) {
logInfo[paramName] = value; logInfo[paramName] = value;
}); });
} }
......
...@@ -19,6 +19,10 @@ function () { ...@@ -19,6 +19,10 @@ function () {
} }
if (state.videoType === 'html5' && !(_checkPlaybackRates())) { if (state.videoType === 'html5' && !(_checkPlaybackRates())) {
console.log(
'[Video info]: HTML5 mode - playbackRate is not supported.'
);
_hideSpeedControl(state); _hideSpeedControl(state);
return; return;
...@@ -65,7 +69,7 @@ function () { ...@@ -65,7 +69,7 @@ function () {
state.videoSpeedControl.el state.videoSpeedControl.el
); );
$.each(state.videoSpeedControl.speeds, function(index, speed) { $.each(state.videoSpeedControl.speeds, function (index, speed) {
var link = '<a class="speed_link" href="#">' + speed + 'x</a>'; var link = '<a class="speed_link" href="#">' + speed + 'x</a>';
state.videoSpeedControl.videoSpeedsEl state.videoSpeedControl.videoSpeedsEl
...@@ -78,26 +82,27 @@ function () { ...@@ -78,26 +82,27 @@ function () {
} }
/** /**
* @desc Check if playbackRate supports by browser. * @desc Check if playbackRate supports by browser.
* *
* @type {function} * @type {function}
* @access private * @access private
* *
* @param {object} state The object containg the state of the video player. * @param {object} state The object containg the state of the video player.
* All other modules, their parameters, public variables, etc. are * All other modules, their parameters, public variables, etc. are
* available via this object. * available via this object.
* *
* @this {object} The global window object. * @this {object} The global window object.
* *
* @returns {Boolean} * @returns {Boolean}
* true: Browser support playbackRate functionality. * true: Browser support playbackRate functionality.
* false: Browser doesn't support playbackRate functionality. * false: Browser doesn't support playbackRate functionality.
*/ */
function _checkPlaybackRates() { function _checkPlaybackRates() {
var video = document.createElement('video'); var video = document.createElement('video');
// If browser supports, 1.0 should be returned by playbackRate property. // If browser supports, 1.0 should be returned by playbackRate
// In this case, function return True. Otherwise, False will be returned. // property. In this case, function return True. Otherwise, False will
// be returned.
return Boolean(video.playbackRate); return Boolean(video.playbackRate);
} }
...@@ -128,7 +133,7 @@ function () { ...@@ -128,7 +133,7 @@ function () {
.on('click', state.videoSpeedControl.changeVideoSpeed); .on('click', state.videoSpeedControl.changeVideoSpeed);
if (onTouchBasedDevice()) { if (onTouchBasedDevice()) {
state.videoSpeedControl.el.on('click', function(event) { state.videoSpeedControl.el.on('click', function (event) {
// So that you can't highlight this control via a drag // So that you can't highlight this control via a drag
// operation, we disable the default browser actions on a // operation, we disable the default browser actions on a
// click event. // click event.
...@@ -287,7 +292,7 @@ function () { ...@@ -287,7 +292,7 @@ function () {
this.videoSpeedControl.videoSpeedsEl.find('li').removeClass('active'); this.videoSpeedControl.videoSpeedsEl.find('li').removeClass('active');
this.videoSpeedControl.speeds = params.newSpeeds; this.videoSpeedControl.speeds = params.newSpeeds;
$.each(this.videoSpeedControl.speeds, function(index, speed) { $.each(this.videoSpeedControl.speeds, function (index, speed) {
var link, listItem; var link, listItem;
link = '<a class="speed_link" href="#">' + speed + 'x</a>'; link = '<a class="speed_link" href="#">' + speed + 'x</a>';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment