frontend + main functionalities

This commit is contained in:
Krisna Pranav
2023-04-15 18:18:10 +05:30
parent 1bb3e1f6a4
commit 4eaeb933d9
6 changed files with 262 additions and 15 deletions

30
Makefile Normal file
View File

@@ -0,0 +1,30 @@
ifndef encoders
encoders = h264
endif
tags =
ifneq (,$(findstring h264,$(encoders)))
tags = h264enc
endif
ifneq (,$(findstring vp8,$(encoders)))
tags := $(tags) vp8enc
endif
tags := $(strip $(tags))
main.tar.gz: clean main
@tar zcf main.tar.gz frontend main
main.zip: clean main
@zip -r main.zip frontend main
main:
go build -tags "$(tags)" cmd/main.go
.PHONY: clean
clean:
@if [ -f main ]; then rm main; fi
@if [ -f main.tar.gz ]; then rm main.tar.gz ; fi
@if [ -f main.zip ]; then rm main.zip ; fi

74
cmd/main.go Normal file
View File

@@ -0,0 +1,74 @@
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/krishpranav/remote-desktop/api"
"github.com/krishpranav/remote-desktop/encoders"
"github.com/krishpranav/remote-desktop/rdisplay"
"github.com/krishpranav/remote-desktop/rtc"
)
const (
httpDefaultPort = "8080"
defaultStunServer = "stun:stun.l.google.com:19302"
)
func main() {
httpPort := flag.String("http.port", httpDefaultPort, "HTTP listen port")
stunServer := flag.String("stun.server", defaultStunServer, "STUN server URL (stun:)")
flag.Parse()
var video rdisplay.Service
video, err := rdisplay.NewVideoProvider()
if err != nil {
log.Fatalf("Can't init video: %v", err)
}
_, err = video.Screens()
if err != nil {
log.Fatalf("Can't get screens: %v", err)
}
var enc encoders.Service = &encoders.EncoderService{}
if err != nil {
log.Fatalf("Can't create encoder service: %v", err)
}
var webrtc rtc.Service
webrtc = rtc.NewRemoteScreenService(*stunServer, video, enc)
mux := http.NewServeMux()
mux.Handle("/api/", http.StripPrefix("/api", api.MakeHandler(webrtc, video)))
mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("./frontend"))))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
return
}
http.ServeFile(w, r, "./frontend/index.html")
})
errors := make(chan error, 2)
go func() {
log.Printf("Starting signaling server on port %s", *httpPort)
errors <- http.ListenAndServe(fmt.Sprintf(":%s", *httpPort), mux)
}()
go func() {
interrupt := make(chan os.Signal)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
errors <- fmt.Errorf("Received %v signal", <-interrupt)
}()
err = <-errors
log.Printf("%s, exiting.", err)
}

View File

