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