Просмотр исходного кода

Add various callbacks, .nextFrame() method and .paused property

phoboslab 7 лет назад
Родитель
Сommit
4f21d11e4a
6 измененных файлов: 81 добавлений и 4 удалений
  1. 9 0
      README.md
  2. 3 2
      jsmpeg.min.js
  3. 12 0
      src/ajax-progressive.js
  4. 10 0
      src/ajax.js
  5. 37 1
      src/player.js
  6. 10 1
      src/websocket.js

+ 9 - 0
README.md Просмотреть файл

@@ -52,6 +52,13 @@ The `options` argument supports the following properties:
52 52
 - `audioBufferSize` – when streaming, size in bytes for the audio decode buffer. Default 128*1024 (128kb). You may have to increase this for very high bitrates.
53 53
 - `onVideoDecode(decoder, time)` – A callback that is called after each decoded and rendered video frame
54 54
 - `onAudioDecode(decoder, time)` – A callback that is called after each decoded audio frame
55
+- `onPlay(player)` – A callback that is called whenever playback starts
56
+- `onPause(player)` – A callback that is called whenever playback paused (e.g. when .pause() is called or the source has ended)
57
+- `onEnded(player)` – A callback that is called when playback has reached the end of the source (only called when `loop` is `false`).
58
+- `onStalled(player)` – A callback that is called whenever there's not enough data for playback
59
+- `onSourceEstablished(source)` – A callback that is called when source has first received data
60
+- `onSourceCompleted(source)` – A callback that is called when the source has received all data
61
+
55 62
 
56 63
 All options except from `canvas` can also be used with the HTML Element through `data-` attributes. E.g. to specify looping and autoplay in JavaScript:
57 64
 
@@ -75,9 +82,11 @@ A `JSMpeg.Player` instance supports the following methods and properties:
75 82
 - `.play()` – start playback
76 83
 - `.pause()` – pause playback
77 84
 - `.stop()` – stop playback and seek to the beginning
85
+- `.nextFrame()` – advance playback by one video frame. This does not decode audio. Returns `true` on success, `false` when there's not enough data.
78 86
 - `.destroy()` – stops playback, disconnects the source and cleans up WebGL and WebAudio state. The player can not be used afterwards.
79 87
 - `.volume` – get or set the audio volume (0-1)
80 88
 - `.currentTime` – get or set the current playback position in seconds
89
+- `.paused` – read only, wether playback is paused
81 90
 
82 91
 
83 92
 ## Encoding Video/Audio for JSMpeg

Разница между файлами не показана из-за своего большого размера
+ 3 - 2
jsmpeg.min.js


+ 12 - 0
src/ajax-progressive.js Просмотреть файл

@@ -17,6 +17,9 @@ var AjaxProgressiveSource = function(url, options) {
17 17
 	this.loadStartTime = 0;
18 18
 	this.throttled = options.throttled !== false;
19 19
 	this.aborted = false;
20
+
21
+	this.onEstablishedCallback = options.onSourceEstablished;
22
+	this.onCompletedCallback = options.onSourceCompleted;
20 23
 };
21 24
 