@@ -16,7 +16,7 @@ html, body {
display: flex;
justify-content: center;
align-items: center;
background-color: #303030;
background-color: #141313;
}
#controls {
@@ -36,11 +36,11 @@ button, select, option {
font-size: 2.5rem;
font-weight: 300;
color: white;
background-color:white;
background-color:rgba(59, 40, 40, 0.225);
background-image: linear-gradient(to bottom, #ffffff 0%,#efefef 100%);
padding: 4px 12px;
border: 2px solid #20639b;
color:#20639b;
border: 2px solid #810d68;
color:#a40f9d;
margin-right: 20px;
cursor: pointer;
}
@@ -50,7 +50,7 @@ select {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
background-color: #fff;
background-color: #0d16bc;
background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'),
linear-gradient(to bottom, #ffffff 0%,#efefef 100%);
background-repeat: no-repeat, repeat;
@@ -86,7 +86,7 @@ select:invalid {
align-items: center;
text-shadow: 1px 1px 3px black;
justify-content: center;
color: #acacac;
color: #ff00e1;
font-size: 3rem;
z-index: 0;
}

View File

@@ -4,11 +4,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="/frontend/css/style.css">
<link rel="stylesheet" href="/static/css/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.4.0/adapter.min.js"
integrity="sha256-UH0Npcih7yj1s23pQK0UCrCSxx7AkT91CCMsUGnZ9Ew=" crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap" rel="stylesheet">
<title>WebRTC remote viewer</title>
<title>Remote Desktop</title>
</head>
<body>
<div id="app">

View File

@@ -1,12 +1,155 @@
function showError(error) {
const errorNode = document.querySelector('#error');
if (errorNode.firstChild) {
errorNode.remoteChild(errorNode.firstChild);
errorNode.removeChild(errorNode.firstChild);
}
errorNode.appendChild(document.createTextNode(error.message || error));
}
function loadSession() {
// fetch functionality
}
}
function loadScreens() {
return fetch('/api/screens', {
method: 'GET',
headers: {
'Accepts': 'application/json'
}
}).then(res => {
return res.json();
}).catch(showError);
}
function startSession(offer, screen) {
return fetch('/api/session', {
method: 'POST',
body: JSON.stringify({
offer,
screen
}),
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
return res.json();
}).then(msg => {
return msg.answer;
});
}
function createOffer(pc, { audio, video }) {
return new Promise((accept, reject) => {
pc.onicecandidate = evt => {
if (!evt.candidate) {
// ICE Gathering finished
const { sdp: offer } = pc.localDescription;
accept(offer);
}
};
pc.createOffer({
offerToReceiveAudio: audio,
offerToReceiveVideo: video
}).then(ld => {
pc.setLocalDescription(ld)
}).catch(reject)
});
}
function startRemoteSession(screen, remoteVideoNode, stream) {
let pc;
return Promise.resolve().then(() => {
pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.ontrack = (evt) => {
console.info('ontrack triggered');
remoteVideoNode.srcObject = evt.streams[0];
remoteVideoNode.play();
};
stream && stream.getTracks().forEach(track => {
pc.addTrack(track, stream);
})
return createOffer(pc, { audio: false, video: true });
}).then(offer => {
console.info(offer);
return startSession(offer, screen);
}).then(answer => {
console.info(answer);
return pc.setRemoteDescription(new RTCSessionDescription({
sdp: answer,
type: 'answer'
}));
}).then(() => pc);
}
let peerConnection = null;
document.addEventListener('DOMContentLoaded', () => {
let selectedScreen = 0;
const remoteVideo = document.querySelector('#remote-video');
const screenSelect = document.querySelector('#screen-select');
const startStop = document.querySelector('#start-stop');
loadScreens().then(response => {
while (screenSelect.firstChild) {
screenSelect.removeChild(screenSelect.firstChild);
}
screenSelect.appendChild(document.createElement('option'));
response.screens.forEach(screen => {
const option = document.createElement('option');
option.appendChild(document.createTextNode('Screen ' + (screen.index + 1)));
option.setAttribute('value', screen.index);
screenSelect.appendChild(option);
});
}).catch(showError);
screenSelect.addEventListener('change', evt => {
selectedScreen = parseInt(evt.currentTarget.value, 10);
});
const enableStartStop = (enabled) => {
if (enabled) {
startStop.removeAttribute('disabled');
} else {
startStop.setAttribute('disabled', '');
}
}
const setStartStopTitle = (title) => {
startStop.removeChild(startStop.firstChild);
startStop.appendChild(document.createTextNode(title));
}
startStop.addEventListener('click', () => {
enableStartStop(false);
const userMediaPromise = (adapter.browserDetails.browser === 'safari') ?
navigator.mediaDevices.getUserMedia({ video: true }) :
Promise.resolve(null);
if (!peerConnection) {
userMediaPromise.then(stream => {
return startRemoteSession(selectedScreen, remoteVideo, stream).then(pc => {
remoteVideo.style.setProperty('visibility', 'visible');
peerConnection = pc;
}).catch(showError).then(() => {
enableStartStop(true);
setStartStopTitle('Stop');
});
})
} else {
peerConnection.close();
peerConnection = null;
enableStartStop(true);
setStartStopTitle('Start');
remoteVideo.style.setProperty('visibility', 'collapse');
}
});
});
window.addEventListener('beforeunload', () => {
if (peerConnection) {
peerConnection.close();
}
})

BIN
main Executable file

Binary file not shown.