-
Notifications
You must be signed in to change notification settings - Fork 5.7k
/
main.js
119 lines (104 loc) · 3.58 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const srcVideo = document.getElementById('srcVideo');
const motionVideo = document.getElementById('motionVideo');
const detailVideo = document.getElementById('detailVideo');
let srcStream;
let motionStream;
let detailStream;
const offerOptions = {
offerToReceiveAudio: 0,
offerToReceiveVideo: 1
};
function maybeCreateStream() {
if (srcStream) {
return;
}
if (srcVideo.captureStream) {
srcStream = srcVideo.captureStream();
call();
} else {
console.log('captureStream() not supported');
}
}
// Video tag capture must be set up after video tracks are enumerated.
srcVideo.oncanplay = maybeCreateStream;
if (srcVideo.readyState >= 3) { // HAVE_FUTURE_DATA
// Video is already ready to play, call maybeCreateStream in case oncanplay
// fired before we registered the event handler.
maybeCreateStream();
}
srcVideo.play();
function setVideoTrackContentHints(stream, hint) {
const tracks = stream.getVideoTracks();
tracks.forEach(track => {
if ('contentHint' in track) {
track.contentHint = hint;
if (track.contentHint !== hint) {
console.log('Invalid video track contentHint: \'' + hint + '\'');
}
} else {
console.log('MediaStreamTrack contentHint attribute not supported');
}
});
}
function call() {
// This creates multiple independent PeerConnections instead of multiple
// streams on a single PeerConnection object so that b=AS (the bitrate
// constraints) can be applied independently.
motionStream = srcStream.clone();
// TODO(pbos): Remove fluid when no clients use it, motion is the newer name.
setVideoTrackContentHints(motionStream, 'fluid');
setVideoTrackContentHints(motionStream, 'motion');
establishPC(motionVideo, motionStream);
detailStream = srcStream.clone();
// TODO(pbos): Remove detailed when no clients use it, detail is the newer
// name.
setVideoTrackContentHints(detailStream, 'detailed');
setVideoTrackContentHints(detailStream, 'detail');
establishPC(detailVideo, detailStream);
}
function establishPC(videoTag, stream) {
const pc1 = new RTCPeerConnection(null);
const pc2 = new RTCPeerConnection(null);
pc1.onicecandidate = e => {
onIceCandidate(pc1, pc2, e);
};
pc2.onicecandidate = e => {
onIceCandidate(pc2, pc1, e);
};
pc2.ontrack = event => {
if (videoTag.srcObject !== event.streams[0]) {
videoTag.srcObject = event.streams[0];
}
};
stream.getTracks().forEach(track => pc1.addTrack(track, stream));
pc1.createOffer(offerOptions)
.then(desc => {
pc1.setLocalDescription(desc)
.then(() => pc2.setRemoteDescription(desc))
.then(() => pc2.createAnswer())
.then(answerDesc => onCreateAnswerSuccess(pc1, pc2, answerDesc))
.catch(onSetSessionDescriptionError);
})
.catch(e => console.log('Failed to create session description: ' + e.toString()));
}
function onSetSessionDescriptionError(error) {
console.log('Failed to set session description: ' + error.toString());
}
function onCreateAnswerSuccess(pc1, pc2, desc) {
// Hard-code video bitrate to 50kbps.
desc.sdp = desc.sdp.replace(/a=mid:(.*)\r\n/g, 'a=mid:$1\r\nb=AS:' + 50 + '\r\n');
pc2.setLocalDescription(desc)
.then(() => pc1.setRemoteDescription(desc))
.catch(onSetSessionDescriptionError);
}
function onIceCandidate(pc, otherPc, event) {
otherPc.addIceCandidate(event.candidate);
}