Przeglądaj źródła

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

phoboslab 7 lat temu
rodzic
commit
4f21d11e4a
6 zmienionych plików z 81 dodań i 4 usunięć
  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 Wyświetl plik

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.
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
 - `onVideoDecode(decoder, time)` – A callback that is called after each decoded and rendered video frame
53
 - `onVideoDecode(decoder, time)` – A callback that is called after each decoded and rendered video frame
54
 - `onAudioDecode(decoder, time)` – A callback that is called after each decoded audio frame
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
 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:
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
 - `.play()` – start playback
82
 - `.play()` – start playback
76
 - `.pause()` – pause playback
83
 - `.pause()` – pause playback
77
 - `.stop()` – stop playback and seek to the beginning
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
 - `.destroy()` – stops playback, disconnects the source and cleans up WebGL and WebAudio state. The player can not be used afterwards.
86
 - `.destroy()` – stops playback, disconnects the source and cleans up WebGL and WebAudio state. The player can not be used afterwards.
79
 - `.volume` – get or set the audio volume (0-1)
87
 - `.volume` – get or set the audio volume (0-1)
80
 - `.currentTime` – get or set the current playback position in seconds
88
 - `.currentTime` – get or set the current playback position in seconds
89
+- `.paused` – read only, wether playback is paused
81
 
90
 
82
 
91
 
83
 ## Encoding Video/Audio for JSMpeg
92
 ## Encoding Video/Audio for JSMpeg

Plik diff jest za duży
+ 3 - 2
jsmpeg.min.js


+ 12 - 0
src/ajax-progressive.js Wyświetl plik

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

+ 10 - 0
src/ajax.js Wyświetl plik

8
 	this.completed = false;
8
 	this.completed = false;
9
 	this.established = false;
9
 	this.established = false;
10
 	this.progress = 0;
10
 	this.progress = 0;
11
+
12
+	this.onEstablishedCallback = options.onSourceEstablished;
13
+	this.onCompletedCallback = options.onSourceCompleted;
11
 };
14
 };
12
 
15
 
13
 AjaxSource.prototype.connect = function(destination) {
16
 AjaxSource.prototype.connect = function(destination) {
49
 	this.completed = true;
52
 	this.completed = true;
50
 	this.progress = 1;
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
 	if (this.destination) {
62
 	if (this.destination) {
53
 		this.destination.write(data);
63
 		this.destination.write(data);
54
 	}
64
 	}

+ 37 - 1
src/player.js Wyświetl plik

29
 
29
 
30
 	if (!options.disableWebAssembly && JSMpeg.WASMModule.IsSupported()) {
30
 	if (!options.disableWebAssembly && JSMpeg.WASMModule.IsSupported()) {
31
 		this.wasmModule = new JSMpeg.WASMModule();
31
 		this.wasmModule = new JSMpeg.WASMModule();
32
-		options.wasmModule =this.wasmModule;
32
+		options.wasmModule = this.wasmModule;
33
 	}
33
 	}
34
 
34
 
35
 	if (options.video !== false) {
35
 	if (options.video !== false) {
63
 		set: this.setVolume
63
 		set: this.setVolume
64
 	});
64
 	});
65
 
65
 
66
+	this.paused = true;
66
 	this.unpauseOnShow = false;
67
 	this.unpauseOnShow = false;
67
 	if (options.pauseWhenHidden !== false) {
68
 	if (options.pauseWhenHidden !== false) {
68
 		document.addEventListener('visibilitychange', this.showHide.bind(this));
69
 		document.addEventListener('visibilitychange', this.showHide.bind(this));
104
 };
105
 };
105
 
106
 
106
 Player.prototype.play = function(ev) {
107
 Player.prototype.play = function(ev) {
108
+	if (this.animationId) {
109
+		return;
110
+	}
111
+
107
 	this.animationId = requestAnimationFrame(this.update.bind(this));
112
 	this.animationId = requestAnimationFrame(this.update.bind(this));
108
 	this.wantsToPlay = true;
113
 	this.wantsToPlay = true;
114
+	this.paused = false;
109
 };
115
 };
110
 
116
 
111
 Player.prototype.pause = function(ev) {
117
 Player.prototype.pause = function(ev) {
118
+	if (this.paused) {
119
+		return;
120
+	}
121
+
112
 	cancelAnimationFrame(this.animationId);
122
 	cancelAnimationFrame(this.animationId);
123
+	this.animationId = null;
113
 	this.wantsToPlay = false;
124
 	this.wantsToPlay = false;
114
 	this.isPlaying = false;
125
 	this.isPlaying = false;
126
+	this.paused = true;
115
 
127
 
116
 	if (this.audio && this.audio.canPlay) {
128
 	if (this.audio && this.audio.canPlay) {
117
 		// Seek to the currentTime again - audio may already be enqueued a bit
129
 		// Seek to the currentTime again - audio may already be enqueued a bit
119
 		this.audioOut.stop();
131
 		this.audioOut.stop();
120
 		this.seek(this.currentTime);
132
 		this.seek(this.currentTime);
121
 	}
133
 	}
134
+
135
+	if (this.options.onPause) {
136
+		this.options.onPause(this);
137
+	}
122
 };
138
 };
123
 
139
 
124
 Player.prototype.getVolume = function() {
140
 Player.prototype.getVolume = function() {
186
 	if (!this.isPlaying) {
202
 	if (!this.isPlaying) {
187
 		this.isPlaying = true;
203
 		this.isPlaying = true;
188
 		this.startTime = JSMpeg.Now() - this.currentTime;
204
 		this.startTime = JSMpeg.Now() - this.currentTime;
205
+
206
+		if (this.options.onPlay) {
207
+			this.options.onPlay(this);
208
+		}
189
 	}
209
 	}
190
 
210
 
191
 	if (this.options.streaming) {
211
 	if (this.options.streaming) {
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
 Player.prototype.updateForStaticFile = function() {
249
 Player.prototype.updateForStaticFile = function() {
223
 	var notEnoughData = false,
250
 	var notEnoughData = false,
224
 		headroom = 0;
251
 		headroom = 0;
275
 		}
302
 		}
276
 		else {
303
 		else {
277
 			this.pause();
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
 return Player;
318
 return Player;

+ 10 - 1
src/websocket.js Wyświetl plik

18
 	this.progress = 0;
18
 	this.progress = 0;
19
 
19
 
20
 	this.reconnectTimeoutId = 0;
20
 	this.reconnectTimeoutId = 0;
21
+
22
+	this.onEstablishedCallback = options.onSourceEstablished;
23
+	this.onCompletedCallback = options.onSourceCompleted; // Never used
21
 };
24
 };
22
 
25
 
23
 WSSource.prototype.connect = function(destination) {
26
 WSSource.prototype.connect = function(destination) {
49
 
52
 
50
 WSSource.prototype.onOpen = function() {
53
 WSSource.prototype.onOpen = function() {
51
 	this.progress = 1;
54
 	this.progress = 1;
52
-	this.established = true;
53
 };
55
 };
54
 
56
 
55
 WSSource.prototype.onClose = function() {
57
 WSSource.prototype.onClose = function() {
62
 };
64
 };
63
 
65
 
64
 WSSource.prototype.onMessage = function(ev) {
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
 	if (this.destination) {
74
 	if (this.destination) {
66
 		this.destination.write(ev.data);
75
 		this.destination.write(ev.data);
67
 	}
76
 	}