7 Commits
1.0 ... 1.2

Author SHA1 Message Date
Milan Nikolic
a9c82d2d6e Add option to draw images with WebGL 2017-10-06 10:29:21 +02:00
Milan Nikolic
f36a47b9a2 update README.md 2017-10-05 21:49:41 +02:00
Milan Nikolic
29e9f6a562 Format HTML 2017-10-05 21:44:19 +02:00
Milan Nikolic
24fffe2c3f Format HTML, improve performance a bit 2017-10-05 20:40:33 +02:00
Milan Nikolic
04492f988a Update README.md 2017-10-05 19:57:03 +02:00
Milan Nikolic
3dc3aa1796 Remove CSS and add table 2017-10-05 17:36:20 +02:00
Milan Nikolic
f0e85361bc Add Android build 2017-10-04 03:17:00 +02:00
6 changed files with 154 additions and 70 deletions

View File

@@ -1,6 +1,6 @@
## cam2ip
Turn any webcam into ip camera.
Turn any webcam into an IP camera.
Example (in web browser):
@@ -18,9 +18,10 @@ or
Binaries are compiled with static OpenCV library:
- [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.0/cam2ip-1.0-64bit.tar.gz)
- [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.0/cam2ip-1.0.zip)
- [RPi 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.0/cam2ip-1.0-RPi.tar.gz)
- [Android 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.2/cam2ip-1.2-android.tar.gz)
- [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.2/cam2ip-1.2-64bit.tar.gz)
- [RPi 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.2/cam2ip-1.2-RPi.tar.gz)
- [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.2/cam2ip-1.2.zip)
### Installation
@@ -34,17 +35,19 @@ This will install app in `$GOPATH/bin/cam2ip`.
```
Usage of ./cam2ip:
-bind-addr string
Bind address (default ":56000")
Bind address (default ":56000")
-delay int
Delay between frames, in milliseconds (default 10)
Delay between frames, in milliseconds (default 10)
-frame-height float
Frame height (default 480)
Frame height (default 480)
-frame-width float
Frame width (default 640)
Frame width (default 640)
-htpasswd-file string
Path to htpasswd file, if empty auth is disabled
Path to htpasswd file, if empty auth is disabled
-index int
Camera index
Camera index
-webgl
Use WebGL to draw images
```
### Handlers

View File

