easy-player-pro 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,681 @@
1
+
2
+ //
3
+ // Copyright (c) 2013-2021 Winlin
4
+ //
5
+ // SPDX-License-Identifier: MIT
6
+ //
7
+
8
+ 'use strict';
9
+
10
+ function SrsError(name, message) {
11
+ this.name = name;
12
+ this.message = message;
13
+ this.stack = (new Error()).stack;
14
+ }
15
+ SrsError.prototype = Object.create(Error.prototype);
16
+ SrsError.prototype.constructor = SrsError;
17
+
18
+ // Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
19
+ // Async-awat-prmise based SRS RTC Publisher.
20
+ function SrsRtcPublisherAsync() {
21
+ var self = {};
22
+
23
+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
24
+ self.constraints = {
25
+ audio: true,
26
+ video: {
27
+ width: {ideal: 320, max: 576}
28
+ }
29
+ };
30
+
31
+ // @see https://github.com/rtcdn/rtcdn-draft
32
+ // @url The WebRTC url to play with, for example:
33
+ // webrtc://r.ossrs.net/live/livestream
34
+ // or specifies the API port:
35
+ // webrtc://r.ossrs.net:11985/live/livestream
36
+ // or autostart the publish:
37
+ // webrtc://r.ossrs.net/live/livestream?autostart=true
38
+ // or change the app from live to myapp:
39
+ // webrtc://r.ossrs.net:11985/myapp/livestream
40
+ // or change the stream from livestream to mystream:
41
+ // webrtc://r.ossrs.net:11985/live/mystream
42
+ // or set the api server to myapi.domain.com:
43
+ // webrtc://myapi.domain.com/live/livestream
44
+ // or set the candidate(eip) of answer:
45
+ // webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
46
+ // or force to access https API:
47
+ // webrtc://r.ossrs.net/live/livestream?schema=https
48
+ // or use plaintext, without SRTP:
49
+ // webrtc://r.ossrs.net/live/livestream?encrypt=false
50
+ // or any other information, will pass-by in the query:
51
+ // webrtc://r.ossrs.net/live/livestream?vhost=xxx
52
+ // webrtc://r.ossrs.net/live/livestream?token=xxx
53
+ self.publish = async function (url) {
54
+ var conf = self.__internal.prepareUrl(url);
55
+ self.pc.addTransceiver("audio", {direction: "sendonly"});
56
+ self.pc.addTransceiver("video", {direction: "sendonly"});
57
+ //self.pc.addTransceiver("video", {direction: "sendonly"});
58
+ //self.pc.addTransceiver("audio", {direction: "sendonly"});
59
+
60
+ if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
61
+ throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
62
+ }
63
+ var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
64
+
65
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
66
+ stream.getTracks().forEach(function (track) {
67
+ self.pc.addTrack(track);
68
+
69
+ // Notify about local track when stream is ok.
70
+ self.ontrack && self.ontrack({track: track});
71
+ });
72
+
73
+ var offer = await self.pc.createOffer();
74
+ await self.pc.setLocalDescription(offer);
75
+ var session = await new Promise(function (resolve, reject) {
76
+ // @see https://github.com/rtcdn/rtcdn-draft
77
+ var data = {
78
+ api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
79
+ clientip: null, sdp: offer.sdp
80
+ };
81
+ console.log("Generated offer: ", data);
82
+
83
+ const xhr = new XMLHttpRequest();
84
+ xhr.onload = function() {
85
+ if (xhr.readyState !== xhr.DONE) return;
86
+ if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
87
+ const data = JSON.parse(xhr.responseText);
88
+ console.log("Got answer: ", data);
89
+ return data.code ? reject(xhr) : resolve(data);
90
+ }
91
+ xhr.open('POST', conf.apiUrl, true);
92
+ xhr.setRequestHeader('Content-type', 'application/json');
93
+ xhr.send(JSON.stringify(data));
94
+ });
95
+ await self.pc.setRemoteDescription(
96
+ new RTCSessionDescription({type: 'answer', sdp: session.sdp})
97
+ );
98
+ session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
99
+
100
+ return session;
101
+ };
102
+
103
+ // Close the publisher.
104
+ self.close = function () {
105
+ self.pc && self.pc.close();
106
+ self.pc = null;
107
+ };
108
+
109
+ // The callback when got local stream.
110
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
111
+ self.ontrack = function (event) {
112
+ // Add track to stream of SDK.
113
+ self.stream.addTrack(event.track);
114
+ };
115
+
116
+ // Internal APIs.
117
+ self.__internal = {
118
+ defaultPath: '/rtc/v1/publish/',
119
+ prepareUrl: function (webrtcUrl) {
120
+ var urlObject = self.__internal.parse(webrtcUrl);
121
+
122
+ // If user specifies the schema, use it as API schema.
123
+ var schema = urlObject.user_query.schema;
124
+ schema = schema ? schema + ':' : window.location.protocol;
125
+
126
+ var port = urlObject.port || 1985;
127
+ if (schema === 'https:') {
128
+ port = urlObject.port || 443;
129
+ }
130
+
131
+ // @see https://github.com/rtcdn/rtcdn-draft
132
+ var api = urlObject.user_query.play || self.__internal.defaultPath;
133
+ if (api.lastIndexOf('/') !== api.length - 1) {
134
+ api += '/';
135
+ }
136
+
137
+ var apiUrl = schema + '//' + urlObject.server + ':' + port + api;
138
+ for (var key in urlObject.user_query) {
139
+ if (key !== 'api' && key !== 'play') {
140
+ apiUrl += '&' + key + '=' + urlObject.user_query[key];
141
+ }
142
+ }
143
+ // Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
144
+ apiUrl = apiUrl.replace(api + '&', api + '?');
145
+
146
+ var streamUrl = urlObject.url;
147
+
148
+ return {
149
+ apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
150
+ tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
151
+ };
152
+ },
153
+ parse: function (url) {
154
+ // @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
155
+ var a = document.createElement("a");
156
+ a.href = url.replace("rtmp://", "http://")
157
+ .replace("webrtc://", "http://")
158
+ .replace("rtc://", "http://");
159
+
160
+ var vhost = a.hostname;
161
+ var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
162
+ var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
163
+
164
+ // parse the vhost in the params of app, that srs supports.
165
+ app = app.replace("...vhost...", "?vhost=");
166
+ if (app.indexOf("?") >= 0) {
167
+ var params = app.slice(app.indexOf("?"));
168
+ app = app.slice(0, app.indexOf("?"));
169
+
170
+ if (params.indexOf("vhost=") > 0) {
171
+ vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
172
+ if (vhost.indexOf("&") > 0) {
173
+ vhost = vhost.slice(0, vhost.indexOf("&"));
174
+ }
175
+ }
176
+ }
177
+
178
+ // when vhost equals to server, and server is ip,
179
+ // the vhost is __defaultVhost__
180
+ if (a.hostname === vhost) {
181
+ var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
182
+ if (re.test(a.hostname)) {
183
+ vhost = "__defaultVhost__";
184
+ }
185
+ }
186
+
187
+ // parse the schema
188
+ var schema = "rtmp";
189
+ if (url.indexOf("://") > 0) {
190
+ schema = url.slice(0, url.indexOf("://"));
191
+ }
192
+
193
+ var port = a.port;
194
+ if (!port) {
195
+ // Finger out by webrtc url, if contains http or https port, to overwrite default 1985.
196
+ if (schema === 'webrtc' && url.indexOf(`webrtc://${a.host}:`) === 0) {
197
+ port = (url.indexOf(`webrtc://${a.host}:80`) === 0) ? 80 : 443;
198
+ }
199
+
200
+ // Guess by schema.
201
+ if (schema === 'http') {
202
+ port = 80;
203
+ } else if (schema === 'https') {
204
+ port = 443;
205
+ } else if (schema === 'rtmp') {
206
+ port = 1935;
207
+ }
208
+ }
209
+
210
+ var ret = {
211
+ url: url,
212
+ schema: schema,
213
+ server: a.hostname, port: port,
214
+ vhost: vhost, app: app, stream: stream
215
+ };
216
+ self.__internal.fill_query(a.search, ret);
217
+
218
+ // For webrtc API, we use 443 if page is https, or schema specified it.
219
+ if (!ret.port) {
220
+ if (schema === 'webrtc' || schema === 'rtc') {
221
+ if (ret.user_query.schema === 'https') {
222
+ ret.port = 443;
223
+ } else if (window.location.href.indexOf('https://') === 0) {
224
+ ret.port = 443;
225
+ } else {
226
+ // For WebRTC, SRS use 1985 as default API port.
227
+ ret.port = 1985;
228
+ }
229
+ }
230
+ }
231
+
232
+ return ret;
233
+ },
234
+ fill_query: function (query_string, obj) {
235
+ // pure user query object.
236
+ obj.user_query = {};
237
+
238
+ if (query_string.length === 0) {
239
+ return;
240
+ }
241
+
242
+ // split again for angularjs.
243
+ if (query_string.indexOf("?") >= 0) {
244
+ query_string = query_string.split("?")[1];
245
+ }
246
+
247
+ var queries = query_string.split("&");
248
+ for (var i = 0; i < queries.length; i++) {
249
+ var elem = queries[i];
250
+
251
+ var query = elem.split("=");
252
+ obj[query[0]] = query[1];
253
+ obj.user_query[query[0]] = query[1];
254
+ }
255
+
256
+ // alias domain for vhost.
257
+ if (obj.domain) {
258
+ obj.vhost = obj.domain;
259
+ }
260
+ }
261
+ };
262
+
263
+ self.pc = new RTCPeerConnection(null);
264
+
265
+ // To keep api consistent between player and publisher.
266
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
267
+ // @see https://webrtc.org/getting-started/media-devices
268
+ self.stream = new MediaStream();
269
+
270
+ return self;
271
+ }
272
+
273
+ // Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
274
+ // Async-await-promise based SRS RTC Player.
275
+ function SrsRtcPlayerAsync() {
276
+ var self = {};
277
+
278
+ // @see https://github.com/rtcdn/rtcdn-draft
279
+ // @url The WebRTC url to play with, for example:
280
+ // webrtc://r.ossrs.net/live/livestream
281
+ // or specifies the API port:
282
+ // webrtc://r.ossrs.net:11985/live/livestream
283
+ // webrtc://r.ossrs.net:80/live/livestream
284
+ // or autostart the play:
285
+ // webrtc://r.ossrs.net/live/livestream?autostart=true
286
+ // or change the app from live to myapp:
287
+ // webrtc://r.ossrs.net:11985/myapp/livestream
288
+ // or change the stream from livestream to mystream:
289
+ // webrtc://r.ossrs.net:11985/live/mystream
290
+ // or set the api server to myapi.domain.com:
291
+ // webrtc://myapi.domain.com/live/livestream
292
+ // or set the candidate(eip) of answer:
293
+ // webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
294
+ // or force to access https API:
295
+ // webrtc://r.ossrs.net/live/livestream?schema=https
296
+ // or use plaintext, without SRTP:
297
+ // webrtc://r.ossrs.net/live/livestream?encrypt=false
298
+ // or any other information, will pass-by in the query:
299
+ // webrtc://r.ossrs.net/live/livestream?vhost=xxx
300
+ // webrtc://r.ossrs.net/live/livestream?token=xxx
301
+ self.play = async function(url) {
302
+ var conf = self.__internal.prepareUrl(url);
303
+ self.pc.addTransceiver("audio", {direction: "recvonly"});
304
+ self.pc.addTransceiver("video", {direction: "recvonly"});
305
+ //self.pc.addTransceiver("video", {direction: "recvonly"});
306
+ //self.pc.addTransceiver("audio", {direction: "recvonly"});
307
+
308
+ var offer = await self.pc.createOffer();
309
+ await self.pc.setLocalDescription(offer);
310
+ var session = await new Promise(function(resolve, reject) {
311
+ // @see https://github.com/rtcdn/rtcdn-draft
312
+ var data = {
313
+ api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
314
+ clientip: null, sdp: offer.sdp
315
+ };
316
+ console.log("Generated offer: ", data);
317
+
318
+ const xhr = new XMLHttpRequest();
319
+ xhr.onload = function() {
320
+ if (xhr.readyState !== xhr.DONE) return;
321
+ if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
322
+ const data = JSON.parse(xhr.responseText);
323
+ console.log("Got answer: ", data);
324
+ return data.code ? reject(xhr) : resolve(data);
325
+ }
326
+ xhr.open('POST', conf.apiUrl, true);
327
+ xhr.setRequestHeader('Content-type', 'application/json');
328
+ xhr.send(JSON.stringify(data));
329
+ });
330
+ await self.pc.setRemoteDescription(
331
+ new RTCSessionDescription({type: 'answer', sdp: session.sdp})
332
+ );
333
+ session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
334
+
335
+ return session;
336
+ };
337
+
338
+ // Close the player.
339
+ self.close = function() {
340
+ self.pc && self.pc.close();
341
+ self.pc = null;
342
+ };
343
+
344
+ // The callback when got remote track.
345
+ // Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
346
+ self.ontrack = function (event) {
347
+ // https://webrtc.org/getting-started/remote-streams
348
+ self.stream.addTrack(event.track);
349
+ };
350
+
351
+ // Internal APIs.
352
+ self.__internal = {
353
+ defaultPath: '/rtc/v1/play/',
354
+ prepareUrl: function (webrtcUrl) {
355
+ var urlObject = self.__internal.parse(webrtcUrl);
356
+
357
+ // If user specifies the schema, use it as API schema.
358
+ var schema = urlObject.user_query.schema;
359
+ schema = schema ? schema + ':' : window.location.protocol;
360
+
361
+ var port = urlObject.port || 1985;
362
+ if (schema === 'https:') {
363
+ port = urlObject.port || 443;
364
+ }
365
+
366
+ // @see https://github.com/rtcdn/rtcdn-draft
367
+ var api = urlObject.user_query.play || self.__internal.defaultPath;
368
+ if (api.lastIndexOf('/') !== api.length - 1) {
369
+ api += '/';
370
+ }
371
+
372
+ var apiUrl = schema + '//' + urlObject.server + ':' + port + api;
373
+ for (var key in urlObject.user_query) {
374
+ if (key !== 'api' && key !== 'play') {
375
+ apiUrl += '&' + key + '=' + urlObject.user_query[key];
376
+ }
377
+ }
378
+ // Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
379
+ apiUrl = apiUrl.replace(api + '&', api + '?');
380
+
381
+ var streamUrl = urlObject.url;
382
+
383
+ return {
384
+ apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
385
+ tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
386
+ };
387
+ },
388
+ parse: function (url) {
389
+ // @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
390
+ var a = document.createElement("a");
391
+ a.href = url.replace("rtmp://", "http://")
392
+ .replace("webrtc://", "http://")
393
+ .replace("rtc://", "http://");
394
+
395
+ var vhost = a.hostname;
396
+ var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
397
+ var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
398
+
399
+ // parse the vhost in the params of app, that srs supports.
400
+ app = app.replace("...vhost...", "?vhost=");
401
+ if (app.indexOf("?") >= 0) {
402
+ var params = app.slice(app.indexOf("?"));
403
+ app = app.slice(0, app.indexOf("?"));
404
+
405
+ if (params.indexOf("vhost=") > 0) {
406
+ vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
407
+ if (vhost.indexOf("&") > 0) {
408
+ vhost = vhost.slice(0, vhost.indexOf("&"));
409
+ }
410
+ }
411
+ }
412
+
413
+ // when vhost equals to server, and server is ip,
414
+ // the vhost is __defaultVhost__
415
+ if (a.hostname === vhost) {
416
+ var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
417
+ if (re.test(a.hostname)) {
418
+ vhost = "__defaultVhost__";
419
+ }
420
+ }
421
+
422
+ // parse the schema
423
+ var schema = "rtmp";
424
+ if (url.indexOf("://") > 0) {
425
+ schema = url.slice(0, url.indexOf("://"));
426
+ }
427
+
428
+ var port = a.port;
429
+ if (!port) {
430
+ // Finger out by webrtc url, if contains http or https port, to overwrite default 1985.
431
+ if (schema === 'webrtc' && url.indexOf(`webrtc://${a.host}:`) === 0) {
432
+ port = (url.indexOf(`webrtc://${a.host}:80`) === 0) ? 80 : 443;
433
+ }
434
+
435
+ // Guess by schema.
436
+ if (schema === 'http') {
437
+ port = 80;
438
+ } else if (schema === 'https') {
439
+ port = 443;
440
+ } else if (schema === 'rtmp') {
441
+ port = 1935;
442
+ }
443
+ }
444
+
445
+ var ret = {
446
+ url: url,
447
+ schema: schema,
448
+ server: a.hostname, port: port,
449
+ vhost: vhost, app: app, stream: stream
450
+ };
451
+ self.__internal.fill_query(a.search, ret);
452
+
453
+ // For webrtc API, we use 443 if page is https, or schema specified it.
454
+ if (!ret.port) {
455
+ if (schema === 'webrtc' || schema === 'rtc') {
456
+ if (ret.user_query.schema === 'https') {
457
+ ret.port = 443;
458
+ } else if (window.location.href.indexOf('https://') === 0) {
459
+ ret.port = 443;
460
+ } else {
461
+ // For WebRTC, SRS use 1985 as default API port.
462
+ ret.port = 1985;
463
+ }
464
+ }
465
+ }
466
+
467
+ return ret;
468
+ },
469
+ fill_query: function (query_string, obj) {
470
+ // pure user query object.
471
+ obj.user_query = {};
472
+
473
+ if (query_string.length === 0) {
474
+ return;
475
+ }
476
+
477
+ // split again for angularjs.
478
+ if (query_string.indexOf("?") >= 0) {
479
+ query_string = query_string.split("?")[1];
480
+ }
481
+
482
+ var queries = query_string.split("&");
483
+ for (var i = 0; i < queries.length; i++) {
484
+ var elem = queries[i];
485
+
486
+ var query = elem.split("=");
487
+ obj[query[0]] = query[1];
488
+ obj.user_query[query[0]] = query[1];
489
+ }
490
+
491
+ // alias domain for vhost.
492
+ if (obj.domain) {
493
+ obj.vhost = obj.domain;
494
+ }
495
+ }
496
+ };
497
+
498
+ self.pc = new RTCPeerConnection(null);
499
+
500
+ // Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
501
+ self.stream = new MediaStream();
502
+
503
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
504
+ self.pc.ontrack = function(event) {
505
+ if (self.ontrack) {
506
+ self.ontrack(event);
507
+ }
508
+ };
509
+
510
+ return self;
511
+ }
512
+
513
+ // Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
514
+ // Async-awat-prmise based SRS RTC Publisher by WHIP.
515
+ function SrsRtcWhipWhepAsync() {
516
+ var self = {};
517
+
518
+ // https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
519
+ self.constraints = {
520
+ audio: true,
521
+ video: {
522
+ width: {ideal: 320, max: 576}
523
+ }
524
+ };
525
+
526
+ // See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
527
+ // @url The WebRTC url to publish with, for example:
528
+ // http://localhost:1985/rtc/v1/whip/?app=live&stream=livestream
529
+ self.publish = async function (url) {
530
+ if (url.indexOf('/whip/') === -1) throw new Error(`invalid WHIP url ${url}`);
531
+
532
+ self.pc.addTransceiver("audio", {direction: "sendonly"});
533
+ self.pc.addTransceiver("video", {direction: "sendonly"});
534
+
535
+ if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
536
+ throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
537
+ }
538
+ var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
539
+
540
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
541
+ stream.getTracks().forEach(function (track) {
542
+ self.pc.addTrack(track);
543
+
544
+ // Notify about local track when stream is ok.
545
+ self.ontrack && self.ontrack({track: track});
546
+ });
547
+
548
+ var offer = await self.pc.createOffer();
549
+ await self.pc.setLocalDescription(offer);
550
+ const answer = await new Promise(function (resolve, reject) {
551
+ console.log("Generated offer: ", offer);
552
+
553
+ const xhr = new XMLHttpRequest();
554
+ xhr.onload = function() {
555
+ if (xhr.readyState !== xhr.DONE) return;
556
+ if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
557
+ const data = xhr.responseText;
558
+ console.log("Got answer: ", data);
559
+ return data.code ? reject(xhr) : resolve(data);
560
+ }
561
+ xhr.open('POST', url, true);
562
+ xhr.setRequestHeader('Content-type', 'application/sdp');
563
+ xhr.send(offer.sdp);
564
+ });
565
+ await self.pc.setRemoteDescription(
566
+ new RTCSessionDescription({type: 'answer', sdp: answer})
567
+ );
568
+
569
+ return self.__internal.parseId(url, offer.sdp, answer);
570
+ };
571
+
572
+ // See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
573
+ // @url The WebRTC url to play with, for example:
574
+ // http://localhost:1985/rtc/v1/whep/?app=live&stream=livestream
575
+ self.play = async function(url) {
576
+ if (url.indexOf('/whip-play/') === -1 && url.indexOf('/whep/') === -1) throw new Error(`invalid WHEP url ${url}`);
577
+
578
+ self.pc.addTransceiver("audio", {direction: "recvonly"});
579
+ self.pc.addTransceiver("video", {direction: "recvonly"});
580
+
581
+ var offer = await self.pc.createOffer();
582
+ await self.pc.setLocalDescription(offer);
583
+ const answer = await new Promise(function(resolve, reject) {
584
+ console.log("Generated offer: ", offer);
585
+
586
+ const xhr = new XMLHttpRequest();
587
+ xhr.onload = function() {
588
+ if (xhr.readyState !== xhr.DONE) return;
589
+ if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
590
+ const data = xhr.responseText;
591
+ console.log("Got answer: ", data);
592
+ return data.code ? reject(xhr) : resolve(data);
593
+ }
594
+ xhr.open('POST', url, true);
595
+ xhr.setRequestHeader('Content-type', 'application/sdp');
596
+ xhr.send(offer.sdp);
597
+ });
598
+ await self.pc.setRemoteDescription(
599
+ new RTCSessionDescription({type: 'answer', sdp: answer})
600
+ );
601
+
602
+ return self.__internal.parseId(url, offer.sdp, answer);
603
+ };
604
+
605
+ // Close the publisher.
606
+ self.close = function () {
607
+ self.pc && self.pc.close();
608
+ self.pc = null;
609
+ };
610
+
611
+ // The callback when got local stream.
612
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
613
+ self.ontrack = function (event) {
614
+ // Add track to stream of SDK.
615
+ self.stream.addTrack(event.track);
616
+ };
617
+
618
+ self.pc = new RTCPeerConnection(null);
619
+
620
+ // To keep api consistent between player and publisher.
621
+ // @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
622
+ // @see https://webrtc.org/getting-started/media-devices
623
+ self.stream = new MediaStream();
624
+
625
+ // Internal APIs.
626
+ self.__internal = {
627
+ parseId: (url, offer, answer) => {
628
+ let sessionid = offer.substr(offer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
629
+ sessionid = sessionid.substr(0, sessionid.indexOf('\n') - 1) + ':';
630
+ sessionid += answer.substr(answer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
631
+ sessionid = sessionid.substr(0, sessionid.indexOf('\n'));
632
+
633
+ const a = document.createElement("a");
634
+ a.href = url;
635
+ return {
636
+ sessionid: sessionid, // Should be ice-ufrag of answer:offer.
637
+ simulator: a.protocol + '//' + a.host + '/rtc/v1/nack/',
638
+ };
639
+ },
640
+ };
641
+
642
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
643
+ self.pc.ontrack = function(event) {
644
+ if (self.ontrack) {
645
+ self.ontrack(event);
646
+ }
647
+ };
648
+
649
+ return self;
650
+ }
651
+
652
+ // Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
653
+ // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
654
+ function SrsRtcFormatSenders(senders, kind) {
655
+ var codecs = [];
656
+ senders.forEach(function (sender) {
657
+ var params = sender.getParameters();
658
+ params && params.codecs && params.codecs.forEach(function(c) {
659
+ if (kind && sender.track.kind !== kind) {
660
+ return;
661
+ }
662
+
663
+ if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
664
+ return;
665
+ }
666
+
667
+ var s = '';
668
+
669
+ s += c.mimeType.replace('audio/', '').replace('video/', '');
670
+ s += ', ' + c.clockRate + 'HZ';
671
+ if (sender.track.kind === "audio") {
672
+ s += ', channels: ' + c.channels;
673
+ }
674
+ s += ', pt: ' + c.payloadType;
675
+
676
+ codecs.push(s);
677
+ });
678
+ });
679
+ return codecs.join(", ");
680
+ }
681
+