mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-10-05 23:16:53 +08:00
embed demo player
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,3 +17,5 @@ donut
|
||||
tags
|
||||
|
||||
.DS_Store
|
||||
|
||||
.vscode
|
@@ -1,10 +1,18 @@
|
||||
# Moving player to static
|
||||
|
||||
## Date: 2/3/24
|
||||
### Hypothesis: It's easy to do experimentation with local player
|
||||
### Summary
|
||||
|
||||
Copy and adapt code from https://github.com/flavioribeiro/donut-video into static/demo/
|
||||
|
||||
# Investigating a potential memory leak
|
||||
|
||||
## Date: 2/2/24
|
||||
### Hyphotesis: There's a memory leak happening
|
||||
### Hypothesis: There's a memory leak happening
|
||||
### Signs: pprof/allocs
|
||||

|
||||
### Sumary
|
||||
### Summary
|
||||
|
||||
* Start the donut `make run`,
|
||||
* Check the general profiling http://localhost:6060/debug/pprof/?debug=1
|
@@ -26,7 +26,7 @@ func NewStreamingController(c *entities.Config, l *zap.SugaredLogger) *Streaming
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StreamingController) Stream(sp entities.StreamParameters) {
|
||||
func (c *StreamingController) Stream(sp *entities.StreamParameters) {
|
||||
r, w := io.Pipe()
|
||||
|
||||
defer r.Close()
|
||||
@@ -78,7 +78,7 @@ func (c *StreamingController) Stream(sp entities.StreamParameters) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StreamingController) writeMpegtsToWebRTC(mpegTSDemuxData *astits.DemuxerData, h264PID uint16, err error, sp entities.StreamParameters, eia608Reader *EIA608Reader) error {
|
||||
func (c *StreamingController) writeMpegtsToWebRTC(mpegTSDemuxData *astits.DemuxerData, h264PID uint16, err error, sp *entities.StreamParameters, eia608Reader *EIA608Reader) error {
|
||||
if mpegTSDemuxData.PID == h264PID && mpegTSDemuxData.PES != nil {
|
||||
|
||||
if err = sp.VideoTrack.WriteSample(media.Sample{Data: mpegTSDemuxData.PES.Data, Duration: time.Second / 30}); err != nil {
|
||||
|
@@ -1,67 +0,0 @@
|
||||
/* eslint-env browser */
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: [{
|
||||
urls: 'stun:stun.l.google.com:19302'
|
||||
}]
|
||||
})
|
||||
const log = msg => {
|
||||
document.getElementById('div').innerHTML += msg + '<br>'
|
||||
}
|
||||
|
||||
pc.ontrack = function (event) {
|
||||
const el = document.createElement(event.track.kind)
|
||||
el.srcObject = event.streams[0]
|
||||
el.autoplay = true
|
||||
el.controls = true
|
||||
|
||||
document.getElementById('remoteVideos').appendChild(el)
|
||||
}
|
||||
|
||||
pc.oniceconnectionstatechange = e => log(pc.iceConnectionState)
|
||||
pc.onicecandidate = event => {
|
||||
if (event.candidate === null) {
|
||||
document.getElementById('localSessionDescription').value = btoa(JSON.stringify(pc.localDescription))
|
||||
}
|
||||
}
|
||||
|
||||
// Offer to receive 1 audio, and 1 video track
|
||||
pc.addTransceiver('video', {
|
||||
direction: 'sendrecv'
|
||||
})
|
||||
pc.addTransceiver('audio', {
|
||||
direction: 'sendrecv'
|
||||
})
|
||||
|
||||
pc.createOffer().then(d => pc.setLocalDescription(d)).catch(log)
|
||||
|
||||
window.startSession = () => {
|
||||
const sd = document.getElementById('remoteSessionDescription').value
|
||||
if (sd === '') {
|
||||
return alert('Session Description must not be empty')
|
||||
}
|
||||
|
||||
try {
|
||||
pc.setRemoteDescription(JSON.parse(atob(sd)))
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
}
|
||||
|
||||
window.copySessionDescription = () => {
|
||||
const browserSessionDescription = document.getElementById('localSessionDescription')
|
||||
|
||||
browserSessionDescription.focus()
|
||||
browserSessionDescription.select()
|
||||
|
||||
try {
|
||||
const successful = document.execCommand('copy')
|
||||
const msg = successful ? 'successful' : 'unsuccessful'
|
||||
log('Copying SessionDescription was ' + msg)
|
||||
} catch (err) {
|
||||
log('Oops, unable to copy SessionDescription ' + err)
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
Browser Session Description
|
||||
<br/>
|
||||
<textarea id="localSessionDescription" readonly="true"></textarea>
|
||||
<br/>
|
||||
|
||||
<button onclick="window.copySessionDescription()">Copy browser Session Description to clipboard</button>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
Remote Session Description
|
||||
<br/>
|
||||
<textarea id="remoteSessionDescription"></textarea>
|
||||
<br/>
|
||||
<button onclick="window.startSession()">Start Session</button>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
Video
|
||||
<br/>
|
||||
<div id="remoteVideos"></div> <br />
|
||||
|
||||
Logs
|
||||
<br/>
|
||||
<div id="div"></div>
|
@@ -1 +0,0 @@
|
||||
Initially copied from https://github.com/pion/webrtc/tree/master/examples/play-from-disk/jsfiddle
|
@@ -81,7 +81,7 @@ func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) err
|
||||
|
||||
metadataSender, err := h.webRTCController.CreateDataChannel(peer, entities.MetadataChannelID)
|
||||
if err != nil {
|
||||
h.l.Errorw("error while createing a web rtc data channel",
|
||||
h.l.Errorw("error while creating a web rtc data channel",
|
||||
"error", err,
|
||||
)
|
||||
return err
|
||||
@@ -110,7 +110,7 @@ func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) err
|
||||
return err
|
||||
}
|
||||
|
||||
go h.streamingController.Stream(entities.StreamParameters{
|
||||
go h.streamingController.Stream(&entities.StreamParameters{
|
||||
Cancel: cancel,
|
||||
Ctx: ctx,
|
||||
WebRTCConn: peer,
|
||||
|
@@ -20,8 +20,11 @@ func NewServeMux(
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("/", index)
|
||||
|
||||
fs := http.FileServer(http.Dir("./static"))
|
||||
mux.Handle("/demo/", http.StripPrefix("/demo/", fs))
|
||||
|
||||
mux.Handle("/doSignaling", setCors(errorHandler(l, signaling)))
|
||||
mux.Handle("/demo", http.FileServer(http.Dir("./demo")))
|
||||
|
||||
return mux
|
||||
}
|
||||
|
@@ -3,4 +3,5 @@ ffmpeg -hide_banner -loglevel verbose \
|
||||
-f lavfi -i sine=frequency=1000:sample_rate=44100 \
|
||||
-c:v libx264 -preset veryfast -tune zerolatency -profile:v baseline \
|
||||
-b:v 1000k -bufsize 2000k -x264opts keyint=30:min-keyint=30:scenecut=-1 \
|
||||
-c:a aac -b:a 128k \
|
||||
-f mpegts "udp://${SRT_INPUT_HOST}:${SRT_INPUT_PORT}?pkt_size=${PKT_SIZE}"
|
133
static/demo.js
Normal file
133
static/demo.js
Normal file
@@ -0,0 +1,133 @@
|
||||
// just to avoid adding dup messages
|
||||
window.metadataMessages = {}
|
||||
|
||||
window.startSession = () => {
|
||||
let srtHost = document.getElementById('srt-host').value;
|
||||
let srtPort = document.getElementById('srt-port').value;
|
||||
let srtStreamId = document.getElementById('srt-stream-id').value;
|
||||
|
||||
setupWebRTC((pc, offer) => {
|
||||
let srtFullAddress = JSON.stringify({
|
||||
"srtHost": srtHost,
|
||||
"srtPort": srtPort,
|
||||
"srtStreamId": srtStreamId,
|
||||
offer
|
||||
});
|
||||
|
||||
// sending localSDP,SRT params, and fetching remote SDP
|
||||
fetchRemoteDescription(srtFullAddress).then(remoteOffer => {
|
||||
log("receiving remote sdp offer: " + JSON.stringify(remoteOffer));
|
||||
|
||||
if (remoteOffer === undefined) {
|
||||
log("error while fetching remote");
|
||||
return;
|
||||
}
|
||||
pc.setRemoteDescription(remoteOffer);
|
||||
}).catch(log, "error");
|
||||
});
|
||||
}
|
||||
|
||||
const setupWebRTC = (setRemoteSDPfn) => {
|
||||
log("setting up web rtc");
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: [{
|
||||
urls: 'stun:stun.l.google.com:19302'
|
||||
}]
|
||||
});
|
||||
|
||||
// offer to (only) receive 1 audio, and 1 video track
|
||||
pc.addTransceiver('video', {
|
||||
direction: 'recvonly'
|
||||
});
|
||||
pc.addTransceiver('audio', {
|
||||
direction: 'recvonly'
|
||||
});
|
||||
|
||||
// once a track arrives, add it to the remoteVideos div
|
||||
// with auto play.
|
||||
pc.ontrack = function (event) {
|
||||
const el = document.createElement(event.track.kind);
|
||||
el.srcObject = event.streams[0];
|
||||
el.autoplay = true
|
||||
el.controls = true;
|
||||
el.width = "640";
|
||||
el.height = "360";
|
||||
|
||||
document.getElementById('remoteVideos').appendChild(el);
|
||||
}
|
||||
|
||||
pc.createDataChannel('metadata');
|
||||
// once the metadata arrives, add it to the metadata div
|
||||
pc.ondatachannel = (e) => {
|
||||
log("ondatachannel: " + e);
|
||||
|
||||
e.channel.onmessage = (event) => {
|
||||
let msg = JSON.parse(event.data)
|
||||
if (msg.Message in metadataMessages) {
|
||||
// avoid logging dup messages
|
||||
return;
|
||||
}
|
||||
|
||||
const el = document.createElement("p")
|
||||
el.innerText = msg.Type.padEnd(8, ' ') + ": " + msg.Message
|
||||
|
||||
let metadata = document.getElementById('metadata');
|
||||
metadata.insertBefore(el, metadata.firstChild);
|
||||
metadataMessages[msg.Message] = true;
|
||||
};
|
||||
};
|
||||
|
||||
pc.oniceconnectionstatechange = e => log("ice state change: " + pc.iceConnectionState);
|
||||
pc.onicegatheringstatechange = e => log("gathering state change: " + pc.iceGatheringState);
|
||||
pc.onsignalingstatechange = e => log("signaling state change: " + pc.signalingState);
|
||||
|
||||
// creating a local sdp offer
|
||||
pc.createOffer()
|
||||
.then(offer => {
|
||||
pc.setLocalDescription(offer);
|
||||
setRemoteSDPfn(pc, offer);
|
||||
}).catch(log, "error");
|
||||
}
|
||||
|
||||
const fetchRemoteDescription = async (bodyRequest) => {
|
||||
log("requesting remote sdp offer for: " + bodyRequest)
|
||||
|
||||
const res = await fetch('/doSignaling', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: bodyRequest
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
res.text().then(err => {
|
||||
log(err, "error");
|
||||
window.alert(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
const formattedNow = () => {
|
||||
let now = new Date();
|
||||
let minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
let seconds = now.getSeconds().toString().padStart(2, '0');
|
||||
let ms = now.getMilliseconds().toString().padStart(3, '0');
|
||||
return minutes + ":" + seconds + ":" + ms;
|
||||
}
|
||||
|
||||
const log = (msg, level = "info") => {
|
||||
const el = document.createElement("p")
|
||||
if (level === "error") {
|
||||
el.style = "color: red;background-color: yellow;";
|
||||
}
|
||||
|
||||
el.innerText = "[[" + level.toUpperCase().padEnd(5, ' ') + "]] " + formattedNow() + " : " + msg
|
||||
|
||||
let logEl = document.getElementById('log');
|
||||
logEl.insertBefore(el, logEl.firstChild);
|
||||
}
|
66
static/index.html
Normal file
66
static/index.html
Normal file
@@ -0,0 +1,66 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>donut</title>
|
||||
<script src="demo.js"></script>
|
||||
<style src="demo.css"></style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>SRT Config</h1>
|
||||
<b> SRT Host </b>
|
||||
<input type="text" id="srt-host" value="srt"> <br />
|
||||
|
||||
<b> SRT Port </b>
|
||||
<input type="text" id="srt-port" value="40052" /> <br />
|
||||
|
||||
<b> SRT Stream ID </b>
|
||||
<input type="text" id="srt-stream-id" value="stream-id" /> <br />
|
||||
<button onclick="onConnect()"> Connect </button>
|
||||
|
||||
<h1>Video</h1>
|
||||
<div id="remoteVideos"></div>
|
||||
|
||||
<h1>Metadata</h1>
|
||||
<div id="metadata"></div>
|
||||
|
||||
<h1>Logs</h1>
|
||||
<div id="log"></div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
function docReady(fn) {
|
||||
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||
setTimeout(fn, 1);
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", fn);
|
||||
}
|
||||
}
|
||||
|
||||
docReady(function () {
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
|
||||
window.onConnect = () => {
|
||||
window.startSession();
|
||||
}
|
||||
|
||||
if (urlParams.has('srtHost')) {
|
||||
document.getElementById('srt-host').value = urlParams.get('srtHost');
|
||||
}
|
||||
if (urlParams.has('srtPort')) {
|
||||
document.getElementById('srt-port').value = urlParams.get('srtPort');
|
||||
}
|
||||
if (urlParams.has('srtStreamId')) {
|
||||
document.getElementById('srt-stream-id').value = urlParams.get('srtStreamId');
|
||||
}
|
||||
|
||||
if (urlParams.get('autoplay') === "true") {
|
||||
onConnect();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
Reference in New Issue
Block a user