-
-
Save legokichi/75c74ede4d66590763a32a31208a5551 to your computer and use it in GitHub Desktop.
<script> | |
"use strict"; | |
async function captureDesktop() { | |
const localStream = await navigator.mediaDevices.getDisplayMedia({ | |
video: { | |
frameRate: 15, | |
displaySurface: "monitor", | |
}, | |
}); | |
consolelog("make Offer"); | |
const peer = new RTCPeerConnection({ iceServers: [] }); | |
peer.onsignalingstatechange = () => { | |
consolelog("onsignalingstatechange", peer.signalingState); | |
}; | |
peer.onicecandidateerror = (evt) => { | |
consolelog("onicecandidateerror", evt); | |
}; | |
peer.onsignalingstatechange = () => { | |
consolelog("onsignalingstatechange", peer.signalingState); | |
}; | |
peer.oniceconnectionstatechange = () => { | |
consolelog("oniceconnectionstatechange", peer.iceConnectionState); | |
}; | |
peer.onicegatheringstatechange = () => { | |
consolelog("onicegatheringstatechange", peer.iceGatheringState); | |
}; | |
peer.onconnectionstatechange = () => { | |
consolelog("onconnectionstatechange", peer.connectionState); | |
}; | |
peer.ontrack = (event) => { | |
consolelog("ontrack", event); | |
}; | |
peer.onremovestream = (event) => { | |
consolelog("onremovestream", event); | |
}; | |
consolelog("Adding local stream..."); | |
peer.addStream(localStream); | |
const localDescription = await peer.createOffer(); | |
consolelog("createOffer() succsess in promise"); | |
await peer.setLocalDescription(localDescription); | |
consolelog("setLocalDescription() succsess in promise"); | |
await new Promise((resolve) => { | |
peer.onicecandidate = (evt) => { | |
consolelog("onicecandidate", evt.candidate); | |
if (evt.candidate != null) { | |
// Trickle ICE の場合は、ICE candidateを相手に送る | |
// Vanilla ICE の場合には、何もしない | |
} else { | |
// Trickle ICE の場合は、何もしない | |
// Vanilla ICE の場合には、ICE candidateを含んだSDPを相手に送る | |
resolve(); | |
} | |
}; | |
}); | |
const localSdpText = peer.localDescription.sdp | |
consolelog("---sending sdp ---"); | |
// consolelog(localSdpText); | |
const textarea = document.createElement("textarea"); | |
textarea.value = localSdpText; | |
textarea.readonly = true; | |
textarea.onclick = async () => { | |
textarea.focus(); | |
textarea.select(); | |
await navigator.clipboard.writeText(localSdpText); | |
}; | |
document.body.appendChild(textarea); | |
const textarea2 = document.createElement("textarea"); | |
textarea2.placeholder = "paste remote sdp here"; | |
textarea2.onclick = async () => { | |
textarea2.value = await navigator.clipboard.readText(); | |
textarea2.focus(); | |
textarea2.select(); | |
}; | |
document.body.appendChild(textarea2); | |
const button = document.createElement("button"); | |
button.appendChild(document.createTextNode("Receive Offer")); | |
document.body.appendChild(button); | |
await new Promise((resolve) => { button.onclick = resolve; }); | |
let remoteSdpText = textarea2.value; | |
if (remoteSdpText.slice(-1) !== "\n") { | |
remoteSdpText += "\n"; | |
} | |
consolelog("Received answer text..."); | |
consolelog(remoteSdpText); | |
document.body.removeChild(button); | |
document.body.removeChild(textarea); | |
document.body.removeChild(textarea2); | |
const answerSdp = new RTCSessionDescription({ | |
type: "answer", | |
sdp: remoteSdpText, | |
}); | |
await peer.setRemoteDescription(answerSdp); | |
consolelog("setRemoteDescription(answer) succsess in promise"); | |
await new Promise((resolve) => { | |
peer.onconnectionstatechange = () => { | |
consolelog("onconnectionstatechange", peer.iceConnectionState); | |
if (peer.connectionState === "disconnected") { | |
resolve(); | |
} | |
}; | |
}); | |
consolelog("Hang up."); | |
peer.close(); | |
} | |
async function receiveDesktop() { | |
const textarea = document.createElement("textarea"); | |
textarea.placeholder = "paste remote sdp here"; | |
textarea.onclick = async () => { | |
textarea.value = await navigator.clipboard.readText(); | |
textarea.focus(); | |
textarea.select(); | |
}; | |
document.body.appendChild(textarea); | |
const button = document.createElement("button"); | |
button.appendChild(document.createTextNode("Receive Offer")); | |
document.body.appendChild(button); | |
await new Promise((resolve) => { button.onclick = resolve; }); | |
let remoteSdpText = textarea.value; | |
if (remoteSdpText.slice(-1) !== "\n") { | |
remoteSdpText += "\n"; | |
} | |
consolelog("Received offer text..."); | |
// consolelog(remoteSdpText); | |
document.body.removeChild(textarea); | |
document.body.removeChild(button); | |
const offerSdp = new RTCSessionDescription({ | |
type: "offer", | |
sdp: remoteSdpText, | |
}); | |
const peer = new RTCPeerConnection({ iceServers: [] }); | |
peer.onsignalingstatechange = () => { | |
consolelog("onsignalingstatechange", peer.signalingState); | |
}; | |
peer.onicecandidateerror = (evt) => { | |
consolelog("onicecandidateerror", evt); | |
}; | |
peer.oniceconnectionstatechange = () => { | |
consolelog("oniceconnectionstatechange", peer.iceConnectionState); | |
}; | |
peer.onicegatheringstatechange = () => { | |
consolelog("onicegatheringstatechange", peer.iceGatheringState); | |
}; | |
peer.onconnectionstatechange = () => { | |
consolelog("onconnectionstatechange", peer.connectionState); | |
}; | |
const video = document.createElement("video"); | |
peer.ontrack = (event) => { | |
consolelog("ontrack", event); | |
const remoteStream = event.streams[0]; | |
video.muted = true; | |
video.volume = 0; | |
video.autoplay = true; | |
video.playsinline = true; | |
video.controls = false; | |
video.srcObject = remoteStream; | |
}; | |
peer.onremovestream = (event) => { | |
consolelog("onremovestream", event); | |
video.pause(); | |
video.srcObject = null; | |
}; | |
await peer.setRemoteDescription(offerSdp); | |
consolelog("setRemoteDescription(offer) succsess in promise"); | |
consolelog("sending Answer. Creating remote session description..."); | |
const localDescription = await peer.createAnswer(); | |
consolelog("createAnswer() succsess in promise"); | |
await peer.setLocalDescription(localDescription); | |
consolelog("setLocalDescription() succsess in promise"); | |
// -- Trickle ICE の場合は、初期SDPを相手に送る -- | |
// -- Vanilla ICE の場合には、まだSDPは送らない -- | |
//sendSdp(peerConnection.localDescription); | |
await new Promise((resolve) => { | |
peer.onicecandidate = function (evt) { | |
consolelog("onicecandidate", evt.candidate); | |
if (evt.candidate != null) { | |
// Trickle ICE の場合は、ICE candidateを相手に送る | |
// Vanilla ICE の場合には、何もしない | |
} else { | |
// Trickle ICE の場合は、何もしない | |
// Vanilla ICE の場合には、ICE candidateを含んだSDPを相手に送る | |
resolve(); | |
} | |
}; | |
}); | |
const localSdpText = peer.localDescription.sdp; | |
consolelog("---sending sdp ---"); | |
// consolelog(localSdpText); | |
const textarea2 = document.createElement("textarea"); | |
textarea2.value = localSdpText; | |
textarea2.readonly = true; | |
textarea2.onclick = async () => { | |
textarea2.focus(); | |
textarea2.select(); | |
await navigator.clipboard.writeText(localSdpText); | |
}; | |
document.body.appendChild(textarea2); | |
await new Promise((resolve) => { | |
peer.onconnectionstatechange = () => { | |
consolelog("onconnectionstatechange", peer.iceConnectionState); | |
if (peer.connectionState === "connected") { | |
resolve(); | |
} | |
}; | |
}); | |
document.body.removeChild(textarea2); | |
document.body.appendChild(video); | |
video.play(); | |
await new Promise((resolve) => { | |
peer.onconnectionstatechange = () => { | |
consolelog("onconnectionstatechange", peer.iceConnectionState); | |
if (peer.connectionState === "disconnected") { | |
resolve(); | |
} | |
}; | |
}); | |
consolelog("Hang up."); | |
peer.close(); | |
video.pause(); | |
video.srcObject = null; | |
document.body.removeChild(video); | |
} | |
const logarea = document.createElement("textarea"); | |
function consolelog(...args) { | |
console.log(...args); | |
logarea.appendChild(document.createTextNode(args.join(" ") + "\n")); | |
} | |
window.onerror = (message, source, lineno, colno, error) => { | |
consolelog(message, source, lineno, colno, error); | |
}; | |
window.addEventListener("unhandledrejection", (evt) => { | |
consolelog(["onUnhandledrejection", evt.reason.stack].join("\n--------\n")); | |
}); | |
document.addEventListener("DOMContentLoaded", (ev) => { | |
logarea.style = "display: block; width: 100%; height: 200px;"; | |
logarea.readonly = true; | |
// logarea.onclick = async () => { | |
// logarea.focus(); | |
// logarea.select(); | |
// await navigator.clipboard.writeText(logarea.value); | |
// }; | |
document.body.appendChild(logarea); | |
const button1 = document.createElement("button"); | |
const button2 = document.createElement("button"); | |
button1.appendChild(document.createTextNode("captureDesktop")); | |
button2.appendChild(document.createTextNode("receiveDesktop")); | |
button1.onclick = () => { | |
document.body.removeChild(button1); | |
document.body.removeChild(button2); | |
captureDesktop(); | |
}; | |
button2.onclick = () => { | |
document.body.removeChild(button1); | |
document.body.removeChild(button2); | |
receiveDesktop(); | |
}; | |
document.body.appendChild(button1); | |
document.body.appendChild(button2); | |
}); | |
</script> | |
<body> | |
</body> |
legokichi
commented
Mar 25, 2024
•
v=0
o=- 2894475170694576761 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=extmap-allow-mixed
a=msid-semantic: WMS
m=video 42863 UDP/TLS/RTP/SAVPF 96 97 102 103 104 105 106 107 108 109 127 125 39 40 45 46 98 99 100 101 112 113 114
c=IN IP4 192.168.249.54
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:117327436 1 udp 2113937151 192.168.249.54 42863 typ host generation 0 network-cost 999
a=ice-ufrag:TpRA
a=ice-pwd:/EFJHqGJTil8s7ErV8SFvkyW
a=ice-options:trickle
a=fingerprint:sha-256 44:F9:C0:E4:79:4F:63:59:8A:41:34:33:AB:EB:69:BE:7A:5E:F9:37:22:E5:8E:E7:7F:89:8B:AA:6E:7D:83:B9
a=setup:active
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 urn:3gpp:video-orientation
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:103 rtx/90000
a=fmtp:103 apt=102
a=rtpmap:104 H264/90000
a=rtcp-fb:104 goog-remb
a=rtcp-fb:104 transport-cc
a=rtcp-fb:104 ccm fir
a=rtcp-fb:104 nack
a=rtcp-fb:104 nack pli
a=fmtp:104 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:105 rtx/90000
a=fmtp:105 apt=104
a=rtpmap:106 H264/90000
a=rtcp-fb:106 goog-remb
a=rtcp-fb:106 transport-cc
a=rtcp-fb:106 ccm fir
a=rtcp-fb:106 nack
a=rtcp-fb:106 nack pli
a=fmtp:106 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=106
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:125 rtx/90000
a=fmtp:125 apt=127
a=rtpmap:39 H264/90000
a=rtcp-fb:39 goog-remb
a=rtcp-fb:39 transport-cc
a=rtcp-fb:39 ccm fir
a=rtcp-fb:39 nack
a=rtcp-fb:39 nack pli
a=fmtp:39 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:40 rtx/90000
a=fmtp:40 apt=39
a=rtpmap:45 AV1/90000
a=rtcp-fb:45 goog-remb
a=rtcp-fb:45 transport-cc
a=rtcp-fb:45 ccm fir
a=rtcp-fb:45 nack
a=rtcp-fb:45 nack pli
a=rtpmap:46 rtx/90000
a=fmtp:46 apt=45
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:112 red/90000
a=rtpmap:113 rtx/90000
a=fmtp:113 apt=112
a=rtpmap:114 ulpfec/90000