mirror of
https://github.com/krishpranav/remote-desktop.git
synced 2025-09-26 20:21:10 +08:00
frontend + main functionalities
This commit is contained in:
30
Makefile
Normal file
30
Makefile
Normal 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
74
cmd/main.go
Normal 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)
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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">
|
||||
|
@@ -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();
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user