choose http/ws, remove frame, add close state

This commit is contained in:
trichimtrich 2019-04-07 00:25:28 +08:00
parent e190e51330
commit 17bc3d9da3
6 changed files with 558 additions and 354 deletions

219
index_http.html Normal file
View file

@ -0,0 +1,219 @@
<html>
<style>
textarea {
width: 60%;
height: 50px;
}
</style>
<select id="gameOp">
<option value="Contra.nes">Contra.nes</option>
<option value="Kirby's Adventure.nes">Kirby's Adventure.nes</option>
<option value="Mega Man 2.nes">Mega Man 2.nes</option>
<option value="Metal Gear.nes">Metal Gear.nes</option>
<option value="Mortal Kombat 4.nes">Mortal Kombat 4.nes</option>
<option value="Super Mario Bros 2.nes">Super Mario Bros 2.nes</option>
<option value="Super Mario Bros 3.nes">Super Mario Bros 3.nes</option>
<option value="Super Mario Bros.nes">Super Mario Bros.nes</option>
<option value="Teenage Mutant Ninja Turtles 3.nes">Teenage Mutant Ninja Turtles 3.nes</option>
<option value="VS Super Mario Bros.nes">VS Super Mario Bros.nes</option>
<option value="supermariobros.rom">supermariobros.rom</option>
<option value="zelda.rom">zelda.rom</option>
</select>
<button id="playGame" onclick="window.startSession()" disabled>Play Mario</button>
<br/><br/>
<div id="remoteVideos" ></div> <br />
<h3>Instruction</h3>
<div>
Use Up, Down, Left, Right to Move <br />
Z to jump (A) <br />
X to sprint (B) <br />
C is start button <br />
V is select button <br />
<!--Fullscreen media for better gaming experience<br /-->
</div><br>
<h3>Log:</h3>
<pre id="div"></pre>
<div>
🎮<u><i>Refresh to retry when checking is too long</i></u>
</div>
<script>
// miscs
DEBUG = true;
let log = msg => {
if (DEBUG) {
document.getElementById('div').innerHTML += msg + '<br>'
console.log(msg);
}
}
var localSessionDescription = ""
var remoteSessionDescription = ""
// Register with server the session description
function postSession(session) {
if (session == "") {
return;
}
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
remoteSessionDescription = this.responseText;
document.getElementById('playGame').disabled = false;
}
};
xhttp.open("POST", "/session", true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.send(JSON.stringify({"game": gameOp.value, "sdp": session}));
}
let pc = new RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
})
const dataChannelOptions = {
ordered: false, // do not guarantee order
maxPacketLifeTime: 3000, // in milliseconds
};
let inputChannel = pc.createDataChannel('foo')
inputChannel.onclose = () => console.log('inputChannel has closed')
inputChannel.onopen = () => console.log('inputChannel has opened')
inputChannel.onmessage = e => log(`Message from DataChannel '${inputChannel.label}' payload '${e.data}'`)
pc.ontrack = function (event) {
var el = document.createElement(event.track.kind)
el.srcObject = event.streams[0]
el.autoplay = true
// el.controls = true
// el.poster = new URL("https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif");
document.getElementById('remoteVideos').appendChild(el)
}
pc.onicecandidate = event => {
if (event.candidate === null) {
var session = btoa(JSON.stringify(pc.localDescription));
localSessionDescription = session;
postSession(session)
}
}
pc.createOffer({offerToReceiveVideo: true, offerToReceiveAudio: true}).then(d => pc.setLocalDescription(d)).catch(log)
// Key controller
keyState = {
// controllers
a: false,
b: false,
start: false,
select: false,
// navigators
up: false,
down: false,
left: false,
right: false,
}
keyMap = {
37: "left",
38: "up",
39: "right",
40: "down",
90: "a",
88: "b",
67: "start",
86: "select",
}
INPUT_FPS = 100;
INPUT_STATE_PACKET = 5;
stateUnchange = true;
unchangePacket = INPUT_STATE_PACKET;
function setState(e, bo) {
if (e.keyCode in keyMap) {
keyState[keyMap[e.keyCode]] = bo;
stateUnchange = false;
unchangePacket = INPUT_STATE_PACKET;
}
}
document.body.onkeydown = function(e){
setState(e, true);
};
document.body.onkeyup = function(e){
setState(e, false);
};
window.startSession = () => {
let sd = remoteSessionDescription
if (sd === '') {
return alert('Session Description must not be empty')
}
try {
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))));
} catch (e) {
alert(e);
}
}
var timer = null;
function sendInput() {
// prepare key
/*
const (
ButtonA = iota
ButtonB
ButtonSelect
ButtonStart
ButtonUp
ButtonDown
ButtonLeft
ButtonRight
)
*/
if (stateUnchange || unchangePacket > 0) {
st = "";
["a", "b", "select", "start", "up", "down", "left", "right"].forEach(elem => {
st += keyState[elem]?1:0;
});
ss = parseInt(st, 2);
console.log(`Key state string: ${st} ==> ${ss}`);
// send
inputChannel.send(ss);
stateUnchange = false;
unchangePacket--;
}
}
function startInput() {
if (timer == null) {
timer = setInterval(sendInput, 1000 / INPUT_FPS)
}
}
function endInput() {
clearInterval(timer);
timer = null;
}
pc.oniceconnectionstatechange = e => {
log(pc.iceConnectionState);
if (pc.iceConnectionState === "connected") {
startInput();
}
else if (pc.iceConnectionState === "disconnected") {
endInput();
}
}
</script>
</html>

