webrtc-streamer 0.8.9 → 0.8.10
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.
- package/.vscode/launch.json +1 -1
- package/README.md +7 -1
- package/h265.mkv +0 -0
- package/html/index.html +8 -6
- package/html/webrtcstreamer.js +79 -22
- package/package.json +1 -1
package/.vscode/launch.json
CHANGED
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
[](https://webrtcstreamer.agreeabletree-365b9a90.canadacentral.azurecontainerapps.io/)
|
|
18
18
|
[](https://gitpod.io/#https://github.com/mpromonet/webrtc-streamer)
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
Experimentation to stream WebRTC media sources like capture devices, screen capture, mkv files and RMTP/RTSP sources using simple signaling mechanism (see [api](docs/api.md)). It is also compatible with [WHEP](https://datatracker.ietf.org/doc/html/draft-ietf-wish-whep-01) interface.
|
|
21
22
|
|
|
22
23
|
## Artefacts
|
|
@@ -136,7 +137,7 @@ The container entry point is the webrtc-streamer application, then you can:
|
|
|
136
137
|
- run the container giving config.json file:
|
|
137
138
|
|
|
138
139
|
```sh
|
|
139
|
-
docker run -p 8000:8000 -v $PWD/config.json:/
|
|
140
|
+
docker run -p 8000:8000 -v $PWD/config.json:/usr/local/share/webrtc-streamer/config.json mpromonet/webrtc-streamer
|
|
140
141
|
```
|
|
141
142
|
|
|
142
143
|
## Using embedded STUN/TURN server behind a NAT
|
|
@@ -395,3 +396,8 @@ following architectures:
|
|
|
395
396
|
- arm64 crosscompiled
|
|
396
397
|
- Windows x64 build with clang
|
|
397
398
|
- MacOS
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
## Star History
|
|
402
|
+
|
|
403
|
+
[](https://star-history.com/#mpromonet/webrtc-streamer&Date)
|
package/h265.mkv
ADDED
|
Binary file
|
package/html/index.html
CHANGED
|
@@ -10,21 +10,23 @@
|
|
|
10
10
|
// ------------------------------------------
|
|
11
11
|
// WebRTC connections
|
|
12
12
|
// ------------------------------------------
|
|
13
|
-
|
|
13
|
+
let webRtcServerList = {};
|
|
14
14
|
|
|
15
15
|
// ------------------------------------------
|
|
16
16
|
// decode URL arguments
|
|
17
17
|
// ------------------------------------------
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let argurl = { video:location.search.slice(1) };
|
|
19
|
+
let argoptions = null;
|
|
20
|
+
let prefmime = null;
|
|
21
|
+
let layout = null;
|
|
21
22
|
if (typeof URLSearchParams != 'undefined') {
|
|
22
|
-
|
|
23
|
+
let params = new URLSearchParams(location.search);
|
|
23
24
|
argurl = { video:params.get("video"), audio:params.get("audio") };
|
|
24
25
|
argoptions = params.get("options");
|
|
25
26
|
if (!argoptions) {
|
|
26
27
|
argoptions = webrtcConfig.options;
|
|
27
28
|
}
|
|
29
|
+
prefmime = params.get("codec");
|
|
28
30
|
layout = params.get("layout");
|
|
29
31
|
} else {
|
|
30
32
|
console.log("URLSearchParams not supported then no argument could be used");
|
|
@@ -195,7 +197,7 @@
|
|
|
195
197
|
options += webrtcConfig.layoutextraoptions;
|
|
196
198
|
}
|
|
197
199
|
|
|
198
|
-
webRtcServer.connect(url.video, url.audio, options);
|
|
200
|
+
webRtcServer.connect(url.video, url.audio, options, null, prefmime);
|
|
199
201
|
|
|
200
202
|
// highlight the navigation
|
|
201
203
|
var navElt = document.getElementById ("nav_" + videoTag);
|
package/html/webrtcstreamer.js
CHANGED
|
@@ -80,6 +80,65 @@ WebRtcStreamer.prototype.disconnect = function() {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
WebRtcStreamer.prototype.filterPreferredCodec = function(sdp, prefmime) {
|
|
84
|
+
const lines = sdp.split('\n');
|
|
85
|
+
const [prefkind, prefcodec] = prefmime.toLowerCase().split('/');
|
|
86
|
+
let currentMediaType = null;
|
|
87
|
+
let sdpSections = [];
|
|
88
|
+
let currentSection = [];
|
|
89
|
+
|
|
90
|
+
// Group lines into sections
|
|
91
|
+
lines.forEach(line => {
|
|
92
|
+
if (line.startsWith('m=')) {
|
|
93
|
+
if (currentSection.length) {
|
|
94
|
+
sdpSections.push(currentSection);
|
|
95
|
+
}
|
|
96
|
+
currentSection = [line];
|
|
97
|
+
} else {
|
|
98
|
+
currentSection.push(line);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
sdpSections.push(currentSection);
|
|
102
|
+
|
|
103
|
+
// Process each section
|
|
104
|
+
const processedSections = sdpSections.map(section => {
|
|
105
|
+
const firstLine = section[0];
|
|
106
|
+
if (!firstLine.startsWith('m=' + prefkind)) {
|
|
107
|
+
return section.join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Get payload types for preferred codec
|
|
111
|
+
const rtpLines = section.filter(line => line.startsWith('a=rtpmap:'));
|
|
112
|
+
const preferredPayloads = rtpLines
|
|
113
|
+
.filter(line => line.toLowerCase().includes(prefcodec))
|
|
114
|
+
.map(line => line.split(':')[1].split(' ')[0]);
|
|
115
|
+
|
|
116
|
+
if (preferredPayloads.length === 0) {
|
|
117
|
+
return section.join('\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Modify m= line to only include preferred payloads
|
|
121
|
+
const mLine = firstLine.split(' ');
|
|
122
|
+
const newMLine = [...mLine.slice(0,3), ...preferredPayloads].join(' ');
|
|
123
|
+
|
|
124
|
+
// Filter related attributes
|
|
125
|
+
const filteredLines = section.filter(line => {
|
|
126
|
+
if (line === firstLine) return false;
|
|
127
|
+
if (line.startsWith('a=rtpmap:')) {
|
|
128
|
+
return preferredPayloads.some(payload => line.startsWith(`a=rtpmap:${payload}`));
|
|
129
|
+
}
|
|
130
|
+
if (line.startsWith('a=fmtp:') || line.startsWith('a=rtcp-fb:')) {
|
|
131
|
+
return preferredPayloads.some(payload => line.startsWith(`a=${line.split(':')[0].split('a=')[1]}:${payload}`));
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return [newMLine, ...filteredLines].join('\n');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return processedSections.join('\n');
|
|
140
|
+
}
|
|
141
|
+
|
|
83
142
|
/*
|
|
84
143
|
* GetIceServers callback
|
|
85
144
|
*/
|
|
@@ -89,7 +148,7 @@ WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl,
|
|
|
89
148
|
try {
|
|
90
149
|
this.createPeerConnection();
|
|
91
150
|
|
|
92
|
-
|
|
151
|
+
let callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl);
|
|
93
152
|
if (audiourl) {
|
|
94
153
|
callurl += "&audiourl="+encodeURIComponent(audiourl);
|
|
95
154
|
}
|
|
@@ -102,29 +161,27 @@ WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl,
|
|
|
102
161
|
}
|
|
103
162
|
|
|
104
163
|
// clear early candidates
|
|
105
|
-
this.earlyCandidates.length = 0;
|
|
106
|
-
|
|
164
|
+
this.earlyCandidates.length = 0;
|
|
165
|
+
|
|
107
166
|
// create Offer
|
|
108
167
|
this.pc.createOffer(this.mediaConstraints).then((sessionDescription) => {
|
|
109
168
|
console.log("Create offer:" + JSON.stringify(sessionDescription));
|
|
110
169
|
|
|
111
170
|
console.log(`video codecs:${Array.from(new Set(RTCRtpReceiver.getCapabilities("video")?.codecs?.map(codec => codec.mimeType)))}`)
|
|
112
171
|
console.log(`audio codecs:${Array.from(new Set(RTCRtpReceiver.getCapabilities("audio")?.codecs?.map(codec => codec.mimeType)))}`)
|
|
113
|
-
|
|
172
|
+
|
|
114
173
|
if (prefmime != undefined) {
|
|
115
174
|
//set prefered codec
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
});
|
|
175
|
+
let [prefkind] = prefmime.split('/');
|
|
176
|
+
if (prefkind != "video" && prefkind != "audio") {
|
|
177
|
+
prefkind = "video";
|
|
178
|
+
prefmime = prefkind + "/" + prefmime;
|
|
179
|
+
}
|
|
180
|
+
console.log("sdp:" + sessionDescription.sdp);
|
|
181
|
+
sessionDescription.sdp = this.filterPreferredCodec(sessionDescription.sdp, prefmime);
|
|
182
|
+
console.log("sdp:" + sessionDescription.sdp);
|
|
126
183
|
}
|
|
127
|
-
|
|
184
|
+
|
|
128
185
|
|
|
129
186
|
this.pc.setLocalDescription(sessionDescription)
|
|
130
187
|
.then(() => {
|
|
@@ -164,7 +221,7 @@ WebRtcStreamer.prototype.getIceCandidate = function() {
|
|
|
164
221
|
WebRtcStreamer.prototype.createPeerConnection = function() {
|
|
165
222
|
console.log("createPeerConnection config: " + JSON.stringify(this.pcConfig));
|
|
166
223
|
this.pc = new RTCPeerConnection(this.pcConfig);
|
|
167
|
-
|
|
224
|
+
let pc = this.pc;
|
|
168
225
|
pc.peerid = Math.random();
|
|
169
226
|
|
|
170
227
|
pc.onicecandidate = (evt) => this.onIceCandidate(evt);
|
|
@@ -198,7 +255,7 @@ WebRtcStreamer.prototype.createPeerConnection = function() {
|
|
|
198
255
|
}
|
|
199
256
|
|
|
200
257
|
try {
|
|
201
|
-
|
|
258
|
+
let dataChannel = pc.createDataChannel("ClientDataChannel");
|
|
202
259
|
dataChannel.onopen = function() {
|
|
203
260
|
console.log("local datachannel open");
|
|
204
261
|
this.send("local channel openned");
|
|
@@ -247,7 +304,7 @@ WebRtcStreamer.prototype.onAddStream = function(event) {
|
|
|
247
304
|
console.log("Remote track added:" + JSON.stringify(event));
|
|
248
305
|
|
|
249
306
|
this.videoElement.srcObject = event.stream;
|
|
250
|
-
|
|
307
|
+
let promise = this.videoElement.play();
|
|
251
308
|
if (promise !== undefined) {
|
|
252
309
|
promise.catch((error) => {
|
|
253
310
|
console.warn("error:"+error);
|
|
@@ -262,11 +319,11 @@ WebRtcStreamer.prototype.onAddStream = function(event) {
|
|
|
262
319
|
WebRtcStreamer.prototype.onReceiveCall = function(dataJson) {
|
|
263
320
|
|
|
264
321
|
console.log("offer: " + JSON.stringify(dataJson));
|
|
265
|
-
|
|
322
|
+
let descr = new RTCSessionDescription(dataJson);
|
|
266
323
|
this.pc.setRemoteDescription(descr).then(() => {
|
|
267
324
|
console.log ("setRemoteDescription ok");
|
|
268
325
|
while (this.earlyCandidates.length) {
|
|
269
|
-
|
|
326
|
+
let candidate = this.earlyCandidates.shift();
|
|
270
327
|
this.addIceCandidate(this.pc.peerid, candidate);
|
|
271
328
|
}
|
|
272
329
|
|
|
@@ -283,8 +340,8 @@ WebRtcStreamer.prototype.onReceiveCall = function(dataJson) {
|
|
|
283
340
|
WebRtcStreamer.prototype.onReceiveCandidate = function(dataJson) {
|
|
284
341
|
console.log("candidate: " + JSON.stringify(dataJson));
|
|
285
342
|
if (dataJson) {
|
|
286
|
-
for (
|
|
287
|
-
|
|
343
|
+
for (let i=0; i<dataJson.length; i++) {
|
|
344
|
+
let candidate = new RTCIceCandidate(dataJson[i]);
|
|
288
345
|
|
|
289
346
|
console.log("Adding ICE candidate :" + JSON.stringify(candidate) );
|
|
290
347
|
this.pc.addIceCandidate(candidate).then( () => { console.log ("addIceCandidate OK"); }
|