Browse Source

Fix lateTime, added bw filter

Dominic Szablewski 12 years ago
parent
commit
a771daeb7a
1 changed files with 64 additions and 11 deletions
  1. 64 11
      jsmpg.js

+ 64 - 11
jsmpg.js View File

30
 	this.autoplay = !!opts.autoplay;
30
 	this.autoplay = !!opts.autoplay;
31
 	this.loop = !!opts.loop;
31
 	this.loop = !!opts.loop;
32
 	this.externalLoadCallback = opts.onload || null;
32
 	this.externalLoadCallback = opts.onload || null;
33
+	this.bwFilter = opts.bwFilter || false;
34
+	this.externalDecodeCallback = opts.ondecodeframe || null;
33
 
35
 
34
 	this.customIntraQuantMatrix = new Uint8Array(64);
36
 	this.customIntraQuantMatrix = new Uint8Array(64);
35
 	this.customNonIntraQuantMatrix = new Uint8Array(64);
37
 	this.customNonIntraQuantMatrix = new Uint8Array(64);
53
 
55
 
54
 jsmpeg.prototype.waitForIntraFrame = true;
56
 jsmpeg.prototype.waitForIntraFrame = true;
55
 jsmpeg.prototype.socketBufferSize = 512 * 1024; // 512kb each
57
 jsmpeg.prototype.socketBufferSize = 512 * 1024; // 512kb each
