-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.go
136 lines (112 loc) · 3.19 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// HTML Video Stream server.
//
// Based on http://phoboslab.org/log/2013/09/html5-live-video-streaming-via-websockets.
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"net/http"
"strconv"
"sync"
"golang.org/x/net/websocket"
)
// ffmpeg -s 640x480 -f video4linux2 -i /dev/video0 -f mpeg1video -b 800k -r 30 'http://127.0.0.1:8084/input?width=640&height=480'
// goexec 'http.ListenAndServe(":8080", http.FileServer(http.Dir(".")))'
var data = make(ChanWriter)
var state = struct {
Statuses map[*websocket.Conn]struct{}
Width, Height int
sync.RWMutex
}{Statuses: make(map[*websocket.Conn]struct{})}
func output(c *websocket.Conn) {
// TODO: See if maybe this would help? It wasn't here when I wrote the code originally.
c.PayloadType = websocket.BinaryFrame
state.Lock()
state.Statuses[c] = struct{}{}
println("New handler #", len(state.Statuses))
var streamHeader = struct {
Magic [4]byte
Width uint16
Height uint16
}{
[...]byte{'j', 's', 'm', 'p'},
uint16(state.Width),
uint16(state.Height),
}
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, streamHeader)
fmt.Println("Sending header of len", len(buf.Bytes()))
err := websocket.Message.Send(c, buf.Bytes())
if err != nil {
panic(err)
}
state.Unlock()
//time.Sleep(30 * time.Second)
select {} // TODO: Fix leak and unreachable code below.
defer func() {
state.Lock()
println("End of handler #", len(state.Statuses))
delete(state.Statuses, c)
state.Unlock()
}()
}
func input(w http.ResponseWriter, r *http.Request) {
state.Lock()
state.Width, _ = strconv.Atoi(r.URL.Query().Get("width"))
state.Height, _ = strconv.Atoi(r.URL.Query().Get("height"))
state.Unlock()
//io.Copy(os.Stdout, r.Body)
//io.Copy(data, r.Body)
b := make([]byte, 1024)
for {
n, err := r.Body.Read(b)
// TODO: If we cared about correctness, should process b[:n] bytes before checking/dealing with error.
if err != nil {
return
}
state.Lock()
fmt.Println("Sending data to", len(state.Statuses))
for c := range state.Statuses {
//n, err := c.Write(b)
err := websocket.Message.Send(c, b)
if err != nil {
log.Println(err)
delete(state.Statuses, c)
continue
}
fmt.Println("Wrote", n, "bytes to some websocket")
}
state.Unlock()
}
fmt.Println("Video feed disconnected.")
}
func list(w http.ResponseWriter, r *http.Request) {
state.RLock()
defer state.RUnlock()
fmt.Fprintf(w, "We have %v connection(s).\n", len(state.Statuses))
fmt.Fprintf(w, "%#v\n%vx%v", state.Statuses, state.Width, state.Height)
}
func main() {
println("Starting.")
http.Handle("/output", websocket.Handler(output))
http.HandleFunc("/input", input)
http.HandleFunc("/list", list)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./live-status.html") })
http.Handle("/favicon.ico/", http.NotFoundHandler())
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}
// ChanWriter writes bytes into a channel.
type ChanWriter chan []byte
// Write implements io.Writer.
func (cw ChanWriter) Write(p []byte) (n int, err error) {
// Make a copy of p in order to avoid retaining it.
b := make([]byte, len(p))
copy(b, p)
cw <- b
return len(b), nil
}