1 /** The minplayer namespace. */ 2 var minplayer = minplayer || {}; 3 4 /** All the media player implementations */ 5 minplayer.players = minplayer.players || {}; 6 7 /** 8 * @constructor 9 * @extends minplayer.display 10 * @class The HTML5 media player implementation. 11 * 12 * @param {object} context The jQuery context. 13 * @param {object} options This components options. 14 * @param {object} queue The event queue to pass events around. 15 */ 16 minplayer.players.html5 = function(context, options, queue) { 17 18 // Derive players base. 19 minplayer.players.base.call(this, context, options, queue); 20 }; 21 22 /** Derive from minplayer.players.base. */ 23 minplayer.players.html5.prototype = new minplayer.players.base(); 24 25 /** Reset the constructor. */ 26 minplayer.players.html5.prototype.constructor = minplayer.players.html5; 27 28 /** 29 * @see minplayer.players.base#getPriority 30 * @param {object} file A {@link minplayer.file} object. 31 * @return {number} The priority of this media player. 32 */ 33 minplayer.players.html5.getPriority = function(file) { 34 return 10; 35 }; 36 37 /** 38 * @see minplayer.players.base#canPlay 39 * @return {boolean} If this player can play this media type. 40 */ 41 minplayer.players.html5.canPlay = function(file) { 42 switch (file.mimetype) { 43 case 'video/ogg': 44 return !!minplayer.playTypes.videoOGG; 45 case 'video/mp4': 46 case 'video/x-mp4': 47 case 'video/m4v': 48 case 'video/x-m4v': 49 return !!minplayer.playTypes.videoH264; 50 case 'video/x-webm': 51 case 'video/webm': 52 case 'application/octet-stream': 53 return !!minplayer.playTypes.videoWEBM; 54 case 'audio/ogg': 55 return !!minplayer.playTypes.audioOGG; 56 case 'audio/mpeg': 57 return !!minplayer.playTypes.audioMP3; 58 case 'audio/mp4': 59 return !!minplayer.playTypes.audioMP4; 60 default: 61 return false; 62 } 63 }; 64 65 /** 66 * @see minplayer.plugin.construct 67 */ 68 minplayer.players.html5.prototype.construct = function() { 69 70 // Call base constructor. 71 minplayer.players.base.prototype.construct.call(this); 72 73 // Set the plugin name within the options. 74 this.options.pluginName = 'html5'; 75 76 // Add the player events. 77 this.addPlayerEvents(); 78 }; 79 80 /** 81 * Adds a new player event. 82 * 83 * @param {string} type The type of event being fired. 84 * @param {function} callback Called when the event is fired. 85 */ 86 minplayer.players.html5.prototype.addPlayerEvent = function(type, callback) { 87 if (this.player) { 88 89 // Add an event listener for this event type. 90 this.player.addEventListener(type, (function(player) { 91 92 // Get the function name. 93 var func = type + 'Event'; 94 95 // If the callback already exists, then remove it from the player. 96 if (player[func]) { 97 player.player.removeEventListener(type, player[func], false); 98 } 99 100 // Create a new callback. 101 player[func] = function(event) { 102 callback.call(player, event); 103 }; 104 105 // Return the callback. 106 return player[func]; 107 108 })(this), false); 109 } 110 }; 111 112 /** 113 * Add events. 114 * @return {boolean} If this action was performed. 115 */ 116 minplayer.players.html5.prototype.addPlayerEvents = function() { 117 118 // Check if the player exists. 119 if (this.player) { 120 121 this.addPlayerEvent('abort', function() { 122 this.trigger('abort'); 123 }); 124 this.addPlayerEvent('loadstart', function() { 125 this.onReady(); 126 }); 127 this.addPlayerEvent('loadeddata', function() { 128 this.onLoaded(); 129 }); 130 this.addPlayerEvent('loadedmetadata', function() { 131 this.onLoaded(); 132 }); 133 this.addPlayerEvent('canplaythrough', function() { 134 this.onLoaded(); 135 }); 136 this.addPlayerEvent('ended', function() { 137 this.onComplete(); 138 }); 139 this.addPlayerEvent('pause', function() { 140 this.onPaused(); 141 }); 142 this.addPlayerEvent('play', function() { 143 this.onPlaying(); 144 }); 145 this.addPlayerEvent('playing', function() { 146 this.onPlaying(); 147 }); 148 149 var errorSent = false; 150 this.addPlayerEvent('error', function() { 151 if (!errorSent) { 152 errorSent = true; 153 this.trigger('error', 'An error occured - ' + this.player.error.code); 154 } 155 }); 156 157 this.addPlayerEvent('waiting', function() { 158 this.onWaiting(); 159 }); 160 this.addPlayerEvent('durationchange', function() { 161 this.duration.set(this.player.duration); 162 this.trigger('durationchange', {duration: this.player.duration}); 163 }); 164 this.addPlayerEvent('progress', function(event) { 165 this.bytesTotal.set(event.total); 166 this.bytesLoaded.set(event.loaded); 167 }); 168 return true; 169 } 170 171 return false; 172 }; 173 174 /** 175 * @see minplayer.players.base#onReady 176 */ 177 minplayer.players.html5.prototype.onReady = function() { 178 minplayer.players.base.prototype.onReady.call(this); 179 180 // Android just say we are loaded here. 181 if (minplayer.isAndroid) { 182 this.onLoaded(); 183 } 184 185 // iOS devices are strange in that they don't autoload. 186 if (minplayer.isIDevice) { 187 this.play(); 188 setTimeout((function(player) { 189 return function() { 190 player.pause(); 191 player.onLoaded(); 192 }; 193 })(this), 1); 194 } 195 }; 196 197 /** 198 * @see minplayer.players.base#playerFound 199 * @return {boolean} TRUE - if the player is in the DOM, FALSE otherwise. 200 */ 201 minplayer.players.html5.prototype.playerFound = function() { 202 return (this.display.find(this.mediaFile.type).length > 0); 203 }; 204 205 /** 206 * @see minplayer.players.base#create 207 * @return {object} The media player entity. 208 */ 209 minplayer.players.html5.prototype.create = function() { 210 minplayer.players.base.prototype.create.call(this); 211 var element = jQuery(document.createElement(this.mediaFile.type)) 212 .attr(this.options.attributes) 213 .append( 214 jQuery(document.createElement('source')).attr({ 215 'src': this.mediaFile.path 216 }) 217 ); 218 219 // Fix the fluid width and height. 220 element.eq(0)[0].setAttribute('width', '100%'); 221 element.eq(0)[0].setAttribute('height', '100%'); 222 element.eq(0)[0].setAttribute('autobuffer', true); 223 var option = this.options.autoload ? 'auto' : 'metadata'; 224 option = minplayer.isIDevice ? 'metadata' : option; 225 element.eq(0)[0].setAttribute('preload', option); 226 return element; 227 }; 228 229 /** 230 * @see minplayer.players.base#getPlayer 231 * @return {object} The media player object. 232 */ 233 minplayer.players.html5.prototype.getPlayer = function() { 234 return this.elements.media.eq(0)[0]; 235 }; 236 237 /** 238 * @see minplayer.players.base#load 239 * @return {boolean} If this action was performed. 240 */ 241 minplayer.players.html5.prototype.load = function(file) { 242 243 // See if a load is even necessary. 244 if (minplayer.players.base.prototype.load.call(this, file)) { 245 246 // Get the current source. 247 var src = this.elements.media.attr('src'); 248 if (!src) { 249 src = jQuery('source', this.elements.media).eq(0).attr('src'); 250 } 251 252 // Only swap out if the new file is different from the source. 253 if (src != file.path) { 254 255 // Add a new player. 256 this.addPlayer(); 257 258 // Set the new player. 259 this.player = this.getPlayer(); 260 261 // Add the events again. 262 this.addPlayerEvents(); 263 264 // Change the source... 265 var code = '<source src="' + file.path + '"></source>'; 266 this.elements.media.removeAttr('src').empty().html(code); 267 return true; 268 } 269 } 270 271 return false; 272 }; 273 274 /** 275 * @see minplayer.players.base#play 276 * @return {boolean} If this action was performed. 277 */ 278 minplayer.players.html5.prototype.play = function() { 279 if (minplayer.players.base.prototype.play.call(this)) { 280 this.player.play(); 281 return true; 282 } 283 284 return false; 285 }; 286 287 /** 288 * @see minplayer.players.base#pause 289 * @return {boolean} If this action was performed. 290 */ 291 minplayer.players.html5.prototype.pause = function() { 292 if (minplayer.players.base.prototype.pause.call(this)) { 293 this.player.pause(); 294 return true; 295 } 296 297 return false; 298 }; 299 300 /** 301 * @see minplayer.players.base#stop 302 * @return {boolean} If this action was performed. 303 */ 304 minplayer.players.html5.prototype.stop = function() { 305 if (minplayer.players.base.prototype.stop.call(this)) { 306 this.player.pause(); 307 this.player.src = ''; 308 return true; 309 } 310 311 return false; 312 }; 313 314 /** 315 * @see minplayer.players.base#seek 316 * @return {boolean} If this action was performed. 317 */ 318 minplayer.players.html5.prototype.seek = function(pos) { 319 if (minplayer.players.base.prototype.seek.call(this, pos)) { 320 this.player.currentTime = pos; 321 return true; 322 } 323 324 return false; 325 }; 326 327 /** 328 * @see minplayer.players.base#setVolume 329 * @return {boolean} If this action was performed. 330 */ 331 minplayer.players.html5.prototype.setVolume = function(vol) { 332 if (minplayer.players.base.prototype.setVolume.call(this, vol)) { 333 this.player.volume = vol; 334 return true; 335 } 336 337 return false; 338 }; 339 340 /** 341 * @see minplayer.players.base#getVolume 342 */ 343 minplayer.players.html5.prototype.getVolume = function(callback) { 344 if (this.isReady()) { 345 callback(this.player.volume); 346 } 347 }; 348 349 /** 350 * @see minplayer.players.base#getDuration 351 */ 352 minplayer.players.html5.prototype.getDuration = function(callback) { 353 if (this.isReady()) { 354 callback(this.player.duration); 355 } 356 }; 357 358 /** 359 * @see minplayer.players.base#getCurrentTime 360 */ 361 minplayer.players.html5.prototype.getCurrentTime = function(callback) { 362 if (this.isReady()) { 363 callback(this.player.currentTime); 364 } 365 }; 366 367 /** 368 * @see minplayer.players.base#getBytesLoaded 369 */ 370 minplayer.players.html5.prototype.getBytesLoaded = function(callback) { 371 if (this.isReady()) { 372 var loaded = 0; 373 374 // Check several different possibilities. 375 if (this.bytesLoaded.value) { 376 loaded = this.bytesLoaded.value; 377 } 378 else if (this.player.buffered && 379 this.player.buffered.length > 0 && 380 this.player.buffered.end && 381 this.player.duration) { 382 loaded = this.player.buffered.end(0); 383 } 384 else if (this.player.bytesTotal != undefined && 385 this.player.bytesTotal > 0 && 386 this.player.bufferedBytes != undefined) { 387 loaded = this.player.bufferedBytes; 388 } 389 390 // Return the loaded amount. 391 callback(loaded); 392 } 393 }; 394 395 /** 396 * @see minplayer.players.base#getBytesTotal 397 */ 398 minplayer.players.html5.prototype.getBytesTotal = function(callback) { 399 if (this.isReady()) { 400 401 var total = 0; 402 403 // Check several different possibilities. 404 if (this.bytesTotal.value) { 405 total = this.bytesTotal.value; 406 } 407 else if (this.player.buffered && 408 this.player.buffered.length > 0 && 409 this.player.buffered.end && 410 this.player.duration) { 411 total = this.player.duration; 412 } 413 else if (this.player.bytesTotal != undefined && 414 this.player.bytesTotal > 0 && 415 this.player.bufferedBytes != undefined) { 416 total = this.player.bytesTotal; 417 } 418 419 // Return the loaded amount. 420 callback(total); 421 } 422 }; 423