Last active
February 16, 2023 16:12
-
-
Save Sean-Der/9d0c78041a839809c27a9400a918b154 to your computer and use it in GitHub Desktop.
Example for metal3d
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<video id="video"> </video> | |
<br /> | |
<button onclick="start()">Call start</button> | |
<script> | |
function E(selector) { | |
return document.querySelector(selector); | |
} | |
function createPeerConnection() { | |
// create a connection and set received track to the video element | |
let config = { | |
sdpSemantics: 'unified-plan' | |
}; | |
let pc = new RTCPeerConnection(config); | |
pc.ontrack = function (event) { | |
if (event.track.kind !== "video") { | |
return | |
} | |
console.log("TRACK"); | |
var el = E('#video') | |
el.srcObject = event.streams[0] | |
el.autoplay = true | |
el.controls = true | |
} | |
return pc | |
} | |
function start() { | |
let pc = createPeerConnection(); | |
// when captured webcam, add the streams to the peer connection to be sent | |
// to the server | |
navigator.mediaDevices.getUserMedia({ | |
audio: false, | |
video: true, | |
}).then(stream => { | |
console.log("Add track"); | |
stream.getTracks().forEach(track => { | |
pc.addTrack(track, stream) | |
}); | |
// we can now try to negociate | |
// connection on server | |
return negociate(pc); | |
}, (err) => { | |
console.error(err); | |
}); | |
} | |
function negociate(pc) { | |
// create an offer... | |
pc.createOffer().then((offer) => { | |
// .. then add offer to local description | |
return pc.setLocalDescription(offer); | |
}).then(() => { | |
// we need to wait that ICE Gathering is complete before to send offer to | |
// our server. | |
return new Promise(resolve => { | |
// add event listener on ice gathering state only if it's not complete. | |
if (pc.iceGatheringState === 'complete') { | |
resolve(); | |
} else { | |
let waitComplete = () => { | |
if (pc.iceGatheringState === 'complete') { | |
pc.removeEventListener('icegatheringstatechange', waitComplete); | |
resolve(); | |
} | |
} | |
pc.addEventListener('icegatheringstatechange', waitComplete); | |
} | |
}); | |
}).then(() => { | |
// now that Ice Gathering is complete, we can send the offer | |
// to our live streaming server. | |
let offer = pc.localDescription; | |
// "fetch()" is wonderful, really ! | |
return fetch('/offer', { | |
method: 'POST', | |
body: JSON.stringify({ | |
sdp: offer.sdp, | |
type: offer.type, | |
}), | |
headers: { | |
'Content-Type': 'application/json', | |
} | |
}); | |
}).then(response => { | |
return response.json(); // this is a Promise | |
}).then(answer => { | |
// we received an anwser (resolved fetch response) that is | |
// the sdp answer from server. The negociation seems to be OK, so | |
// we add it to our RTCPeerConnection object as "remote description". | |
return pc.setRemoteDescription(answer); | |
}).catch(error => { | |
console.error("Error in negociation", error); | |
}); | |
} | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"math/rand" | |
"time" | |
"github.com/gin-contrib/static" | |
"github.com/gin-gonic/gin" | |
"github.com/pion/rtcp" | |
"github.com/pion/webrtc/v2" | |
) | |
func manage(offer webrtc.SessionDescription, answer chan<- *webrtc.SessionDescription) { | |
pc, err := webrtc.NewPeerConnection(webrtc.Configuration{}) | |
if err != nil { | |
log.Panic(err) | |
} | |
ssrc := rand.Uint32() | |
fmt.Println(ssrc) | |
outputTrack, err := pc.NewTrack(webrtc.DefaultPayloadTypeVP8, ssrc, "video", "pion") | |
if err != nil { | |
log.Panic(err) | |
} | |
if _, err = pc.AddTrack(outputTrack); err != nil { | |
log.Panic(err) | |
} | |
pc.OnTrack(func(t *webrtc.Track, receiver *webrtc.RTPReceiver) { | |
go func() { | |
ticker := time.NewTicker(time.Second * 3) | |
for range ticker.C { | |
errSend := pc.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: t.SSRC()}}) | |
if errSend != nil { | |
fmt.Println(errSend) | |
} | |
} | |
}() | |
log.Println("Track acquired", t.Kind(), t.Codec()) | |
for { | |
rtp, err := t.ReadRTP() | |
if err != nil { | |
log.Panic(err) | |
} | |
rtp.SSRC = ssrc | |
rtp.PayloadType = webrtc.DefaultPayloadTypeVP8 | |
if err := outputTrack.WriteRTP(rtp); err != nil { | |
log.Panic(err) | |
} | |
} | |
}) | |
pc.OnICEConnectionStateChange(func(cs webrtc.ICEConnectionState) { | |
log.Println("Ice connection changed to ", cs.String()) | |
if cs == webrtc.ICEConnectionStateFailed { | |
log.Println("Closing peer connection as ICE connection failed") | |
pc.Close() | |
} | |
}) | |
if err := pc.SetRemoteDescription(offer); err != nil { | |
log.Panic("Set remote description failed:: ", err) | |
} | |
if a, err := pc.CreateAnswer(nil); err != nil { | |
log.Panic(err) | |
} else { | |
pc.SetLocalDescription(a) | |
answer <- pc.LocalDescription() | |
} | |
select {} // I do it for test, commenting this line does nothing | |
} | |
// Offer get client offer information and give back an answer. | |
func Offer(c *gin.Context) { | |
offer := webrtc.SessionDescription{} | |
c.BindJSON(&offer) | |
log.Println("Get offer", offer.Type) | |
answer := make(chan *webrtc.SessionDescription) | |
go manage(offer, answer) | |
c.JSON(200, <-answer) | |
} | |
func main() { | |
r := gin.Default() | |
r.Use(static.Serve("/", static.LocalFile(".", false))) | |
r.POST("/offer", Offer) | |
r.Run(":8080") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment