Procházet zdrojové kódy

Added seeking, close #9

Dominic Szablewski před 10 roky
rodič
revize
b2d57a17d9
2 změnil soubory, kde provedl 71 přidání a 0 odebrání
  1. 17 0
      README.md
  2. 54 0
      jsmpg.js

+ 17 - 0
README.md Zobrazit soubor

@@ -25,6 +25,23 @@ player.play();
25 25
 player.stop();
26 26
 
27 27
 
28
+// If you pass 'seekable: true' in the options, you can seek to a specific frame
29
+// or time in the video.
30
+
31
+var player = new jsmpeg('file.mpeg', {canvas: canvas, seekable: true});
32
+
33
+player.seekToFrame(1200); // Seek to intra frame before frame 1200
34
+player.seekToTime(20); // Seek to intra frame before 20sec
35
+
36
+// seekToFrame() and seekToTime() only seek to the closest, previous intra frame by
37
+// default. If you want to seek to the exact frame or time, pass 'true' as second
38
+// parameter.
39
+// Depending on the input video, this can be potentially slow, as jsmpeg has
40
+// to decode all frames between the previous intra frame and the seek target
41
+
42
+player.seekToFrame(1200, true); // Seek to frame 1200 exactly
43
+
44
+
28 45
 // An 'onload' callback can be specified in the 'options' argument
29 46
 var mpegLoaded = function( player ) {
30 47
 	console.log('Loaded', player);

+ 54 - 0
jsmpg.js Zobrazit soubor

@@ -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();