View file

@ -1,319 +1,283 @@
<html>
<style>
textarea {
width: 60%;
height: 50px;
}
</style>
<select id="gameOp">
<option value="Contra.nes">Contra.nes</option>
<option value="Kirby's Adventure.nes">Kirby's Adventure.nes</option>
<option value="Mega Man 2.nes">Mega Man 2.nes</option>
<option value="Metal Gear.nes">Metal Gear.nes</option>
<option value="Mortal Kombat 4.nes">Mortal Kombat 4.nes</option>
<option value="Super Mario Bros 2.nes">Super Mario Bros 2.nes</option>
<option value="Super Mario Bros 3.nes">Super Mario Bros 3.nes</option>
<option value="Super Mario Bros.nes">Super Mario Bros.nes</option>
<option value="Teenage Mutant Ninja Turtles 3.nes">Teenage Mutant Ninja Turtles 3.nes</option>
<option value="VS Super Mario Bros.nes">VS Super Mario Bros.nes</option>
<option value="supermariobros.rom">supermariobros.rom</option>
<option value="zelda.rom">zelda.rom</option>
</select>
<!--button id="playGame" onclick="window.startSession()" disabled>Play Mario</button-->
<button id="play" onclick="window.startGame()">Play</button>
<br/><br/>
<div id="remoteVideos" ></div> <br />
<h3>Instruction</h3>
<div>
Use Up, Down, Left, Right to Move <br />
Z to jump (A) <br />
X to sprint (B) <br />
C is start button <br />
V is select button <br />
<!--Fullscreen media for better gaming experience<br /-->
</div><br>
<h3>Log:</h3>
<pre id="div"></pre>
<div>
🎮<u><i>Refresh to retry when checking is too long</i></u>
</div>
<script>
// miscs
DEBUG = true;
let log = msg => {
if (DEBUG) {
document.getElementById('div').innerHTML += msg + '<br>'
console.log(msg);
}
}
// for http server
window.startSession = () => {
let sd = remoteSessionDescription
if (sd === '') {
return alert('Session Description must not be empty')
}
try {
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))));
} catch (e) {
alert(e);
}
}
function postSession(session) {
if (session == "") {
return;
}
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
remoteSessionDescription = this.responseText;
document.getElementById('playGame').disabled = false;
}
};
xhttp.open("POST", "/session", true);
xhttp.setRequestHeader("Content-type", "text/plain");
xhttp.send(session);
}
// web socket
window.startGame = () => {
// clear
endInput();
document.getElementById('div').innerHTML = "";
aa = document.getElementsByTagName("video");
for (i = 0; i < aa.length ; ++i) {
aa[i].remove();
}
// end clear
conn = new WebSocket(`ws://${location.host}/ws`);
conn.onopen = () => {
log("WebSocket is opened. Send ping");
conn.send(JSON.stringify({"id": "ping", "data": gameOp.value}));
}
conn.onerror = error => {
log(`Websocket error: ${error}`);
}
conn.onclose = () => {
log("Websocket closed");
// pc.close();
}
conn.onmessage = e => {
d = JSON.parse(e.data);
switch (d["id"]) {
case "pong":
log("Recv pong. Start webrtc");
startWebRTC();
break;
case "sdp":
log("Got remote sdp");
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(d["data"]))));
break;
}
}
// webrtc
pc = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.l.google.com:19302'}]})
// input channel
inputChannel = pc.createDataChannel('foo')
inputChannel.onclose = () => {
log('inputChannel has closed');
}
inputChannel.onopen = () => {
log('inputChannel has opened');
}
inputChannel.onmessage = e => {
log(`Message from DataChannel '${inputChannel.label}' payload '${e.data}'`);
}
pc.oniceconnectionstatechange = e => {
log(`iceConnectionState: ${pc.iceConnectionState}`);
if (pc.iceConnectionState === "connected") {
conn.send(JSON.stringify({"id": "start", "data": ""}));
startInput();
}
else if (pc.iceConnectionState === "disconnected") {
// else { // not sure about this =]
endInput();
}
}
// stream channel
pc.ontrack = function (event) {
var el = document.createElement(event.track.kind);
// console.log(event.streams);
el.srcObject = event.streams[0];
el.autoplay = true;
el.poster = new URL("https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif");
document.getElementById('remoteVideos').appendChild(el)
}
// candidate packet from STUN
pc.onicecandidate = event => {
if (event.candidate === null) {
// var session = btoa(JSON.stringify(pc.localDescription));
// localSessionDescription = session;
// postSession(session)
} else {
console.log(JSON.stringify(event.candidate));
// conn.send(JSON.stringify({"id": "candidate", "data": JSON.stringify(event.candidate)}));
}
}
}
// webrtc
var pc, inputChannel;
var localSessionDescription = "";
var remoteSessionDescription = "";
var conn;
// Input handler
keyState = {
// controllers
a: false,
b: false,
start: false,
select: false,
// navigators
up: false,
down: false,
left: false,
right: false,
}
keyMap = {
37: "left",
38: "up",
39: "right",
40: "down",
90: "a",
88: "b",
67: "start",
86: "select",
}
INPUT_FPS = 100;
INPUT_STATE_PACKET = 5;
stateUnchange = true;
unchangePacket = INPUT_STATE_PACKET;
function setState(e, bo) {
if (e.keyCode in keyMap) {
keyState[keyMap[e.keyCode]] = bo;
stateUnchange = false;
unchangePacket = INPUT_STATE_PACKET;
}
}
document.body.onkeydown = function(e){
setState(e, true);
};
document.body.onkeyup = function(e){
setState(e, false);
};
var timer = null;
function sendInput() {
// prepare key
/*
const (
ButtonA = iota
ButtonB
ButtonSelect
ButtonStart
ButtonUp
ButtonDown
ButtonLeft
ButtonRight
)
*/
if (stateUnchange || unchangePacket > 0) {
st = "";
["a", "b", "select", "start", "up", "down", "left", "right"].forEach(elem => {
st += keyState[elem]?1:0;
});
ss = parseInt(st, 2);
console.log(`Key state string: ${st} ==> ${ss}`);
// send
inputChannel.send(ss);
stateUnchange = false;
unchangePacket--;
}
}
function startInput() {
if (timer == null) {
timer = setInterval(sendInput, 1000 / INPUT_FPS)
}
}
function endInput() {
clearInterval(timer);
timer = null;
}
function startWebRTC() {
// receiver only tracks
pc.addTransceiver('video', {'direction': 'recvonly'});
// create SDP
pc.createOffer({offerToReceiveVideo: true, offerToReceiveAudio: false}).then(d => {
pc.setLocalDescription(d, () => {
// send to ws
session = btoa(JSON.stringify(pc.localDescription));
localSessionDescription = session;
log("Send SDP to remote peer");
conn.send(JSON.stringify({"id": "sdp", "data": session}));
});
}).catch(log);
}
</script>
</html>
<html>
<style>
textarea {
width: 60%;
height: 50px;
}
</style>
<select id="gameOp">
<option value="Contra.nes">Contra.nes</option>
<option value="Kirby's Adventure.nes">Kirby's Adventure.nes</option>
<option value="Mega Man 2.nes">Mega Man 2.nes</option>
<option value="Metal Gear.nes">Metal Gear.nes</option>
<option value="Mortal Kombat 4.nes">Mortal Kombat 4.nes</option>
<option value="Super Mario Bros 2.nes">Super Mario Bros 2.nes</option>
<option value="Super Mario Bros 3.nes">Super Mario Bros 3.nes</option>
<option value="Super Mario Bros.nes">Super Mario Bros.nes</option>
<option value="Teenage Mutant Ninja Turtles 3.nes">Teenage Mutant Ninja Turtles 3.nes</option>
<option value="VS Super Mario Bros.nes">VS Super Mario Bros.nes</option>
<option value="supermariobros.rom">supermariobros.rom</option>
<option value="zelda.rom">zelda.rom</option>
</select>
<button id="play" onclick="window.startGame()">Play</button>
<br/><br/>
<div id="remoteVideos" ></div> <br />
<h3>Instruction</h3>
<div>
Use Up, Down, Left, Right to Move <br />
Z to jump (A) <br />
X to sprint (B) <br />
C is start button <br />
V is select button <br />
<!--Fullscreen media for better gaming experience<br /-->
</div><br>
<h3>Log:</h3>
<pre id="div"></pre>
<div>
🎮<u><i>Refresh to retry when checking is too long</i></u>
</div>
<script>
// miscs
DEBUG = true;
let log = msg => {
if (DEBUG) {
document.getElementById('div').innerHTML += msg + '<br>'
console.log(msg);
}
}
// web socket
window.startGame = () => {
// clear
endInput();
document.getElementById('div').innerHTML = "";
aa = document.getElementsByTagName("video");
for (i = 0; i < aa.length ; ++i) {
aa[i].remove();
}
// end clear
conn = new WebSocket(`ws://${location.host}/ws`);
conn.onopen = () => {
log("WebSocket is opened. Send ping");
conn.send(JSON.stringify({"id": "ping", "data": gameOp.value}));
}
conn.onerror = error => {
log(`Websocket error: ${error}`);
}
conn.onclose = () => {
log("Websocket closed");
// pc.close();
}
conn.onmessage = e => {
d = JSON.parse(e.data);
switch (d["id"]) {
case "pong":
log("Recv pong. Start webrtc");
startWebRTC();
break;
case "sdp":
log("Got remote sdp");
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(d["data"]))));
break;
}
}
// webrtc
pc = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.l.google.com:19302'}]})
// input channel
inputChannel = pc.createDataChannel('foo')
inputChannel.onclose = () => {
log('inputChannel has closed');
}
inputChannel.onopen = () => {
log('inputChannel has opened');
}
inputChannel.onmessage = e => {
log(`Message from DataChannel '${inputChannel.label}' payload '${e.data}'`);
}
pc.oniceconnectionstatechange = e => {
log(`iceConnectionState: ${pc.iceConnectionState}`);
if (pc.iceConnectionState === "connected") {
conn.send(JSON.stringify({"id": "start", "data": ""}));
startInput();
}
else if (pc.iceConnectionState === "disconnected") {
// else { // not sure about this =]
endInput();
}
}
// stream channel
pc.ontrack = function (event) {
var el = document.createElement(event.track.kind);
// console.log(event.streams);
el.srcObject = event.streams[0];
el.autoplay = true;
el.poster = new URL("https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif");
document.getElementById('remoteVideos').appendChild(el)
}
// candidate packet from STUN
pc.onicecandidate = event => {
if (event.candidate === null) {
} else {
console.log(JSON.stringify(event.candidate));
// conn.send(JSON.stringify({"id": "candidate", "data": JSON.stringify(event.candidate)}));
}
}
}
// webrtc
var pc, inputChannel;
var localSessionDescription = "";
var remoteSessionDescription = "";
var conn;
// Input handler
keyState = {
// controllers
a: false,
b: false,
start: false,
select: false,
// navigators
up: false,
down: false,
left: false,
right: false,
}
keyMap = {
37: "left",
38: "up",
39: "right",
40: "down",
90: "a",
88: "b",
67: "start",
86: "select",
}
INPUT_FPS = 100;
INPUT_STATE_PACKET = 5;
stateUnchange = true;
unchangePacket = INPUT_STATE_PACKET;
function setState(e, bo) {
if (e.keyCode in keyMap) {
keyState[keyMap[e.keyCode]] = bo;
stateUnchange = false;
unchangePacket = INPUT_STATE_PACKET;
}
}
document.body.onkeydown = function(e){
setState(e, true);
};
document.body.onkeyup = function(e){
setState(e, false);
};
var timer = null;
function sendInput() {
// prepare key
/*
const (
ButtonA = iota
ButtonB
ButtonSelect
ButtonStart
ButtonUp
ButtonDown
ButtonLeft
ButtonRight
)
*/
if (stateUnchange || unchangePacket > 0) {
st = "";
["a", "b", "select", "start", "up", "down", "left", "right"].forEach(elem => {
st += keyState[elem]?1:0;
});
ss = parseInt(st, 2);
console.log(`Key state string: ${st} ==> ${ss}`);
// send
inputChannel.send(ss);
stateUnchange = false;
unchangePacket--;
}
}
function startInput() {
if (timer == null) {
timer = setInterval(sendInput, 1000 / INPUT_FPS)
}
}
function endInput() {
clearInterval(timer);
timer = null;
}
function startWebRTC() {
// receiver only tracks
pc.addTransceiver('video', {'direction': 'recvonly'});
// create SDP
pc.createOffer({offerToReceiveVideo: true, offerToReceiveAudio: false}).then(d => {
pc.setLocalDescription(d, () => {
// send to ws
session = btoa(JSON.stringify(pc.localDescription));
localSessionDescription = session;
log("Send SDP to remote peer");
conn.send(JSON.stringify({"id": "sdp", "data": session}));
});
}).catch(log);
}
</script>
</html>