58
+jsmpeg.prototype.onlostconnection = null;
59
+
56
 jsmpeg.prototype.initSocketClient = function( client ) {
60
 jsmpeg.prototype.initSocketClient = function( client ) {
57
 	this.buffer = new BitReader(new ArrayBuffer(this.socketBufferSize));
61
 	this.buffer = new BitReader(new ArrayBuffer(this.socketBufferSize));
58
 
62
 
119
 
123
 
120
 	// If we are still here, we found the next picture start code!
124
 	// If we are still here, we found the next picture start code!
121
 
125
 
122
-
123
-
124
-	// Skip picture decoding until we find the first intra frame
126
+	
127
+	// Skip picture decoding until we find the first intra frame?
125
 	if( this.waitForIntraFrame ) {
128
 	if( this.waitForIntraFrame ) {
126
 		next.advance(10); // skip temporalReference
129
 		next.advance(10); // skip temporalReference
127
 		if( next.getBits(3) == PICTURE_TYPE_I ) {
130
 		if( next.getBits(3) == PICTURE_TYPE_I ) {
138
 	}
141
 	}
139
 
142
 
140
 	
143
 	
141
-	// Copy the picture chunk over to 'buffer' and schedule decoding.
144
+	// Copy the picture chunk over to 'this.buffer' and schedule decoding.
142
 	var chunkEnd = ((next.index) >> 3);
145
 	var chunkEnd = ((next.index) >> 3);
143
 
146
 
144
 	if( chunkEnd > next.chunkBegin ) {
147
 	if( chunkEnd > next.chunkBegin ) {
181
 
184
 
182
 jsmpeg.prototype.recordBuffers = [];
185
 jsmpeg.prototype.recordBuffers = [];
183
 
186
 
187
+jsmpeg.prototype.canRecord = function(){
188
+	return (this.client && this.client.readyState == this.client.OPEN);
189
+};
190
+
184
 jsmpeg.prototype.startRecording = function(callback) {
191
 jsmpeg.prototype.startRecording = function(callback) {
185
-	if( !this.client ) {
186
-		throw("Can't record when loading from file.");
192
+	if( !this.canRecord() ) {
187
 		return;
193
 		return;
188
 	}
194
 	}
189
 	
195
 	
192
 	this.isRecording = true;
198
 	this.isRecording = true;
193
 	this.recorderWaitForIntraFrame = true;
199
 	this.recorderWaitForIntraFrame = true;
194
 	this.didStartRecordingCallback = callback || null;
200
 	this.didStartRecordingCallback = callback || null;
201
+
202
+	this.recordedFrames = 0;
203
+	this.recordedSize = 0;
195
 	
204
 	
196
 	// Fudge a simple Sequence Header for the MPEG file
205
 	// Fudge a simple Sequence Header for the MPEG file
197
 	
206
 	
300
 
309
 
301
 jsmpeg.prototype.play = function(file) {
310
 jsmpeg.prototype.play = function(file) {
302
 	if( this.playing ) { return; }
311
 	if( this.playing ) { return; }
312
+	this.targetTime = Date.now();
303
 	this.playing = true;
313
 	this.playing = true;
304
 	this.scheduleNextFrame();
314
 	this.scheduleNextFrame();
305
 };
315
 };
360
 jsmpeg.prototype.targetTime = 0;
370
 jsmpeg.prototype.targetTime = 0;
361
 
371
 
362
 jsmpeg.prototype.nextFrame = function() {
372
 jsmpeg.prototype.nextFrame = function() {
373
+	if( !this.buffer ) { return; }
363
 	while(true) {
374
 	while(true) {
364
 		var code = this.buffer.findNextMPEGStartCode();
375
 		var code = this.buffer.findNextMPEGStartCode();
365
 		
376
 		
389
 };
400
 };
390
 
401
 
391
 jsmpeg.prototype.scheduleNextFrame = function() {
402
 jsmpeg.prototype.scheduleNextFrame = function() {
392
-	var wait = (1000/this.pictureRate) - this.lateTime;
403
+	this.lateTime = Date.now() - this.targetTime;
404
+	var wait = Math.max(0, (1000/this.pictureRate) - this.lateTime);
393
 	this.targetTime = Date.now() + wait;
405
 	this.targetTime = Date.now() + wait;
394
-
395
 	if( wait < 18 ) {
406
 	if( wait < 18 ) {
396
 		this.scheduleAnimation();
407
 		this.scheduleAnimation();
397
 	}
408
 	}
472
 	this.canvas.height = this.height;
483
 	this.canvas.height = this.height;
473
 	
484
 	
474
 	this.currentRGBA = this.canvasContext.getImageData(0, 0, this.width, this.height);
485
 	this.currentRGBA = this.canvasContext.getImageData(0, 0, this.width, this.height);
486
+	this.currentRGBA32 = new Uint32Array( this.currentRGBA.data.buffer );
475
 	this.fillArray(this.currentRGBA.data, 255);
487
 	this.fillArray(this.currentRGBA.data, 255);
476
 };
488
 };
477
 
489
 
541
 	
553
 	
542
 	
554
 	
543
 	if( skipOutput != DECODE_SKIP_OUTPUT ) {
555
 	if( skipOutput != DECODE_SKIP_OUTPUT ) {
544
-		this.YCbCrToRGBA();
556
+		if( this.bwFilter ) {
557
+			this.YToRGBA();
558
+		}
559
+		else {
560
+			this.YCbCrToRGBA();	
561
+		}
545
 		this.canvasContext.putImageData(this.currentRGBA, 0, 0);
562
 		this.canvasContext.putImageData(this.currentRGBA, 0, 0);
563
+
564
+		if(this.externalDecodeCallback) {
565
+			this.externalDecodeCallback(this, this.canvas);
566
+		}
546
 	}
567
 	}
547
 	
568
 	
548
 	// If this is a reference picutre then rotate the prediction pointers
569
 	// If this is a reference picutre then rotate the prediction pointers
577
 	var pCr = this.currentCr;
598
 	var pCr = this.currentCr;
578
 	var pRGBA = this.currentRGBA.data;
599
 	var pRGBA = this.currentRGBA.data;
579
 
600
 
580
-
581
-
582
 	// Chroma values are the same for each block of 4 pixels, so we proccess
601
 	// Chroma values are the same for each block of 4 pixels, so we proccess
583
 	// 2 lines at a time, 2 neighboring pixels each.
602
 	// 2 lines at a time, 2 neighboring pixels each.
603
+	// I wish we could use 32bit writes to the RGBA buffer instead of writing
604
+	// each byte separately, but we need the automatic clamping of the RGBA
605
+	// buffer.
584
 
606
 
585
 	var yIndex1 = 0;
607
 	var yIndex1 = 0;
586
 	var yIndex2 = this.codedWidth;
608
 	var yIndex2 = this.codedWidth;
643
 	}
665
 	}
644
 };
666
 };
645
 
667
 
668
+jsmpeg.prototype.YToRGBA = function() {	
669
+	// Luma only
670
+
671
+	var pY = this.currentY;
672
+	var pRGBA = this.currentRGBA32;
673
+
674
+	var yIndex = 0;
675
+	var yNext2Lines = (this.codedWidth - this.width);
676
+
677
+	var rgbaIndex = 0;	
678
+	var cols = this.width;
679
+	var rows = this.height;
680
+
681
+	var y;
682
+
683
+	for( var row = 0; row < rows; row++ ) {
684
+		for( var col = 0; col < cols; col++ ) {
685
+			y = pY[yIndex++];
686
+			pRGBA[rgbaIndex++] = 0xff000000 | y << 16 | y << 8 | y;
687
+		}
688
+		
689
+		yIndex += yNext2Lines;
690
+	}
691
+};
692
+
646
 
693
 
647
 
694
 
648
 
695
 
857
 		H, V, oddH, oddV,
904
 		H, V, oddH, oddV,
858
 		src, dest, last;
905
 		src, dest, last;
859
 
906
 
907
+	// We use 32bit writes here
860
 	var dY = this.currentY32;
908
 	var dY = this.currentY32;
861
 	var dCb = this.currentCb32;
909
 	var dCb = this.currentCb32;
862
 	var dCr = this.currentCr32;
910
 	var dCr = this.currentCr32;
948
 		}
996
 		}
949
 	}
997
 	}
950
 	
998
 	
999
+	if( this.bwFilter ) {
1000
+		// No need to copy chrominance when black&white filter is active
1001
+		return;
1002
+	}
951
 	
1003
 	
1004
+
952
 	// Chrominance
1005
 	// Chrominance
953
 	
1006
 	
954
 	width = this.halfWidth;
1007
 	width = this.halfWidth;