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 {function} ready Called when the player is ready.
 15  */
 16 minplayer.players.html5 = function(context, options, ready) {
 17 
 18   // Derive players base.
 19   minplayer.players.base.call(this, context, options, ready);
 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       return minplayer.playTypes.videoH264;
 46     case 'video/x-webm':
 47       return minplayer.playTypes.videoWEBM;
 48     case 'audio/ogg':
 49       return minplayer.playTypes.audioOGG;
 50     case 'audio/mpeg':
 51       return minplayer.playTypes.audioMP3;
 52     case 'audio/mp4':
 53       return minplayer.playTypes.audioMP4;
 54     default:
 55       return false;
 56   }
 57 };
 58 
 59 /**
 60  * @see minplayer.plugin.construct
 61  */
 62 minplayer.players.html5.prototype.construct = function() {
 63 
 64   // Call base constructor.
 65   minplayer.players.base.prototype.construct.call(this);
 66 
 67   // Store the this pointer...
 68   var _this = this;
 69 
 70   // For the HTML5 player, we will just pass events along...
 71   if (this.media) {
 72     this.media.addEventListener('abort', function() {
 73       _this.trigger('abort');
 74     }, false);
 75     this.media.addEventListener('loadstart', function() {
 76       _this.onReady();
 77     }, false);
 78     this.media.addEventListener('loadeddata', function() {
 79       _this.onLoaded();
 80     }, false);
 81     this.media.addEventListener('loadedmetadata', function() {
 82       _this.onLoaded();
 83     }, false);
 84     this.media.addEventListener('canplaythrough', function() {
 85       _this.onLoaded();
 86     }, false);
 87     this.media.addEventListener('ended', function() {
 88       _this.onComplete();
 89     }, false);
 90     this.media.addEventListener('pause', function() {
 91       _this.onPaused();
 92     }, false);
 93     this.media.addEventListener('play', function() {
 94       _this.onPlaying();
 95     }, false);
 96     this.media.addEventListener('playing', function() {
 97       _this.onPlaying();
 98     }, false);
 99     this.media.addEventListener('error', function() {
100       var error = '';
101       switch (this.error.code) {
102          case MEDIA_ERR_NETWORK:
103             error = 'Network error - please try again later.';
104             break;
105          case MEDIA_ERR_DECODE:
106             error = 'Video is broken..';
107             break;
108          case MEDIA_ERR_SRC_NOT_SUPPORTED:
109             error = 'Sorry, your browser can\'t play this video.';
110             break;
111        }
112       _this.trigger('error', error);
113     }, false);
114     this.media.addEventListener('waiting', function() {
115       _this.onWaiting();
116     }, false);
117     this.media.addEventListener('durationchange', function() {
118       _this.duration.set(this.duration);
119       _this.trigger('durationchange', {duration: this.duration});
120     }, false);
121     this.media.addEventListener('progress', function(event) {
122       _this.bytesTotal.set(event.total);
123       _this.bytesLoaded.set(event.loaded);
124     }, false);
125     if (this.autoBuffer()) {
126       this.media.autobuffer = true;
127     } else {
128       this.media.autobuffer = false;
129       this.media.preload = 'none';
130     }
131   }
132 };
133 
134 /**
135  * Determine if this player is able to autobuffer.
136  * @return {boolean} TRUE - the player is able to autobuffer.
137  */
138 minplayer.players.html5.prototype.autoBuffer = function() {
139   var preload = this.media.preload !== 'none';
140   if (typeof this.media.hasAttribute === 'function') {
141     return this.media.hasAttribute('preload') && preload;
142   }
143   else {
144     return false;
145   }
146 };
147 
148 /**
149  * @see minplayer.players.base#playerFound
150  * @return {boolean} TRUE - if the player is in the DOM, FALSE otherwise.
151  */
152 minplayer.players.html5.prototype.playerFound = function() {
153   return (this.display.find(this.mediaFile.type).length > 0);
154 };
155 
156 /**
157  * @see minplayer.players.base#create
158  * @return {object} The media player entity.
159  */
160 minplayer.players.html5.prototype.create = function() {
161   minplayer.players.base.prototype.create.call(this);
162   var element = document.createElement(this.mediaFile.type), attribute = '';
163   for (attribute in this.options.attributes) {
164     if (this.options.attributes.hasOwnProperty(attribute)) {
165       element.setAttribute(attribute, this.options.attributes[attribute]);
166     }
167   }
168   return element;
169 };
170 
171 /**
172  * @see minplayer.players.base#getMedia
173  * @return {object} The media player object.
174  */
175 minplayer.players.html5.prototype.getMedia = function() {
176   return this.options.elements.media.eq(0)[0];
177 };
178 
179 /**
180  * @see minplayer.players.base#load
181  */
182 minplayer.players.html5.prototype.load = function(file) {
183 
184   if (file && this.isReady()) {
185 
186     // Get the current source.
187     var src = this.options.elements.player.attr('src');
188 
189     // If the source is different.
190     if (src != file.path) {
191 
192       // Change the source...
193       var code = '<source src="' + file.path + '" ';
194       code += 'type="' + file.mimetype + '"';
195       code += file.codecs ? ' codecs="' + file.path + '">' : '>';
196       this.options.elements.player.attr('src', '').empty().html(code);
197     }
198   }
199 
200   // Always call the base first on load...
201   minplayer.players.base.prototype.load.call(this, file);
202 };
203 
204 /**
205  * @see minplayer.players.base#play
206  */
207 minplayer.players.html5.prototype.play = function() {
208   minplayer.players.base.prototype.play.call(this);
209   if (this.isReady()) {
210     this.media.play();
211   }
212 };
213 
214 /**
215  * @see minplayer.players.base#pause
216  */
217 minplayer.players.html5.prototype.pause = function() {
218   minplayer.players.base.prototype.pause.call(this);
219   if (this.isReady()) {
220     this.media.pause();
221   }
222 };
223 
224 /**
225  * @see minplayer.players.base#stop
226  */
227 minplayer.players.html5.prototype.stop = function() {
228   minplayer.players.base.prototype.stop.call(this);
229   if (this.isReady()) {
230     this.media.pause();
231     this.media.src = '';
232   }
233 };
234 
235 /**
236  * @see minplayer.players.base#seek
237  */
238 minplayer.players.html5.prototype.seek = function(pos) {
239   minplayer.players.base.prototype.seek.call(this, pos);
240   if (this.isReady()) {
241     this.media.currentTime = pos;
242   }
243 };
244 
245 /**
246  * @see minplayer.players.base#setVolume
247  */
248 minplayer.players.html5.prototype.setVolume = function(vol) {
249   minplayer.players.base.prototype.setVolume.call(this, vol);
250   if (this.isReady()) {
251     this.media.volume = vol;
252   }
253 };
254 
255 /**
256  * @see minplayer.players.base#getVolume
257  */
258 minplayer.players.html5.prototype.getVolume = function(callback) {
259   if (this.isReady()) {
260     callback(this.media.volume);
261   }
262 };
263 
264 /**
265  * @see minplayer.players.base#getDuration
266  */
267 minplayer.players.html5.prototype.getDuration = function(callback) {
268   if (this.isReady()) {
269     callback(this.media.duration);
270   }
271 };
272 
273 /**
274  * @see minplayer.players.base#getCurrentTime
275  */
276 minplayer.players.html5.prototype.getCurrentTime = function(callback) {
277   if (this.isReady()) {
278     callback(this.media.currentTime);
279   }
280 };
281 
282 /**
283  * @see minplayer.players.base#getBytesLoaded
284  */
285 minplayer.players.html5.prototype.getBytesLoaded = function(callback) {
286   if (this.isReady()) {
287     var loaded = 0;
288 
289     // Check several different possibilities.
290     if (this.bytesLoaded.value) {
291       loaded = this.bytesLoaded.value;
292     }
293     else if (this.media.buffered &&
294         this.media.buffered.length > 0 &&
295         this.media.buffered.end &&
296         this.media.duration) {
297       loaded = this.media.buffered.end(0);
298     }
299     else if (this.media.bytesTotal != undefined &&
300              this.media.bytesTotal > 0 &&
301              this.media.bufferedBytes != undefined) {
302       loaded = this.media.bufferedBytes;
303     }
304 
305     // Return the loaded amount.
306     callback(loaded);
307   }
308 };
309 
310 /**
311  * @see minplayer.players.base#getBytesTotal
312  */
313 minplayer.players.html5.prototype.getBytesTotal = function(callback) {
314   if (this.isReady()) {
315 
316     var total = 0;
317 
318     // Check several different possibilities.
319     if (this.bytesTotal.value) {
320       total = this.bytesTotal.value;
321     }
322     else if (this.media.buffered &&
323         this.media.buffered.length > 0 &&
324         this.media.buffered.end &&
325         this.media.duration) {
326       total = this.media.duration;
327     }
328     else if (this.media.bytesTotal != undefined &&
329              this.media.bytesTotal > 0 &&
330              this.media.bufferedBytes != undefined) {
331       total = this.media.bytesTotal;
332     }
333 
334     // Return the loaded amount.
335     callback(total);
336   }
337 };
338