72
main.go
View file

@ -2,19 +2,19 @@ package main
import (
pionRTC "github.com/pions/webrtc"
"os"
"fmt"
"image"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/giongto35/cloud-game/ui"
"github.com/giongto35/cloud-game/util"
"github.com/giongto35/cloud-game/webrtc"
// "github.com/gorilla/mux"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"encoding/json"
@ -23,10 +23,7 @@ import (
// var webRTC *webrtc.WebRTC
var width = 256
var height = 240
// var gameName = "supermariobros.rom"
var gameName string
// var FPS = 60
var index string = "./index_http.html"
var upgrader = websocket.Upgrader{}
@ -43,25 +40,28 @@ func startGame(path string, imageChannel chan *image.RGBA, inputChannel chan int
}
func main() {
fmt.Printf("Usage: %s [ws]\n", os.Args[0])
fmt.Println("http://localhost:8000")
fmt.Println(time.Now().UnixNano())
if len(os.Args) > 1 {
log.Println("Using websocket")
index = "./index_ws.html"
// router := mux.NewRouter()
// router.HandleFunc("/", getWeb).Methods("GET")
// router.HandleFunc("/session", postSession).Methods("POST")
// http.ListenAndServe(":8000", router)
// ignore origin
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
http.HandleFunc("/", getWeb)
http.HandleFunc("/ws", ws)
http.ListenAndServe(":8000", nil)
// ignore origin
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
http.HandleFunc("/", getWeb)
http.HandleFunc("/ws", ws)
http.ListenAndServe(":8000", nil)
} else {
log.Println("Using http")
router := mux.NewRouter()
router.HandleFunc("/", getWeb).Methods("GET")
router.HandleFunc("/session", postSession).Methods("POST")
http.ListenAndServe(":8000", router)
}
}
func getWeb(w http.ResponseWriter, r *http.Request) {
bs, err := ioutil.ReadFile("./index.html")
bs, err := ioutil.ReadFile(index)
if err != nil {
log.Fatal(err)
}
@ -84,6 +84,8 @@ func ws(w http.ResponseWriter, r *http.Request) {
// start new games and webrtc stuff?
isDone := false
var gameName string
for !isDone {
mt, message, err := c.ReadMessage()
if err != nil {
@ -150,6 +152,15 @@ func ws(w http.ResponseWriter, r *http.Request) {
}
}
type SessionPacket struct {
Game string `json:"game"`
SDP string `json:"sdp"`
}
func postSession(w http.ResponseWriter, r *http.Request) {
bs, err := ioutil.ReadAll(r.Body)
if err != nil {
@ -157,16 +168,24 @@ func postSession(w http.ResponseWriter, r *http.Request) {
}
r.Body.Close()
var postPacket SessionPacket
err = json.Unmarshal(bs, &postPacket)
if err != nil {
log.Fatalln(err)
}
log.Println("Got session with game request:", postPacket.Game)
webRTC := webrtc.NewWebRTC()
localSession, err := webRTC.StartClient(string(bs), width, height)
localSession, err := webRTC.StartClient(postPacket.SDP, width, height)
if err != nil {
log.Fatalln(err)
}
imageChannel := make(chan *image.RGBA, 100)
go screenshotLoop(imageChannel, webRTC)
go startGame("games/"+gameName, imageChannel, webRTC.InputChannel, webRTC)
go startGame("games/" + postPacket.Game, imageChannel, webRTC.InputChannel, webRTC)
w.Write([]byte(localSession))
}
@ -174,12 +193,15 @@ func postSession(w http.ResponseWriter, r *http.Request) {
// func screenshotLoop(imageChannel chan *image.RGBA) {
func screenshotLoop(imageChannel chan *image.RGBA, webRTC *webrtc.WebRTC) {
for image := range imageChannel {
// Client stopped
if webRTC.IsClosed() {
break
}
// encode frame
if webRTC.IsConnected() {
yuv := util.RgbaToYuv(image)
webRTC.ImageChannel <- yuv
} else {
break
}
}
}

View file

@ -65,7 +65,7 @@ func (d *Director) Start(paths []string) {
func (d *Director) Run() {
for {
// quit game
if !d.webRTC.IsConnected() {
if d.webRTC.IsClosed() {
break
}

View file

@ -4,7 +4,6 @@ import (
"image"
"fmt"
"time"
"github.com/giongto35/cloud-game/nes"
)
@ -24,11 +23,10 @@ type GameView struct {
imageChannel chan *image.RGBA
inputChannel chan int
nanotime int64
}
func NewGameView(director *Director, console *nes.Console, title, hash string, imageChannel chan *image.RGBA, inputChannel chan int) View {
gameview := &GameView{director, console, title, hash, false, nil, [8]bool{false}, imageChannel, inputChannel, time.Now().UnixNano()}
gameview := &GameView{director, console, title, hash, false, nil, [8]bool{false}, imageChannel, inputChannel}
go gameview.ListenToInputChannel()
return gameview
}
@ -88,12 +86,7 @@ func (view *GameView) Update(t, dt float64) {
console.StepSeconds(dt)
// fps to set frame
n := time.Now().UnixNano()
if n - view.nanotime > 1000000000 / 100000 {
view.nanotime = n
view.imageChannel <- console.Buffer()
}
view.imageChannel <- console.Buffer()
if view.record {

View file

@ -108,6 +108,7 @@ type WebRTC struct {
connection *webrtc.PeerConnection
encoder *vpxEncoder.VpxEncoder
isConnected bool
isClosed bool
// for yuvI420 image
ImageChannel chan []byte
InputChannel chan int
@ -225,6 +226,7 @@ func (w *WebRTC) StopClient() {
w.connection.Close()
}
w.connection = nil
w.isClosed = true
}
// IsConnected comment
@ -232,6 +234,10 @@ func (w *WebRTC) IsConnected() bool {
return w.isConnected
}
func (w *WebRTC) IsClosed() bool {
return w.isClosed
}
func (w *WebRTC) startStreaming(vp8Track *webrtc.Track) {
fmt.Println("Start streaming")