22 25
 AjaxProgressiveSource.prototype.connect = function(destination) {
@@ -64,6 +67,9 @@ AjaxProgressiveSource.prototype.loadNextChunk = function() {
64 67
 	
65 68
 	if (start >= this.fileSize || this.aborted) {
66 69
 		this.completed = true;
70
+		if (this.onCompletedCallback) {
71
+			this.onCompletedCallback(this);
72
+		}
67 73
 		return;
68 74
 	}
69 75
 	
@@ -101,12 +107,18 @@ AjaxProgressiveSource.prototype.onProgress = function(ev) {
101 107
 };
102 108
 
103 109
 AjaxProgressiveSource.prototype.onChunkLoad = function(data) {
110
+	var isFirstChunk = !this.established;
104 111
 	this.established = true;
105 112
 	this.progress = 1;
113
+	
106 114
 	this.loadedSize += data.byteLength;
107 115
 	this.loadFails = 0;
108 116
 	this.isLoading = false;
109 117
 
118
+	if (isFirstChunk && this.onEstablishedCallback) {
119
+		this.onEstablishedCallback(this);
120
+	}
121
+
110 122
 	if (this.destination) {
111 123
 		this.destination.write(data);
112 124
 	}

+ 10 - 0
src/ajax.js Просмотреть файл

@@ -8,6 +8,9 @@ var AjaxSource = function(url, options) {
8 8
 	this.completed = false;
9 9
 	this.established = false;
10 10
 	this.progress = 0;
11
+
12
+	this.onEstablishedCallback = options.onSourceEstablished;
13
+	this.onCompletedCallback = options.onSourceCompleted;
11 14
 };
12 15
 
13 16
 AjaxSource.prototype.connect = function(destination) {
@@ -49,6 +52,13 @@ AjaxSource.prototype.onLoad = function(data) {
49 52
 	this.completed = true;
50 53
 	this.progress = 1;
51 54
 
55
+	if (this.onEstablishedCallback) {
56
+		this.onEstablishedCallback(this);
57
+	}
58
+	if (this.onCompletedCallback) {
59
+		this.onCompletedCallback(this);
60
+	}
61
+
52 62
 	if (this.destination) {
53 63
 		this.destination.write(data);
54 64
 	}

+ 37 - 1
src/player.js Просмотреть файл

@@ -29,7 +29,7 @@ var Player = function(url, options) {
29 29
 
30 30
 	if (!options.disableWebAssembly && JSMpeg.WASMModule.IsSupported()) {
31 31
 		this.wasmModule = new JSMpeg.WASMModule();
32
-		options.wasmModule =this.wasmModule;
32
+		options.wasmModule = this.wasmModule;
33 33
 	}
34 34
 
35 35
 	if (options.video !== false) {
@@ -63,6 +63,7 @@ var Player = function(url, options) {
63 63
 		set: this.setVolume
64 64
 	});
65 65
 
66
+	this.paused = true;
66 67
 	this.unpauseOnShow = false;
67 68
 	if (options.pauseWhenHidden !== false) {
68 69
 		document.addEventListener('visibilitychange', this.showHide.bind(this));
@@ -104,14 +105,25 @@ Player.prototype.showHide = function(ev) {
104 105
 };
105 106
 
106 107
 Player.prototype.play = function(ev) {
108
+	if (this.animationId) {
109
+		return;
110
+	}
111
+
107 112
 	this.animationId = requestAnimationFrame(this.update.bind(this));
108 113
 	this.wantsToPlay = true;
114
+	this.paused = false;
109 115
 };
110 116
 
111 117
 Player.prototype.pause = function(ev) {
118
+	if (this.paused) {
119
+		return;
120
+	}
121
+
112 122
 	cancelAnimationFrame(this.animationId);
123
+	this.animationId = null;
113 124
 	this.wantsToPlay = false;
114 125
 	this.isPlaying = false;
126
+	this.paused = true;
115 127
 
116 128
 	if (this.audio && this.audio.canPlay) {
117 129
 		// Seek to the currentTime again - audio may already be enqueued a bit
@@ -119,6 +131,10 @@ Player.prototype.pause = function(ev) {
119 131
 		this.audioOut.stop();
120 132
 		this.seek(this.currentTime);
121 133
 	}
134
+
135
+	if (this.options.onPause) {
136
+		this.options.onPause(this);
137
+	}
122 138
 };
123 139
 
124 140
 Player.prototype.getVolume = function() {
@@ -186,6 +202,10 @@ Player.prototype.update = function() {
186 202
 	if (!this.isPlaying) {
187 203
 		this.isPlaying = true;
188 204
 		this.startTime = JSMpeg.Now() - this.currentTime;
205
+
206
+		if (this.options.onPlay) {
207
+			this.options.onPlay(this);
208
+		}
189 209
 	}
190 210
 
191 211
 	if (this.options.streaming) {
@@ -219,6 +239,13 @@ Player.prototype.updateForStreaming = function() {
219 239
 	}
220 240
 };
221 241
 
242
+Player.prototype.nextFrame = function() {
243
+	if (this.source.established && this.video) {
244
+		return this.video.decode();
245
+	}
246
+	return false;
247
+};
248
+
222 249
 Player.prototype.updateForStaticFile = function() {
223 250
 	var notEnoughData = false,
224 251
 		headroom = 0;
@@ -275,8 +302,17 @@ Player.prototype.updateForStaticFile = function() {
275 302
 		}
276 303
 		else {
277 304
 			this.pause();
305
+			if (this.options.onEnded) {
306
+				this.options.onEnded(this);
307
+			}
278 308
 		}
279 309
 	}
310
+
311
+	// If there's not enough data and the source is not completed, we have
312
+	// just stalled.
313
+	else if (notEnoughData && this.options.onStalled) {
314
+		this.options.onStalled(this);
315
+	}
280 316
 };
281 317
 
282 318
 return Player;

+ 10 - 1
src/websocket.js Просмотреть файл

@@ -18,6 +18,9 @@ var WSSource = function(url, options) {
18 18
 	this.progress = 0;
19 19
 
20 20
 	this.reconnectTimeoutId = 0;
21
+
22
+	this.onEstablishedCallback = options.onSourceEstablished;
23
+	this.onCompletedCallback = options.onSourceCompleted; // Never used
21 24
 };
22 25
 
23 26
 WSSource.prototype.connect = function(destination) {
@@ -49,7 +52,6 @@ WSSource.prototype.resume = function(secondsHeadroom) {
49 52
 
50 53
 WSSource.prototype.onOpen = function() {
51 54
 	this.progress = 1;
52
-	this.established = true;
53 55
 };
54 56
 
55 57
 WSSource.prototype.onClose = function() {
@@ -62,6 +64,13 @@ WSSource.prototype.onClose = function() {
62 64
 };
63 65
 
64 66
 WSSource.prototype.onMessage = function(ev) {
67
+	var isFirstChunk = !this.established;
68
+	this.established = true;
69
+
70
+	if (isFirstChunk && this.onEstablishedCallback) {
71
+		this.onEstablishedCallback(this);
72
+	}
73
+
65 74
 	if (this.destination) {
66 75
 		this.destination.write(ev.data);
67 76
 	}