|
|
@@ -30,6 +30,7 @@ var jsmpeg = window.jsmpeg = function( url, opts ) {
|
|
30
|
30
|
this.canvas = opts.canvas || document.createElement('canvas');
|
|
31
|
31
|
this.autoplay = !!opts.autoplay;
|
|
32
|
32
|
this.loop = !!opts.loop;
|
|
|
33
|
+ this.seekable = !!opts.seekable;
|
|
33
|
34
|
this.externalLoadCallback = opts.onload || null;
|
|
34
|
35
|
this.externalDecodeCallback = opts.ondecodeframe || null;
|
|
35
|
36
|
this.externalFinishedCallback = opts.onfinished || null;
|
|
|
@@ -265,6 +266,8 @@ jsmpeg.prototype.stopRecording = function() {
|
|
265
|
266
|
|
|
266
|
267
|
// ----------------------------------------------------------------------------
|
|
267
|
268
|
// Loading via Ajax
|
|
|
269
|
+
|
|
|
270
|
+jsmpeg.prototype.intraFrames = [];
|
|
268
|
271
|
|
|
269
|
272
|
jsmpeg.prototype.load = function( url ) {
|
|
270
|
273
|
this.url = url;
|
|
|
@@ -307,6 +310,11 @@ jsmpeg.prototype.updateLoaderGL = function( ev ) {
|
|
307
|
310
|
|
|
308
|
311
|
jsmpeg.prototype.loadCallback = function(file) {
|
|
309
|
312
|
this.buffer = new BitReader(file);
|
|
|
313
|
+
|
|
|
314
|
+ if( this.seekable ) {
|
|
|
315
|
+ this.collectIntraFrames();
|
|
|
316
|
+ this.buffer.index = 0;
|
|
|
317
|
+ }
|
|
310
|
318
|
|
|
311
|
319
|
this.findStartCode(START_SEQUENCE);
|
|
312
|
320
|
this.firstSequenceHeader = this.buffer.index;
|
|
|
@@ -324,6 +332,52 @@ jsmpeg.prototype.loadCallback = function(file) {
|
|
324
|
332
|
}
|
|
325
|
333
|
};
|
|
326
|
334
|
|
|
|
335
|
+jsmpeg.prototype.collectIntraFrames = function() {
|
|
|
336
|
+ // Loop through the whole buffer and collect all intraFrames to build our seek index.
|
|
|
337
|
+ for( var frame = 0; this.findStartCode(START_PICTURE) !== BitReader.NOT_FOUND; frame++ ) {
|
|
|
338
|
+
|
|
|
339
|
+ // Check if the found picture is an intra frame and remember the position
|
|
|
340
|
+ this.buffer.advance(10); // skip temporalReference
|
|
|
341
|
+ if( this.buffer.getBits(3) == PICTURE_TYPE_I ) {
|
|
|
342
|
+ // Remember index 13 bits back, before temporalReference and picture type
|
|
|
343
|
+ this.intraFrames.push({frame: frame, index: this.buffer.index - 13});
|
|
|
344
|
+ }
|
|
|
345
|
+ }
|
|
|
346
|
+};
|
|
|
347
|
+
|
|
|
348
|
+jsmpeg.prototype.seekToFrame = function(seekFrame, seekExact) {
|
|
|
349
|
+ var target = null;
|
|
|
350
|
+ for( var i = 1; i < this.intraFrames.length; i++ ) {
|
|
|
351
|
+ if( this.intraFrames[i].frame > seekFrame ) {
|
|
|
352
|
+ target = this.intraFrames[i-1];
|
|
|
353
|
+ break;
|
|
|
354
|
+ }
|
|
|
355
|
+ }
|
|
|
356
|
+
|
|
|
357
|
+ if( !target ) {
|
|
|
358
|
+ return false;
|
|
|
359
|
+ }
|
|
|
360
|
+
|
|
|
361
|
+ this.buffer.index = target.index;
|
|
|
362
|
+
|
|
|
363
|
+ // If we're seeking to the exact frame, we may have to decode some more frames before
|
|
|
364
|
+ // the one we want
|
|
|
365
|
+ if( seekExact ) {
|
|
|
366
|
+ for( var currentFrame = target.frame; currentFrame < seekFrame-1; currentFrame++ ) {
|
|
|
367
|
+ this.decodePicture(DECODE_SKIP_OUTPUT);
|
|
|
368
|
+ this.findStartCode(START_PICTURE);
|
|
|
369
|
+ }
|
|
|
370
|
+ }
|
|
|
371
|
+
|
|
|
372
|
+ // Decode and display the picture we have seeked to
|
|
|
373
|
+ this.decodePicture();
|
|
|
374
|
+ return true;
|
|
|
375
|
+};
|
|
|
376
|
+
|
|
|
377
|
+jsmpeg.prototype.seekToTime = function(time, seekExact) {
|
|
|
378
|
+ this.seekToFrame( (time * this.pictureRate)|0, seekExact );
|
|
|
379
|
+};
|
|
|
380
|
+
|
|
327
|
381
|
jsmpeg.prototype.play = function(file) {
|
|
328
|
382
|
if( this.playing ) { return; }
|
|
329
|
383
|
this.targetTime = this.now();
|