@@ -21,6 +21,7 @@ func main() {
flag.IntVar(&srv.Delay, "delay", 10, "Delay between frames, in milliseconds")
flag.Float64Var(&srv.FrameWidth, "frame-width", 640, "Frame width")
flag.Float64Var(&srv.FrameHeight, "frame-height", 480, "Frame height")
flag.BoolVar(&srv.WebGL, "webgl", false, "Use WebGL to draw images")
flag.StringVar(&srv.Bind, "bind-addr", ":56000", "Bind address")
flag.StringVar(&srv.Htpasswd, "htpasswd-file", "", "Path to htpasswd file, if empty auth is disabled")
flag.Parse()

View File

@@ -96,6 +96,7 @@ func (c *Camera) SetProperty(id int, value float64) int {
func (c *Camera) Close() (err error) {
if c.camera == nil {
err = fmt.Errorf("camera: camera is not opened")
return
}
c.camera.Release()

View File

@@ -13,7 +13,7 @@ type HTML struct {
}
// NewHTML returns new HTML handler.
func NewHTML(bind string, width, height float64) *HTML {
func NewHTML(bind string, width, height float64, gl bool) *HTML {
h := &HTML{}
b := strings.Split(bind, ":")
@@ -21,11 +21,16 @@ func NewHTML(bind string, width, height float64) *HTML {
bind = "127.0.0.1" + bind
}
html = strings.Replace(html, "{BIND}", bind, -1)
html = strings.Replace(html, "{WIDTH}", fmt.Sprintf("%.0f", width), -1)
html = strings.Replace(html, "{HEIGHT}", fmt.Sprintf("%.0f", height), -1)
tpl := html
if gl {
tpl = htmlWebGL
}
h.Template = []byte(html)
tpl = strings.Replace(tpl, "{BIND}", bind, -1)
tpl = strings.Replace(tpl, "{WIDTH}", fmt.Sprintf("%.0f", width), -1)
tpl = strings.Replace(tpl, "{HEIGHT}", fmt.Sprintf("%.0f", height), -1)
h.Template = []byte(tpl)
return h
}
@@ -44,66 +49,129 @@ func (h *HTML) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var html = `<html>
<head>
<meta charset="utf-8"/>
<title>cam2ip</title>
<style>
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background-color: #000000;
}
div {
width: 100%;
height: 100%;
position: relative;
}
canvas {
height: auto;
width: auto;
max-height: 100%;
max-width: 100%;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
margin: auto;
}
</style>
<script>
var url = "ws://{BIND}/socket";
ws = new WebSocket(url);
ws = new WebSocket("ws://{BIND}/socket");
var image = new Image();
ws.onopen = function() {
console.log("onopen");
var context = document.getElementById("canvas").getContext("2d", {alpha: false});
image.onload = function() {
context.drawImage(image, 0, 0);
}
}
ws.onmessage = function(e) {
var context = document.getElementById("canvas").getContext("2d");
var image = new Image();
image.onload = function() {
context.drawImage(image, 0, 0);
}
image.setAttribute("src", "data:image/jpeg;base64," + e.data);
}
ws.onclose = function(e) {
console.log("onclose");
}
ws.onerror = function(e) {
console.log("onerror");
}
</script>
</head>
<body>
<div><canvas id="canvas" width="{WIDTH}" height="{HEIGHT}"></canvas></div>
<body style="background-color: #000000">
<table style="width:100%; height:100%">
<tr style="height:100%">
<td style="height:100%; text-align:center">
<canvas id="canvas" width="{WIDTH}" height="{HEIGHT}"></canvas>
</td>
</tr>
</table>
</body>
</html>`
var htmlWebGL = `<html>
<head>
<meta charset="utf-8"/>
<title>cam2ip</title>
<script>
var texture, vloc, tloc, vertexBuff, textureBuff;
ws = new WebSocket("ws://{BIND}/socket");
var image = new Image();
ws.onopen = function() {
var gl = document.getElementById('canvas').getContext('webgl');
var vertexShaderSrc =
"attribute vec2 aVertex;" +
"attribute vec2 aUV;" +
"varying vec2 vTex;" +
"void main(void) {" +
" gl_Position = vec4(aVertex, 0.0, 1.0);" +
" vTex = aUV;" +
"}";
var fragmentShaderSrc =
"precision mediump float;" +
"varying vec2 vTex;" +
"uniform sampler2D sampler0;" +
"void main(void){" +
" gl_FragColor = texture2D(sampler0, vTex);"+
"}";
var vertShaderObj = gl.createShader(gl.VERTEX_SHADER);
var fragShaderObj = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertShaderObj, vertexShaderSrc);
gl.shaderSource(fragShaderObj, fragmentShaderSrc);
gl.compileShader(vertShaderObj);
gl.compileShader(fragShaderObj);
var program = gl.createProgram();
gl.attachShader(program, vertShaderObj);
gl.attachShader(program, fragShaderObj);
gl.linkProgram(program);
gl.useProgram(program);
gl.viewport(0, 0, {WIDTH}, {HEIGHT});
vertexBuff = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuff);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 1, -1, -1, 1, -1, 1, 1]), gl.STATIC_DRAW);
textureBuff = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureBuff);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]), gl.STATIC_DRAW);
vloc = gl.getAttribLocation(program, "aVertex");
tloc = gl.getAttribLocation(program, "aUV");
texture = gl.createTexture();
image.onload = function() {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuff);
gl.enableVertexAttribArray(vloc);
gl.vertexAttribPointer(vloc, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, textureBuff);
gl.enableVertexAttribArray(tloc);
gl.vertexAttribPointer(tloc, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
}
}
ws.onmessage = function(e) {
image.setAttribute("src", "data:image/jpeg;base64," + e.data);
}
</script>
</head>
<body style="background-color: #000000">
<table style="width:100%; height:100%">
<tr style="height:100%">
<td style="height:100%; text-align:center">
<canvas id="canvas" width="{WIDTH}" height="{HEIGHT}"></canvas>
</td>
</tr>
</table>
</body>
</html>`

View File

@@ -3,6 +3,7 @@
CHROOT="/usr/x86_64-pc-linux-gnu-static"
MINGW="/usr/i686-w64-mingw32"
RPI="/usr/armv6j-hardfloat-linux-gnueabi"
ANDROID="/opt/android-toolchain-arm7"
mkdir -p build
@@ -10,13 +11,13 @@ LIBRARY_PATH="$CHROOT/usr/lib:$CHROOT/lib" \
PKG_CONFIG_PATH="$CHROOT/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$CHROOT/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$CHROOT/usr/lib -L$CHROOT/lib" \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -v -x -o build/cam2ip.amd64 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -v -x -o build/cam2ip.linux.amd64 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip
PKG_CONFIG="/usr/bin/i686-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MINGW/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$MINGW/usr/lib -L$MINGW/usr/include" \
CGO_LDFLAGS="-L$MINGW/usr/lib" \
CGO_CFLAGS="-I$MINGW/usr/include" \
CC="i686-w64-mingw32-gcc" CXX="i686-w64-mingw32-g++" \
CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -v -x -o build/cam2ip.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip
@@ -24,7 +25,16 @@ CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -v -x -o build/cam2ip.exe -ldflag
PKG_CONFIG="/usr/bin/armv6j-hardfloat-linux-gnueabi-pkg-config" \
PKG_CONFIG_PATH="$RPI/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$RPI/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$RPI/usr/lib -L$RPI/usr/include" \
CGO_LDFLAGS="-L$RPI/usr/lib" \
CGO_CFLAGS="-I$RPI/usr/include" \
CC="armv6j-hardfloat-linux-gnueabi-gcc" CXX="armv6j-hardfloat-linux-gnueabi-g++" \
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -v -x -o build/cam2ip.arm -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -v -x -o build/cam2ip.linux.arm -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip
PATH="$PATH:$ANDROID/bin" \
PKG_CONFIG="$ANDROID/bin/arm-linux-androideabi-pkg-config" \
PKG_CONFIG_PATH="$ANDROID/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$ANDROID/lib/pkgconfig" \
CGO_LDFLAGS="-L$ANDROID/lib" \
CGO_CFLAGS="-I$ANDROID/include" \
CC="arm-linux-androideabi-gcc" CXX="arm-linux-androideabi-g++" \
CGO_ENABLED=1 GOOS=android GOARCH=arm go build -v -x -o build/cam2ip.android.arm -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip

View File

@@ -24,6 +24,7 @@ type Server struct {
Delay int
FrameWidth float64
FrameHeight float64
WebGL bool
Camera *camera.Camera
}
@@ -42,7 +43,7 @@ func (s *Server) ListenAndServe() error {
basic = auth.NewBasicAuthenticator(realm, auth.HtpasswdFileProvider(s.Htpasswd))
}
http.Handle("/html", newAuthHandler(handlers.NewHTML(s.Bind, s.FrameWidth, s.FrameHeight), basic))
http.Handle("/html", newAuthHandler(handlers.NewHTML(s.Bind, s.FrameWidth, s.FrameHeight, s.WebGL), basic))
http.Handle("/jpeg", newAuthHandler(handlers.NewJPEG(s.Camera), basic))
http.Handle("/mjpeg", newAuthHandler(handlers.NewMJPEG(s.Camera, s.Delay), basic))