mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-22 09:37:09 +00:00
Add input
This commit is contained in:
parent
4224066b5d
commit
21c4f7aa5c
7 changed files with 168 additions and 103 deletions
23
index.html
23
index.html
|
|
@ -6,13 +6,8 @@ textarea {
|
|||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<a href="https://github.com/poi5305/go-yuv2webRTC">https://github.com/poi5305/go-yuv2webRTC</a>
|
||||
<br />
|
||||
<a href="https://github.com/pions/webrtc/tree/v1.2.0/examples/gstreamer-send/jsfiddle">https://github.com/pions/webrtc/tree/v1.2.0/examples/gstreamer-send/jsfiddle</a>
|
||||
</div>
|
||||
|
||||
<div id="remoteVideos"></div> <br />
|
||||
<div id="remoteVideos" ></div> <br />
|
||||
Browser base64 Session Description <br /><textarea id="localSessionDescription" readonly="true"></textarea> <br />
|
||||
|
||||
Golang base64 Session Description: <br /><textarea id="remoteSessionDescription"> </textarea> <br/>
|
||||
|
|
@ -37,6 +32,7 @@ function postSession(session) {
|
|||
xhttp.open("POST", "/session", true);
|
||||
xhttp.setRequestHeader("Content-type", "text/plain");
|
||||
xhttp.send(session);
|
||||
|
||||
}
|
||||
|
||||
let pc = new RTCPeerConnection({
|
||||
|
|
@ -50,6 +46,11 @@ let log = msg => {
|
|||
document.getElementById('div').innerHTML += msg + '<br>'
|
||||
}
|
||||
|
||||
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]
|
||||
|
|
@ -70,6 +71,14 @@ pc.onicecandidate = event => {
|
|||
|
||||
pc.createOffer({offerToReceiveVideo: true, offerToReceiveAudio: true}).then(d => pc.setLocalDescription(d)).catch(log)
|
||||
|
||||
document.body.onkeydown = function(e){
|
||||
inputChannel.send(e.keyCode)
|
||||
};
|
||||
|
||||
document.body.onkeyup = function(e){
|
||||
inputChannel.send(-e.keyCode)
|
||||
};
|
||||
|
||||
window.startSession = () => {
|
||||
let sd = document.getElementById('remoteSessionDescription').value
|
||||
if (sd === '') {
|
||||
|
|
@ -84,4 +93,4 @@ window.startSession = () => {
|
|||
}
|
||||
|
||||
</script>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
10
main.go
10
main.go
|
|
@ -11,9 +11,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/giongto35/game-online/ui"
|
||||
"github.com/giongto35/game-online/webrtc"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/poi5305/go-yuv2webRTC/screenshot"
|
||||
"github.com/poi5305/go-yuv2webRTC/webrtc"
|
||||
)
|
||||
|
||||
var webRTC *webrtc.WebRTC
|
||||
|
|
@ -23,11 +23,12 @@ var height = 240
|
|||
func init() {
|
||||
}
|
||||
|
||||
func startGame(path string, imageChannel chan *image.RGBA) {
|
||||
ui.Run([]string{path}, imageChannel)
|
||||
func startGame(path string, imageChannel chan *image.RGBA, inputChannel chan int) {
|
||||
ui.Run([]string{path}, imageChannel, inputChannel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
imageChannel := make(chan *image.RGBA, 2)
|
||||
fmt.Println("http://localhost:8000")
|
||||
webRTC = webrtc.NewWebRTC()
|
||||
|
||||
|
|
@ -38,9 +39,8 @@ func main() {
|
|||
go http.ListenAndServe(":8000", router)
|
||||
|
||||
// start screenshot loop, wait for connection
|
||||
imageChannel := make(chan *image.RGBA, 2)
|
||||
go screenshotLoop(imageChannel)
|
||||
startGame("games/supermariobros.rom", imageChannel)
|
||||
startGame("games/supermariobros.rom", imageChannel, webRTC.InputChannel)
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,15 @@ type Director struct {
|
|||
menuView View
|
||||
timestamp float64
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
}
|
||||
|
||||
func NewDirector(window *glfw.Window, audio *Audio, imageChannel chan *image.RGBA) *Director {
|
||||
func NewDirector(window *glfw.Window, audio *Audio, imageChannel chan *image.RGBA, inputChannel chan int) *Director {
|
||||
director := Director{}
|
||||
director.window = window
|
||||
director.audio = audio
|
||||
director.imageChannel = imageChannel
|
||||
director.inputChannel = inputChannel
|
||||
return &director
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +87,7 @@ func (d *Director) PlayGame(path string) {
|
|||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
d.SetView(NewGameView(d, console, path, hash, d.imageChannel))
|
||||
d.SetView(NewGameView(d, console, path, hash, d.imageChannel, d.inputChannel))
|
||||
}
|
||||
|
||||
func (d *Director) ShowMenu() {
|
||||
|
|
|
|||
|
|
@ -11,19 +11,37 @@ import (
|
|||
const padding = 0
|
||||
|
||||
type GameView struct {
|
||||
director *Director
|
||||
console *nes.Console
|
||||
title string
|
||||
hash string
|
||||
texture uint32
|
||||
record bool
|
||||
frames []image.Image
|
||||
director *Director
|
||||
console *nes.Console
|
||||
title string
|
||||
hash string
|
||||
texture uint32
|
||||
record bool
|
||||
frames []image.Image
|
||||
|
||||
keyPressed []bool
|
||||
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
}
|
||||
|
||||
func NewGameView(director *Director, console *nes.Console, title, hash string, imageChannel chan *image.RGBA) View {
|
||||
func NewGameView(director *Director, console *nes.Console, title, hash string, imageChannel chan *image.RGBA, inputChannel chan int) View {
|
||||
texture := createTexture()
|
||||
return &GameView{director, console, title, hash, texture, false, nil, imageChannel}
|
||||
gameview := &GameView{director, console, title, hash, texture, false, nil, make([]bool, 255), imageChannel, inputChannel}
|
||||
go gameview.ListenToInputChannel()
|
||||
return gameview
|
||||
}
|
||||
|
||||
func (view *GameView) ListenToInputChannel() {
|
||||
for {
|
||||
key := <-view.inputChannel
|
||||
if key < 0 {
|
||||
view.keyPressed[-key] = false
|
||||
} else {
|
||||
view.keyPressed[key] = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (view *GameView) Enter() {
|
||||
|
|
@ -75,7 +93,8 @@ func (view *GameView) Update(t, dt float64) {
|
|||
if readKey(window, glfw.KeyEscape) {
|
||||
view.director.ShowMenu()
|
||||
}
|
||||
updateControllers(window, console)
|
||||
//updateControllers(window, console)
|
||||
view.updateControllers()
|
||||
console.StepSeconds(dt)
|
||||
gl.BindTexture(gl.TEXTURE_2D, view.texture)
|
||||
setTexture(console.Buffer())
|
||||
|
|
@ -140,3 +159,17 @@ func updateControllers(window *glfw.Window, console *nes.Console) {
|
|||
console.SetButtons1(combineButtons(k1, j1))
|
||||
console.SetButtons2(j2)
|
||||
}
|
||||
|
||||
func (view *GameView) updateControllers() {
|
||||
// TODO: switch case
|
||||
var buttons [8]bool
|
||||
buttons[nes.ButtonLeft] = view.keyPressed[37]
|
||||
buttons[nes.ButtonUp] = view.keyPressed[38]
|
||||
buttons[nes.ButtonRight] = view.keyPressed[39]
|
||||
buttons[nes.ButtonDown] = view.keyPressed[40]
|
||||
buttons[nes.ButtonA] = view.keyPressed[32]
|
||||
buttons[nes.ButtonB] = view.keyPressed[17]
|
||||
buttons[nes.ButtonStart] = view.keyPressed[13]
|
||||
buttons[nes.ButtonSelect] = view.keyPressed[16]
|
||||
view.console.Controller1.SetButtons(buttons)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func init() {
|
|||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func Run(paths []string, imageChannel chan *image.RGBA) {
|
||||
func Run(paths []string, imageChannel chan *image.RGBA, inputChannel chan int) {
|
||||
// initialize audio
|
||||
portaudio.Initialize()
|
||||
defer portaudio.Terminate()
|
||||
|
|
@ -58,6 +58,6 @@ func Run(paths []string, imageChannel chan *image.RGBA) {
|
|||
gl.Enable(gl.TEXTURE_2D)
|
||||
|
||||
// run director
|
||||
director := NewDirector(window, audio, imageChannel)
|
||||
director := NewDirector(window, audio, imageChannel, inputChannel)
|
||||
director.Start(paths)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func zip(in []byte) []byte {
|
||||
var b bytes.Buffer
|
||||
gz := gzip.NewWriter(&b)
|
||||
_, err := gz.Write(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = gz.Flush()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = gz.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func unzip(in []byte) []byte {
|
||||
var b bytes.Buffer
|
||||
_, err := b.Write(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r, err := gzip.NewReader(&b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Encode encodes the input in base64
|
||||
// It can optionally zip the input before encoding
|
||||
func Encode(obj interface{}) string {
|
||||
b, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if compress {
|
||||
b = zip(b)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// Decode decodes the input from base64
|
||||
// It can optionally unzip the input after decoding
|
||||
func Decode(in string, obj interface{}) {
|
||||
b, err := base64.StdEncoding.DecodeString(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if compress {
|
||||
b = unzip(b)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,14 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pions/webrtc"
|
||||
|
|
@ -15,10 +21,84 @@ var config = webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string
|
|||
// Allows compressing offer/answer to bypass terminal input limits.
|
||||
const compress = false
|
||||
|
||||
func init() {
|
||||
//api.mediaEngine.RegisterDefaultCodecs()
|
||||
//webrtc.RegisterDefaultCodecs()
|
||||
}
|
||||
|
||||
func zip(in []byte) []byte {
|
||||
var b bytes.Buffer
|
||||
gz := gzip.NewWriter(&b)
|
||||
_, err := gz.Write(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = gz.Flush()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = gz.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func unzip(in []byte) []byte {
|
||||
var b bytes.Buffer
|
||||
_, err := b.Write(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r, err := gzip.NewReader(&b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Encode encodes the input in base64
|
||||
// It can optionally zip the input before encoding
|
||||
func Encode(obj interface{}) string {
|
||||
b, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if compress {
|
||||
b = zip(b)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// Decode decodes the input from base64
|
||||
// It can optionally unzip the input after decoding
|
||||
func Decode(in string, obj interface{}) {
|
||||
b, err := base64.StdEncoding.DecodeString(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if compress {
|
||||
b = unzip(b)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// NewWebRTC create
|
||||
func NewWebRTC() *WebRTC {
|
||||
w := &WebRTC{
|
||||
ImageChannel: make(chan []byte, 2),
|
||||
InputChannel: make(chan int, 2),
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
|
@ -30,6 +110,7 @@ type WebRTC struct {
|
|||
isConnected bool
|
||||
// for yuvI420 image
|
||||
ImageChannel chan []byte
|
||||
InputChannel chan int
|
||||
}
|
||||
|
||||
// StartClient start webrtc
|
||||
|
|
@ -83,6 +164,23 @@ func (w *WebRTC) StartClient(remoteSession string, width, height int) (string, e
|
|||
w.StopClient()
|
||||
}
|
||||
})
|
||||
//w.listenInputChannel()
|
||||
// Data channel
|
||||
w.connection.OnDataChannel(func(d *webrtc.DataChannel) {
|
||||
fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())
|
||||
|
||||
// Register channel opening handling
|
||||
d.OnOpen(func() {
|
||||
fmt.Printf("Data channel '%s'-'%d' open.\n", d.Label(), d.ID())
|
||||
})
|
||||
|
||||
// Register text message handling
|
||||
d.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||
fmt.Printf("Message from DataChannel '%s': '%s' byte '%b'\n", d.Label(), string(msg.Data), msg.Data)
|
||||
i, _ := strconv.Atoi(string(msg.Data))
|
||||
w.InputChannel <- i
|
||||
})
|
||||
})
|
||||
|
||||
offer := webrtc.SessionDescription{}
|
||||
Decode(remoteSession, &offer)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue