31 Commits
1.4 ... 1.6

Author SHA1 Message Date
Milan Nikolic
12b5f29452 Update version 2019-10-06 06:09:06 +02:00
Milan Nikolic
adf2a742e6 Add timestamp option 2019-10-06 05:48:12 +02:00
Milan Nikolic
1c342a67df Support env vars 2019-10-06 05:14:06 +02:00
Milan Nikolic
9e6d20863a Move main files to cmd 2019-10-06 03:29:40 +02:00
Milan Nikolic
3844a46486 Move options to file 2019-10-06 03:14:07 +02:00
Milan Nikolic
2628bbefbf Allow HEAD 2019-10-06 02:48:39 +02:00
Milan Nikolic
3b0b066cc1 Move context 2019-10-06 02:43:58 +02:00
Milan Nikolic
277d6c5b48 Add index 2019-10-06 02:41:34 +02:00
Milan Nikolic
1baf0deb39 Protect websocket when htpasswd is used 2019-10-06 02:06:27 +02:00
Milan Nikolic
50f28f57db Fix build tags 2019-10-06 02:00:31 +02:00
Milan Nikolic
b4e1af7729 Change websocket library 2019-10-06 01:52:13 +02:00
Milan Nikolic
1c8347fee8 Rename cv3 -> cv4 2019-10-05 23:00:52 +02:00
Milan Nikolic
63dd32115c Update go-opencv 2019-10-05 22:53:13 +02:00
Milan Nikolic
773b8920b0 Merge pull request #19 from RandomErrorMessage/master
bugfix + cleanup
2019-09-29 13:36:14 +02:00
RandomErrorMessage
01b9c564ac re-added goost after fixing upstream amd64 build bug 2019-09-28 18:41:23 -07:00
RandomErrorMessage
f1df08acc7 encoding/base64 is stdlib, re-ordered 2019-09-27 02:38:31 -07:00
RandomErrorMessage
5b7d51a9ac updated go modules, removed goost base64 dependency 2019-09-27 02:31:01 -07:00
RandomErrorMessage
6b28dfec25 removed unused function parameter 2019-09-27 02:30:44 -07:00
RandomErrorMessage
ff09bbeff4 bugfix: missing string addition in javascript 2019-09-27 02:30:29 -07:00
Milan Nikolic
4d73010019 Merge pull request #14 from iDigitalFlame/patch-1
Remove the string replacement of the bind address/port
2018-11-30 23:25:40 +01:00
iDigitalFlame
13385ba650 Remove the string replacement of the bind address/port
Removes the bind address in favor of using JS to auto detect the location and use instead.  "window.location.host" will point to the proper endpoint.
If the server is at "10.10.10.10", 'new WebSocket("ws://" + window.location.host + "/socket");' would be created at "ws://10.10.10.10". The "window.location.host" property also includes the port number as well.
This should fix the current open issue with websockets on HTML not rendering properly when the "-bind" option is omitted.
2018-11-30 21:42:03 +00:00
Milan Nikolic
09932a978c Update README.md 2018-11-17 21:18:31 +01:00
Milan Nikolic
fb1388a10a Add Dockerfile and image 2018-11-17 18:04:51 +01:00
Milan Nikolic
8ebd729dcd Add support for go modules 2018-11-17 18:00:29 +01:00
Milan Nikolic
309f6d339f Use fork with pkgconfig 2018-11-17 17:59:39 +01:00
Milan Nikolic
3d617687fa Update README.md 2018-10-29 16:38:26 +01:00
Milan Nikolic
d5870110d9 Add support for native VFW implementation on Windows 2018-10-29 16:27:46 +01:00
Milan Nikolic
72f12931da Update README.md 2018-10-28 16:43:55 +01:00
Milan Nikolic
bb676e4e44 New release 2018-10-28 14:37:20 +01:00
Milan Nikolic
ec60387edf Add support for native V4L implementation on Linux/RPi 2018-10-28 14:23:04 +01:00
Milan Nikolic
0503355005 Use asm implementation of base64 on amd64 2018-10-28 13:44:47 +01:00
30 changed files with 1234 additions and 188 deletions

22
Dockerfile Normal file
View File

@@ -0,0 +1,22 @@
FROM golang:alpine as build
WORKDIR /build
RUN apk add --no-cache git
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -tags jpeg -o cam2ip -ldflags "-s -w"
FROM scratch
COPY --from=build /build/cam2ip /cam2ip
EXPOSE 56000
ENTRYPOINT ["/cam2ip"]

View File

@@ -11,49 +11,67 @@ or
### Requirements
* [OpenCV](http://opencv.org/) (default is version 2.x via [go-opencv](https://github.com/lazywei/go-opencv), use `-tags cv3` for [gocv](https://github.com/hybridgroup/gocv))
* [libjpeg-turbo](https://www.libjpeg-turbo.org/) (use `-tags jpeg` for native image/jpeg, but note that CPU usage will be much higher)
* [libjpeg-turbo](https://www.libjpeg-turbo.org/) (use `-tags jpeg` to build without `CGo`)
* On Linux/RPi native Go [V4L](https://github.com/korandiz/v4l) implementation is used to capture images.
* On Windows [Video for Windows (VfW)](https://en.wikipedia.org/wiki/Video_for_Windows) framework is used over win32 API.
### Build tags
* `cv2` - build with `OpenCV` 2.x ([go-opencv](https://github.com/lazywei/go-opencv))
* `cv4` - build with `OpenCV` 4.x ([gocv](https://github.com/hybridgroup/gocv))
* `jpeg` - build with native Go `image/jpeg` instead of `libjpeg-turbo`
### Download
Binaries are compiled with static OpenCV/libjpeg-turbo libraries, they should just work:
- [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-64bit.tar.gz)
- [RPi 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-RPi.tar.gz)
- [RPi3 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-RPi3.tar.gz)
- [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4.zip)
- [Windows 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.4/cam2ip-1.4-64bit.zip)
- [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-64bit.tar.gz)
- [Linux 64bit OpenCV](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-64bit-cv2.tar.gz)
- [RPi 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi.tar.gz)
- [RPi 32bit OpenCV](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi-cv2.tar.gz)
- [RPi 32bit Static](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi-nocgo.tar.gz)
- [RPi3 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi3.tar.gz)
- [RPi3 32bit OpenCV](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi3-cv2.tar.gz)
- [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-32bit.zip)
- [Windows 32bit OpenCV](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-32bit-cv2.zip)
- [Windows 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-64bit.zip)
- [Windows 64bit OpenCV](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-64bit-cv2.zip)
### Installation
go get -v github.com/gen2brain/cam2ip
go get -v github.com/gen2brain/cam2ip/cmd/cam2ip
This will install app in `$GOPATH/bin/cam2ip`.
### Run in Docker container
docker run --device=/dev/video0:/dev/video0 -p56000:56000 -it gen2brain/cam2ip # on RPi use gen2brain/cam2ip:arm
### Usage
```
Usage of ./cam2ip:
Usage of cam2ip:
-bind-addr string
Bind address (default ":56000")
Bind address [CAM2IP_BIND_ADDR] (default ":56000")
-delay int
Delay between frames, in milliseconds (default 10)
Delay between frames, in milliseconds [CAM2IP_DELAY] (default 10)
-height float
Frame height (default 480)
-width float
Frame width (default 640)
Frame height [CAM2IP_HEIGHT] (default 480)
-htpasswd-file string
Path to htpasswd file, if empty auth is disabled
Path to htpasswd file, if empty auth is disabled [CAM2IP_HTPASSWD_FILE]
-index int
Camera index
Camera index [CAM2IP_INDEX]
-nowebgl
Disable WebGL drawing of images (html handler)
Disable WebGL drawing of images (html handler) [CAM2IP_NOWEBGL]
-rotate int
Rotate image, valid values are 90, 180, 270
Rotate image, valid values are 90, 180, 270 [CAM2IP_ROTATE]
-timestamp
Draws timestamp on images [CAM2IP_TIMESTAMP]
-video-file string
Use video file instead of camera
Use video file instead of camera [CAM2IP_VIDEO_FILE]
-width float
Frame width [CAM2IP_WIDTH] (default 640)
```
### Handlers

View File

@@ -1,91 +1,10 @@
// +build !cv3
// Package camera.
package camera
import (
"fmt"
"image"
"github.com/disintegration/imaging"
"github.com/lazywei/go-opencv/opencv"
)
// Options.
type Options struct {
Index int
Rotate int
}
// Camera represents camera.
type Camera struct {
opts Options
camera *opencv.Capture
frame *opencv.IplImage
}
// New returns new Camera for given camera index.
func New(opts Options) (camera *Camera, err error) {
camera = &Camera{}
camera.opts = opts
camera.camera = opencv.NewCameraCapture(opts.Index)
if camera.camera == nil {
err = fmt.Errorf("camera: can not open camera %d", opts.Index)
}
return
}
// Read reads next frame from camera and returns image.
func (c *Camera) Read() (img image.Image, err error) {
if c.camera.GrabFrame() {
c.frame = c.camera.RetrieveFrame(1)
if c.frame == nil {
err = fmt.Errorf("camera: can not grab frame")
return
}
img = c.frame.ToImage()
if c.opts.Rotate == 0 {
return
}
switch c.opts.Rotate {
case 90:
img = imaging.Rotate90(img)
case 180:
img = imaging.Rotate180(img)
case 270:
img = imaging.Rotate270(img)
}
} else {
err = fmt.Errorf("camera: can not grab frame")
}
return
}
// GetProperty returns the specified camera property.
func (c *Camera) GetProperty(id int) float64 {
return c.camera.GetProperty(id)
}
// SetProperty sets a camera property.
func (c *Camera) SetProperty(id int, value float64) {
c.camera.SetProperty(id, value)
}
// Close closes camera.
func (c *Camera) Close() (err error) {
if c.camera == nil {
err = fmt.Errorf("camera: camera is not opened")
return
}
c.frame.Release()
c.camera.Release()
c.camera = nil
return
Index int
Rotate int
Width float64
Height float64
Timestamp bool
}

View File

@@ -1,3 +1,5 @@
// +build cv2 cv4
package camera
// Property identifiers.

View File

@@ -0,0 +1,21 @@
// +build !cv2,!cv4
package camera
import (
"github.com/korandiz/v4l"
)
// Property identifiers.
const (
PropBrightness = v4l.CtrlBrightness
PropContrast = v4l.CtrlContrast
PropSaturation = v4l.CtrlSaturation
PropHue = v4l.CtrlHue
PropGain = v4l.CtrlGain
PropExposure = v4l.CtrlExposure
PropWhiteBalanceU = v4l.CtrlWhiteBalance
PropSharpness = v4l.CtrlSharpness
PropWhiteBalanceV = v4l.CtrlDoWhiteBalance
PropBacklight = v4l.CtrlBacklightCompensation
)

101
camera/camera_cv2.go Normal file
View File

@@ -0,0 +1,101 @@
// +build cv2,!cv4
// Package camera.
package camera
import (
"fmt"
"image"
"image/color"
"image/draw"
"time"
"github.com/pbnjay/pixfont"
"github.com/disintegration/imaging"
"github.com/gen2brain/go-opencv/opencv"
)
// Camera represents camera.
type Camera struct {
opts Options
camera *opencv.Capture
frame *opencv.IplImage
}
// New returns new Camera for given camera index.
func New(opts Options) (camera *Camera, err error) {
camera = &Camera{}
camera.opts = opts
camera.camera = opencv.NewCameraCapture(opts.Index)
if camera.camera == nil {
err = fmt.Errorf("camera: can not open camera %d", opts.Index)
}
camera.SetProperty(PropFrameWidth, opts.Width)
camera.SetProperty(PropFrameHeight, opts.Height)
return
}
// Read reads next frame from camera and returns image.
func (c *Camera) Read() (img image.Image, err error) {
if !c.camera.GrabFrame() {
err = fmt.Errorf("camera: can not grab frame")
return
}
c.frame = c.camera.RetrieveFrame(1)
if c.frame == nil {
err = fmt.Errorf("camera: can not retrieve frame")
return
}
img = c.frame.ToImage()
switch c.opts.Rotate {
case 90:
img = imaging.Rotate90(img)
case 180:
img = imaging.Rotate180(img)
case 270:
img = imaging.Rotate270(img)
}
if c.opts.Timestamp {
dimg, ok := img.(draw.Image)
if !ok {
err = fmt.Errorf("camera: %T is not a drawable image type", img)
return
}
pixfont.DrawString(dimg, 10, 10, time.Now().Format("2006-01-02 15:04:05"), color.White)
img = dimg
}
return
}
// GetProperty returns the specified camera property.
func (c *Camera) GetProperty(id int) float64 {
return c.camera.GetProperty(id)
}
// SetProperty sets a camera property.
func (c *Camera) SetProperty(id int, value float64) {
c.camera.SetProperty(id, value)
}
// Close closes camera.
func (c *Camera) Close() (err error) {
if c.camera == nil {
err = fmt.Errorf("camera: camera is not opened")
return
}
c.frame.Release()
c.camera.Release()
c.camera = nil
return
}

View File

@@ -1,4 +1,4 @@
// +build cv3
// +build cv4,!cv2
// Package camera.
package camera
@@ -6,17 +6,16 @@ package camera
import (
"fmt"
"image"
"image/color"
"image/draw"
"time"
"github.com/pbnjay/pixfont"
"github.com/disintegration/imaging"
"gocv.io/x/gocv"
)
// Options.
type Options struct {
Index int
Rotate int
}
// Camera represents camera.
type Camera struct {
opts Options
@@ -37,6 +36,9 @@ func New(opts Options) (camera *Camera, err error) {
err = fmt.Errorf("camera: can not open camera %d: %s", opts.Index, err.Error())
}
camera.SetProperty(PropFrameWidth, opts.Width)
camera.SetProperty(PropFrameHeight, opts.Height)
return
}
@@ -55,11 +57,7 @@ func (c *Camera) Read() (img image.Image, err error) {
}
if c.frame == nil {
err = fmt.Errorf("camera: can not grab frame")
return
}
if c.opts.Rotate == 0 {
err = fmt.Errorf("camera: can not retrieve frame")
return
}
@@ -72,6 +70,17 @@ func (c *Camera) Read() (img image.Image, err error) {
img = imaging.Rotate270(img)
}
if c.opts.Timestamp {
dimg, ok := img.(draw.Image)
if !ok {
err = fmt.Errorf("camera: %T is not a drawable image type", img)
return
}
pixfont.DrawString(dimg, 10, 10, time.Now().Format("2006-01-02 15:04:05"), color.White)
img = dimg
}
return
}

135
camera/camera_linux.go Normal file
View File

@@ -0,0 +1,135 @@
// +build !cv2,!cv4
// Package camera.
package camera
import (
"fmt"
"image"
"image/color"
"image/draw"
"time"
"github.com/disintegration/imaging"
"github.com/korandiz/v4l"
"github.com/korandiz/v4l/fmt/mjpeg"
"github.com/pbnjay/pixfont"
im "github.com/gen2brain/cam2ip/image"
)
// Camera represents camera.
type Camera struct {
opts Options
camera *v4l.Device
}
// New returns new Camera for given camera index.
func New(opts Options) (camera *Camera, err error) {
camera = &Camera{}
camera.opts = opts
devices := v4l.FindDevices()
if len(devices) < opts.Index+1 {
err = fmt.Errorf("camera: no camera at index %d", opts.Index)
return
}
camera.camera, err = v4l.Open(devices[opts.Index].Path)
if err != nil {
err = fmt.Errorf("camera: %s", err.Error())
return
}
if camera.camera == nil {
err = fmt.Errorf("camera: can not open camera %d", opts.Index)
return
}
config, err := camera.camera.GetConfig()
if err != nil {
err = fmt.Errorf("camera: %s", err.Error())
return
}
config.Format = mjpeg.FourCC
config.Width = int(opts.Width)
config.Height = int(opts.Height)
err = camera.camera.SetConfig(config)
if err != nil {
err = fmt.Errorf("camera: %s", err.Error())
return
}
err = camera.camera.TurnOn()
if err != nil {
err = fmt.Errorf("camera: %s", err.Error())
return
}
return
}
// Read reads next frame from camera and returns image.
func (c *Camera) Read() (img image.Image, err error) {
buffer, err := c.camera.Capture()
if err != nil {
err = fmt.Errorf("camera: can not grab frame: %s", err.Error())
return
}
img, err = im.NewDecoder(buffer).Decode()
if err != nil {
err = fmt.Errorf("camera: %s", err.Error())
return
}
switch c.opts.Rotate {
case 90:
img = imaging.Rotate90(img)
case 180:
img = imaging.Rotate180(img)
case 270:
img = imaging.Rotate270(img)
}
if c.opts.Timestamp {
dimg, ok := img.(draw.Image)
if !ok {
err = fmt.Errorf("camera: %T is not a drawable image type", img)
return
}
pixfont.DrawString(dimg, 10, 10, time.Now().Format("2006-01-02 15:04:05"), color.White)
img = dimg
}
return
}
// GetProperty returns the specified camera property.
func (c *Camera) GetProperty(id int) float64 {
ret, _ := c.camera.GetControl(uint32(id))
return float64(ret)
}
// SetProperty sets a camera property.
func (c *Camera) SetProperty(id int, value float64) {
c.camera.SetControl(uint32(id), int32(value))
}
// Close closes camera.
func (c *Camera) Close() (err error) {
if c.camera == nil {
err = fmt.Errorf("camera: camera is not opened")
return
}
c.camera.TurnOff()
c.camera.Close()
c.camera = nil
return
}

View File

@@ -11,7 +11,7 @@ import (
)
func TestCamera(t *testing.T) {
camera, err := New(1)
camera, err := New(Options{0, 0, 640, 480})
if err != nil {
t.Fatal(err)
}
@@ -25,18 +25,6 @@ func TestCamera(t *testing.T) {
defer os.RemoveAll(tmpdir)
var width, height float64 = 640, 480
camera.SetProperty(PropFrameWidth, width)
camera.SetProperty(PropFrameHeight, height)
if camera.GetProperty(PropFrameWidth) != width {
t.Error("FrameWidth not correct")
}
if camera.GetProperty(PropFrameHeight) != height {
t.Error("FrameHeight not correct")
}
var i int
var n int = 10

417
camera/camera_windows.go Normal file
View File

@@ -0,0 +1,417 @@
// +build !cv2,!cv4
// Package camera.
package camera
import (
"bytes"
"fmt"
"image"
"image/color"
"image/draw"
"syscall"
"time"
"unsafe"
"github.com/pbnjay/pixfont"
"github.com/disintegration/imaging"
)
// Camera represents camera.
type Camera struct {
opts Options
camera syscall.Handle
frame *image.RGBA
hdr *videoHdr
instance syscall.Handle
className string
}
// New returns new Camera for given camera index.
func New(opts Options) (camera *Camera, err error) {
camera = &Camera{}
camera.opts = opts
camera.className = "capWindowClass"
camera.instance, err = getModuleHandle()
if err != nil {
return
}
camera.frame = image.NewRGBA(image.Rect(0, 0, int(camera.opts.Width), int(camera.opts.Height)))
go func(c *Camera) {
fn := func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
switch msg {
case wmClose:
destroyWindow(hwnd)
case wmDestroy:
postQuitMessage(0)
default:
ret := defWindowProc(hwnd, msg, wparam, lparam)
return ret
}
return 0
}
err = registerClass(c.className, c.instance, fn)
if err != nil {
return
}
hwnd, err := createWindow(0, c.className, "", wsOverlappedWindow, cwUseDefault, cwUseDefault, int64(c.opts.Width)+100, int64(c.opts.Height)+100, 0, 0, c.instance)
if err != nil {
return
}
c.camera, err = capCreateCaptureWindow("", wsChild, 0, 0, int64(c.opts.Width), int64(c.opts.Height), hwnd, 0)
if err != nil {
return
}
ret := sendMessage(c.camera, wmCapDriverConnect, uintptr(c.opts.Index), 0)
if bool(int(ret) == 0) {
err = fmt.Errorf("camera: can not open camera %d", c.opts.Index)
return
}
var bi bitmapInfo
size := sendMessage(c.camera, wmCapGetVideoformat, 0, 0)
sendMessage(c.camera, wmCapGetVideoformat, size, uintptr(unsafe.Pointer(&bi)))
bi.BmiHeader.BiWidth = int32(c.opts.Width)
bi.BmiHeader.BiHeight = int32(c.opts.Height)
ret = sendMessage(c.camera, wmCapSetVideoformat, size, uintptr(unsafe.Pointer(&bi)))
if bool(int(ret) == 0) {
err = fmt.Errorf("camera: can not set video format")
return
}
sendMessage(c.camera, wmCapSetCallbackFrame, 0, syscall.NewCallback(c.callback))
messageLoop(c.camera)
}(camera)
return
}
// Read reads next frame from camera and returns image.
func (c *Camera) Read() (img image.Image, err error) {
ret := sendMessage(c.camera, wmCapGrabFrameNoStop, 0, 0)
if bool(int(ret) == 0) {
err = fmt.Errorf("camera: can not grab frame")
return
}
data := (*[1 << 24]uint8)(unsafe.Pointer(c.hdr.LpData))[0:c.hdr.DwBytesUsed]
r := bytes.NewReader(data)
width := int(c.opts.Width)
height := int(c.opts.Height)
// Taken from https://github.com/hotei/bmp/blob/master/bmpRGBA.go#L12
// There are 3 bytes per pixel, and each row is 4-byte aligned.
b := make([]byte, (3*width+3)&^3)
// BMP images are stored bottom-up rather than top-down.
for y := height - 1; y >= 0; y-- {
_, err = r.Read(b)
if err != nil {
err = fmt.Errorf("camera: can not retrieve frame: %v", err)
return
}
p := c.frame.Pix[y*c.frame.Stride : y*c.frame.Stride+width*4]
for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
// BMP images are stored in BGR order rather than RGB order.
p[i+0] = b[j+2]
p[i+1] = b[j+1]
p[i+2] = b[j+0]
p[i+3] = 0xFF
}
}
img = c.frame
switch c.opts.Rotate {
case 90:
img = imaging.Rotate90(img)
case 180:
img = imaging.Rotate180(img)
case 270:
img = imaging.Rotate270(img)
}
if c.opts.Timestamp {
dimg, ok := img.(draw.Image)
if !ok {
err = fmt.Errorf("camera: %T is not a drawable image type", img)
return
}
pixfont.DrawString(dimg, 10, 10, time.Now().Format("2006-01-02 15:04:05"), color.White)
img = dimg
}
return
}
// GetProperty returns the specified camera property.
func (c *Camera) GetProperty(id int) float64 {
return 0
}
// SetProperty sets a camera property.
func (c *Camera) SetProperty(id int, value float64) {
return
}
// Close closes camera.
func (c *Camera) Close() (err error) {
sendMessage(c.camera, wmCapSetCallbackFrame, 0, 0)
unregisterClass(c.className, c.instance)
sendMessage(c.camera, wmCapDriverDisconnect, 0, 0)
destroyWindow(c.camera)
return
}
// callback function.
func (c *Camera) callback(hwvd syscall.Handle, hdr *videoHdr) uintptr {
c.hdr = hdr
return 0
}
var (
user32 = syscall.NewLazyDLL("user32.dll")
kernel32 = syscall.NewLazyDLL("kernel32.dll")
avicap32 = syscall.NewLazyDLL("avicap32.dll")
createWindowExW = user32.NewProc("CreateWindowExW")
destroyWindowW = user32.NewProc("DestroyWindow")
defWindowProcW = user32.NewProc("DefWindowProcW")
dispatchMessageW = user32.NewProc("DispatchMessageW")
translateMessageW = user32.NewProc("TranslateMessage")
getMessageW = user32.NewProc("GetMessageW")
sendMessageW = user32.NewProc("SendMessageW")
postQuitMessageW = user32.NewProc("PostQuitMessage")
registerClassExW = user32.NewProc("RegisterClassExW")
unregisterClassW = user32.NewProc("UnregisterClassW")
getModuleHandleW = kernel32.NewProc("GetModuleHandleW")
capCreateCaptureWindowW = avicap32.NewProc("capCreateCaptureWindowW")
)
const (
wmDestroy = 0x0002
wmClose = 0x0010
wmUser = 0x0400
wmCapStart = wmUser
wmCapSetCallbackFrame = wmCapStart + 5
wmCapDriverConnect = wmCapStart + 10
wmCapDriverDisconnect = wmCapStart + 11
wmCapGetVideoformat = wmCapStart + 44
wmCapSetVideoformat = wmCapStart + 45
wmCapGrabFrame = wmCapStart + 60
wmCapGrabFrameNoStop = wmCapStart + 61
wmCapStop = wmCapStart + 68
wmCapAbort = wmCapStart + 69
wsChild = 0x40000000
wsOverlappedWindow = 0x00CF0000
cwUseDefault = 0x7fffffff
)
// wndClassExW https://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx
type wndClassExW struct {
size uint32
style uint32
wndProc uintptr
clsExtra int32
wndExtra int32
instance syscall.Handle
icon syscall.Handle
cursor syscall.Handle
background syscall.Handle
menuName *uint16
className *uint16
iconSm syscall.Handle
}
// msgW https://msdn.microsoft.com/en-us/library/windows/desktop/ms644958.aspx
type msgW struct {
hwnd syscall.Handle
message uint32
wParam uintptr
lParam uintptr
time uint32
pt pointW
}
// https://msdn.microsoft.com/en-us/ecb0f0e1-90c2-48ab-a069-552262b49c7c
type pointW struct {
x, y int32
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx
type bitmapInfoHeader struct {
BiSize uint32
BiWidth int32
BiHeight int32
BiPlanes uint16
BiBitCount uint16
BiCompression uint32
BiSizeImage uint32
BiXPelsPerMeter int32
BiYPelsPerMeter int32
BiClrUsed uint32
BiClrImportant uint32
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd183375.aspx
type bitmapInfo struct {
BmiHeader bitmapInfoHeader
BmiColors *rgbQuad
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162938.aspx
type rgbQuad struct {
RgbBlue byte
RgbGreen byte
RgbRed byte
RgbReserved byte
}
// https://docs.microsoft.com/en-us/windows/desktop/api/vfw/ns-vfw-videohdr_tag
type videoHdr struct {
LpData *uint8
DwBufferLength uint32
DwBytesUsed uint32
DwTimeCaptured uint32
DwUser uint64
DwFlags uint32
DwReserved [4]uint64
}
// https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-getmodulehandlea
func getModuleHandle() (syscall.Handle, error) {
ret, _, err := getModuleHandleW.Call(uintptr(0))
if ret == 0 {
return 0, err
}
return syscall.Handle(ret), nil
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-createwindowexw
func createWindow(exStyle uint64, className, windowName string, style uint64, x, y, width, height int64,
parent, menu, instance syscall.Handle) (syscall.Handle, error) {
ret, _, err := createWindowExW.Call(uintptr(exStyle), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(className))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(windowName))), uintptr(style), uintptr(x), uintptr(y),
uintptr(width), uintptr(height), uintptr(parent), uintptr(menu), uintptr(instance), uintptr(0))
if ret == 0 {
return 0, err
}
return syscall.Handle(ret), nil
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-destroywindow
func destroyWindow(hwnd syscall.Handle) error {
ret, _, err := destroyWindowW.Call(uintptr(hwnd))
if ret == 0 {
return err
}
return nil
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-defwindowprocw
func defWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
ret, _, _ := defWindowProcW.Call(uintptr(hwnd), uintptr(msg), uintptr(wparam), uintptr(lparam))
return uintptr(ret)
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-dispatchmessagew
func dispatchMessage(msg *msgW) {
dispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-translatemessage
func translateMessage(msg *msgW) {
translateMessageW.Call(uintptr(unsafe.Pointer(msg)))
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getmessagew
func getMessage(msg *msgW, hwnd syscall.Handle, msgFilterMin, msgFilterMax uint32) (bool, error) {
ret, _, err := getMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(msgFilterMin), uintptr(msgFilterMax))
if int32(ret) == -1 {
return false, err
}
return int32(ret) != 0, nil
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendmessage
func sendMessage(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
ret, _, _ := sendMessageW.Call(uintptr(hwnd), uintptr(msg), wparam, lparam, 0, 0)
return ret
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-postquitmessage
func postQuitMessage(exitCode int32) {
postQuitMessageW.Call(uintptr(exitCode))
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-registerclassexw
func registerClass(className string, instance syscall.Handle, fn interface{}) error {
var wcx wndClassExW
wcx.size = uint32(unsafe.Sizeof(wcx))
wcx.wndProc = syscall.NewCallback(fn)
wcx.instance = instance
wcx.className = syscall.StringToUTF16Ptr(className)
ret, _, err := registerClassExW.Call(uintptr(unsafe.Pointer(&wcx)))
if ret == 0 {
return err
}
return nil
}
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-unregisterclassw
func unregisterClass(className string, instance syscall.Handle) bool {
ret, _, _ := unregisterClassW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(className))), uintptr(instance))
return ret != 0
}
// https://docs.microsoft.com/en-us/windows/desktop/api/vfw/nf-vfw-capcreatecapturewindoww
func capCreateCaptureWindow(lpszWindowName string, dwStyle, x, y, width, height int64, parent syscall.Handle, id int64) (syscall.Handle, error) {
ret, _, err := capCreateCaptureWindowW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpszWindowName))),
uintptr(dwStyle), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(parent), uintptr(id))
if ret == 0 {
return 0, err
}
return syscall.Handle(ret), nil
}
// messageLoop function
func messageLoop(hwnd syscall.Handle) {
for {
msg := &msgW{}
ok, _ := getMessage(msg, 0, 0, 0)
if ok {
translateMessage(msg)
dispatchMessage(msg)
} else {
break
}
}
return
}

73
cmd/cam2ip/main.go Normal file
View File

@@ -0,0 +1,73 @@
// +build !cv2,!cv4
package main
import (
"flag"
"fmt"
"os"
"github.com/jamiealquiza/envy"
"github.com/gen2brain/cam2ip/camera"
"github.com/gen2brain/cam2ip/server"
)
const (
name = "cam2ip"
version = "1.6"
)
func main() {
srv := server.NewServer()
flag.IntVar(&srv.Index, "index", 0, "Camera index")
flag.IntVar(&srv.Delay, "delay", 10, "Delay between frames, in milliseconds")
flag.Float64Var(&srv.FrameWidth, "width", 640, "Frame width")
flag.Float64Var(&srv.FrameHeight, "height", 480, "Frame height")
flag.IntVar(&srv.Rotate, "rotate", 0, "Rotate image, valid values are 90, 180, 270")
flag.BoolVar(&srv.NoWebGL, "nowebgl", false, "Disable WebGL drawing of images (html handler)")
flag.BoolVar(&srv.Timestamp, "timestamp", false, "Draws timestamp on 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")
envy.Parse("CAM2IP")
flag.Parse()
srv.Name = name
srv.Version = version
var err error
if srv.Htpasswd != "" {
if _, err = os.Stat(srv.Htpasswd); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}
if srv.FileName != "" {
if _, err = os.Stat(srv.FileName); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}
cam, err := camera.New(camera.Options{srv.Index, srv.Rotate, srv.FrameWidth, srv.FrameHeight, srv.Timestamp})
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
srv.Reader = cam
defer srv.Reader.Close()
fmt.Fprintf(os.Stderr, "Listening on %s\n", srv.Bind)
err = srv.ListenAndServe()
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
}

View File

@@ -1,3 +1,5 @@
// +build cv2 cv4
package main
import (
@@ -5,6 +7,8 @@ import (
"fmt"
"os"
"github.com/jamiealquiza/envy"
"github.com/gen2brain/cam2ip/camera"
"github.com/gen2brain/cam2ip/server"
"github.com/gen2brain/cam2ip/video"
@@ -12,7 +16,7 @@ import (
const (
name = "cam2ip"
version = "1.4"
version = "1.6"
)
func main() {
@@ -24,9 +28,12 @@ func main() {
flag.Float64Var(&srv.FrameHeight, "height", 480, "Frame height")
flag.IntVar(&srv.Rotate, "rotate", 0, "Rotate image, valid values are 90, 180, 270")
flag.BoolVar(&srv.NoWebGL, "nowebgl", false, "Disable WebGL drawing of images (html handler)")
flag.BoolVar(&srv.Timestamp, "timestamp", false, "Draws timestamp on 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.StringVar(&srv.FileName, "video-file", "", "Use video file instead of camera")
envy.Parse("CAM2IP")
flag.Parse()
srv.Name = name
@@ -57,15 +64,12 @@ func main() {
srv.Reader = vid
} else {
cam, err := camera.New(camera.Options{srv.Index, srv.Rotate})
cam, err := camera.New(camera.Options{srv.Index, srv.Rotate, srv.FrameWidth, srv.FrameHeight, srv.Timestamp})
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
cam.SetProperty(camera.PropFrameWidth, srv.FrameWidth)
cam.SetProperty(camera.PropFrameHeight, srv.FrameHeight)
srv.Reader = cam
}

20
go.mod Normal file
View File

@@ -0,0 +1,20 @@
module github.com/gen2brain/cam2ip
require (
github.com/abbot/go-http-auth v0.4.0
github.com/antonini/golibjpegturbo v0.0.0-20141208033414-c03a2fa1e89a
github.com/disintegration/imaging v1.6.1
github.com/gen2brain/go-opencv v0.0.0-20191005190506-bf186fc94f7a
github.com/jamiealquiza/envy v1.1.0
github.com/korandiz/v4l v0.0.0-20180520170035-995f703bfc89
github.com/pbnjay/pixfont v0.0.0-20190130005054-401bb7c6aee2
github.com/spf13/cobra v0.0.5 // indirect
gocv.io/x/gocv v0.20.0
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad // indirect
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a // indirect
golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 // indirect
goost.org/encoding/base64 v0.0.0-20190928151742-cd6f75493c10
nhooyr.io/websocket v1.6.5
)
go 1.13

164
go.sum Normal file
View File

@@ -0,0 +1,164 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0=
github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM=
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
github.com/antonini/golibjpegturbo v0.0.0-20141208033414-c03a2fa1e89a h1:+Q4qlzO9KeavJSvWgRKckT3ViTSQ8rR6GroSiXPRXhs=
github.com/antonini/golibjpegturbo v0.0.0-20141208033414-c03a2fa1e89a/go.mod h1:UOX4aiVZ5WVUBY3D/31H4m6Z8UHgBj5Qr/oldRBBUMY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/imaging v1.6.1 h1:JnBbK6ECIZb1NsWIikP9pd8gIlTIRx7fuDNpU9fsxOE=
github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gen2brain/go-opencv v0.0.0-20191005190506-bf186fc94f7a h1:0arrt5Ke40opD5glNdh9ltrkZ0jaqPWsquGPSE3ukug=
github.com/gen2brain/go-opencv v0.0.0-20191005190506-bf186fc94f7a/go.mod h1:pOLh42huXUuMoJWvD2K+EeXzvQ9GZ5HN6gdFk5ZwIuU=
github.com/go-interpreter/wagon v0.5.1-0.20190713202023-55a163980b6c/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jamiealquiza/envy v1.1.0 h1:Nwh4wqTZ28gDA8zB+wFkhnUpz3CEcO12zotjeqqRoKE=
github.com/jamiealquiza/envy v1.1.0/go.mod h1:MP36BriGCLwEHhi1OU8E9569JNZrjWfCvzG7RsPnHus=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/korandiz/v4l v0.0.0-20180520170035-995f703bfc89 h1:BSg+wmA2aXiD7joWbYpkkLvPPZzdhpp2LAgoh8SMcyE=
github.com/korandiz/v4l v0.0.0-20180520170035-995f703bfc89/go.mod h1:jGGdTcTvrj/K3Qvlch8Qzb8PJjeMeJ/XmPbQxc37rJ8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pbnjay/pixfont v0.0.0-20190130005054-401bb7c6aee2 h1:Px7Nfku7x0pH6r2G+RlLI5AnXefcH2BAenMxDpZZfIs=
github.com/pbnjay/pixfont v0.0.0-20190130005054-401bb7c6aee2/go.mod h1:wG8B9TIIBxEYqwgBb9NEs/Gz5/ywV351SGZXRiVJJUA=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
gocv.io/x/gocv v0.20.0 h1:2q75zQ8Zel2tB69G6qrmf/E7EdvaCs90qvkHzdSBOAg=
gocv.io/x/gocv v0.20.0/go.mod h1:vZETJRwLnl11muQ6iL3q4ju+0oJRrdmYdv5xJTH7WYA=
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 h1:qPnAdmjNA41t3QBTx2mFGf/SD1IoslhYu7AmdsVzCcs=
golang.org/x/net v0.0.0-20190926025831-c00fd9afed17/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190927073244-c990c680b611 h1:q9u40nxWT5zRClI/uU9dHCiYGottAg6Nzz4YUQyHxdA=
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
goost.org/encoding/base64 v0.0.0-20190928151742-cd6f75493c10 h1:Z4tmKmcuax8bev3BlmNf3/0tcw6j/BBXFY6Hnq0njew=
goost.org/encoding/base64 v0.0.0-20190928151742-cd6f75493c10/go.mod h1:pBZSqyNgDwqnkoXcbBD1wSOStfw5WgjBx9Zcda9RvLA=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg=
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=

View File

@@ -13,20 +13,13 @@ type HTML struct {
}
// NewHTML returns new HTML handler.
func NewHTML(bind string, width, height float64, nogl bool) *HTML {
func NewHTML(width, height float64, nogl bool) *HTML {
h := &HTML{}
b := strings.Split(bind, ":")
if b[0] == "" {
bind = "127.0.0.1" + bind
}
tpl := htmlWebGL
if nogl {
tpl = 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)
@@ -52,7 +45,7 @@ var html = `<html>
<meta charset="utf-8"/>
<title>cam2ip</title>
<script>
ws = new WebSocket("ws://{BIND}/socket");
ws = new WebSocket("ws://" + window.location.host + "/socket");
var image = new Image();
ws.onopen = function() {
@@ -85,7 +78,7 @@ var htmlWebGL = `<html>
<script>
var texture, vloc, tloc, vertexBuff, textureBuff;
ws = new WebSocket("ws://{BIND}/socket");
ws = new WebSocket("ws://" + window.location.host + "/socket");
var image = new Image();
ws.onopen = function() {

32
handlers/index.go Normal file
View File

@@ -0,0 +1,32 @@
package handlers
import (
"net/http"
)
// Index handler.
type Index struct {
}
// NewIndex returns new Index handler.
func NewIndex() *Index {
return &Index{}
}
// ServeHTTP handles requests on incoming connections.
func (i *Index) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
return
}
w.Write([]byte(`<html>
<head><title>cam2ip</title></head>
<body>
<h1>cam2ip</h1>
<p><a href='/html'>html</a></p>
<p><a href='/jpeg'>jpeg</a></p>
<p><a href='/mjpeg'>mjpeg</a></p>
</body>
</html>`))
}

View File

@@ -4,7 +4,7 @@ import (
"log"
"net/http"
"github.com/gen2brain/cam2ip/encoder"
"github.com/gen2brain/cam2ip/image"
"github.com/gen2brain/cam2ip/reader"
)
@@ -20,7 +20,7 @@ func NewJPEG(reader reader.ImageReader) *JPEG {
// ServeHTTP handles requests on incoming connections.
func (j *JPEG) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
return
}
@@ -35,7 +35,7 @@ func (j *JPEG) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
err = encoder.New(w).Encode(img)
err = image.NewEncoder(w).Encode(img)
if err != nil {
log.Printf("jpeg: encode: %v", err)
return

View File

@@ -9,7 +9,7 @@ import (
"net/textproto"
"time"
"github.com/gen2brain/cam2ip/encoder"
"github.com/gen2brain/cam2ip/image"
"github.com/gen2brain/cam2ip/reader"
)
@@ -26,7 +26,7 @@ func NewMJPEG(reader reader.ImageReader, delay int) *MJPEG {
// ServeHTTP handles requests on incoming connections.
func (m *MJPEG) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
return
}
@@ -62,7 +62,7 @@ loop:
continue
}
err = encoder.New(partWriter).Encode(img)
err = image.NewEncoder(partWriter).Encode(img)
if err != nil {
log.Printf("mjpeg: encode: %v", err)
continue

View File

@@ -2,13 +2,14 @@ package handlers
import (
"bytes"
"encoding/base64"
"context"
"log"
"net/http"
"time"
"golang.org/x/net/websocket"
"nhooyr.io/websocket"
"github.com/gen2brain/cam2ip/encoder"
"github.com/gen2brain/cam2ip/image"
"github.com/gen2brain/cam2ip/reader"
)
@@ -19,13 +20,20 @@ type Socket struct {
}
// NewSocket returns new socket handler.
func NewSocket(reader reader.ImageReader, delay int) websocket.Handler {
s := &Socket{reader, delay}
return websocket.Handler(s.write)
func NewSocket(reader reader.ImageReader, delay int) *Socket {
return &Socket{reader, delay}
}
// write writes images to socket
func (s *Socket) write(ws *websocket.Conn) {
// ServeHTTP handles requests on incoming connections.
func (s *Socket) ServeHTTP(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Accept(w, r, nil)
if err != nil {
log.Printf("socket: accept: %v", err)
return
}
ctx := context.Background()
for {
img, err := s.reader.Read()
if err != nil {
@@ -35,19 +43,21 @@ func (s *Socket) write(ws *websocket.Conn) {
w := new(bytes.Buffer)
err = encoder.New(w).Encode(img)
err = image.NewEncoder(w).Encode(img)
if err != nil {
log.Printf("socket: encode: %v", err)
continue
}
b64 := base64.StdEncoding.EncodeToString(w.Bytes())
b64 := image.EncodeToString(w.Bytes())
_, err = ws.Write([]byte(b64))
err = conn.Write(ctx, websocket.MessageText, []byte(b64))
if err != nil {
break
}
time.Sleep(time.Duration(s.delay) * time.Millisecond)
}
conn.Close(websocket.StatusNormalClosure, "")
}

11
image/base64.go Normal file
View File

@@ -0,0 +1,11 @@
// +build !amd64
package image
import (
"encoding/base64"
)
func EncodeToString(src []byte) string {
return base64.StdEncoding.EncodeToString(src)
}

11
image/base64_amd64.go Normal file
View File

@@ -0,0 +1,11 @@
// +build amd64
package image
import (
"goost.org/encoding/base64"
)
func EncodeToString(src []byte) string {
return base64.StdEncoding.EncodeToString(src)
}

26
image/decode.go Normal file
View File

@@ -0,0 +1,26 @@
// +build !jpeg
// Package image.
package image
import (
"image"
"io"
jpeg "github.com/antonini/golibjpegturbo"
)
// NewDecoder returns a new Decoder.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r}
}
// Decoder struct.
type Decoder struct {
r io.Reader
}
// Decode decodes image from JPEG.
func (d Decoder) Decode() (image.Image, error) {
return jpeg.Decode(d.r)
}

25
image/decode_jpeg.go Normal file
View File

@@ -0,0 +1,25 @@
// +build jpeg
// Package image.
package image
import (
"image"
"image/jpeg"
"io"
)
// NewDecoder returns a new Decoder.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r}
}
// Decoder struct.
type Decoder struct {
r io.Reader
}
// Decode decodes image from JPEG.
func (d Decoder) Decode() (image.Image, error) {
return jpeg.Decode(d.r)
}

View File

@@ -1,7 +1,7 @@
// +build !jpeg
// Package encoder.
package encoder
// Package image.
package image
import (
"image"
@@ -10,8 +10,8 @@ import (
jpeg "github.com/antonini/golibjpegturbo"
)
// New returns a new Encoder.
func New(w io.Writer) *Encoder {
// NewEncoder returns a new Encoder.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w}
}

View File

@@ -1,7 +1,7 @@
// +build jpeg
// Package encoder.
package encoder
// Package image.
package image
import (
"image"
@@ -9,8 +9,8 @@ import (
"io"
)
// New returns a new Encoder.
func New(w io.Writer) *Encoder {
// NewEncoder returns a new Encoder.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w}
}

View File

@@ -13,7 +13,14 @@ PKG_CONFIG_PATH="$CHROOT/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$CHROOT/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$CHROOT/usr/lib -L$CHROOT/lib" \
CGO_CFLAGS="-I$CHROOT/usr/include" \
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
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags cv2 -o build/cam2ip.linux.amd64.cv2 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip
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_CFLAGS="-I$CHROOT/usr/include" \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o build/cam2ip.linux.amd64 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip
PKG_CONFIG="/usr/bin/i686-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW/usr/lib/pkgconfig" \
@@ -21,7 +28,15 @@ PKG_CONFIG_LIBDIR="$MINGW/usr/lib/pkgconfig" \
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
CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -tags "cv2 pkgconfig" -o build/cam2ip.386.cv2.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip/cmd/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" \
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 -o build/cam2ip.386.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip/cmd/cam2ip
PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW64/usr/lib/pkgconfig" \
@@ -29,7 +44,15 @@ PKG_CONFIG_LIBDIR="$MINGW64/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$MINGW64/usr/lib" \
CGO_CFLAGS="-I$MINGW64/usr/include" \
CC="x86_64-w64-mingw32-gcc" CXX="x86_64-w64-mingw32-g++" \
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -v -x -o build/cam2ip.exe.amd64 -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -tags "cv2 pkgconfig" -o build/cam2ip.amd64.cv2.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip/cmd/cam2ip
PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW64/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MINGW64/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$MINGW64/usr/lib" \
CGO_CFLAGS="-I$MINGW64/usr/include" \
CC="x86_64-w64-mingw32-gcc" CXX="x86_64-w64-mingw32-g++" \
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o build/cam2ip.amd64.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip/cmd/cam2ip
PKG_CONFIG="/usr/bin/armv6j-hardfloat-linux-gnueabi-pkg-config" \
PKG_CONFIG_PATH="$RPI/usr/lib/pkgconfig" \
@@ -37,7 +60,23 @@ PKG_CONFIG_LIBDIR="$RPI/usr/lib/pkgconfig" \
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.linux.arm -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -tags cv2 -o build/cam2ip.linux.arm.cv2 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip
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" \
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 -o build/cam2ip.linux.arm -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip
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" \
CGO_CFLAGS="-I$RPI/usr/include" \
CC="armv6j-hardfloat-linux-gnueabi-gcc" CXX="armv6j-hardfloat-linux-gnueabi-g++" \
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -tags jpeg -o build/cam2ip.linux.arm.nocgo -ldflags "-s -w" github.com/gen2brain/cam2ip/cmd/cam2ip
PKG_CONFIG="/usr/bin/armv7a-hardfloat-linux-gnueabi-pkg-config" \
PKG_CONFIG_PATH="$RPI3/usr/lib/pkgconfig" \
@@ -45,4 +84,12 @@ PKG_CONFIG_LIBDIR="$RPI3/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$RPI3/usr/lib" \
CGO_CFLAGS="-I$RPI3/usr/include" \
CC="armv7a-hardfloat-linux-gnueabi-gcc" CXX="armv7a-hardfloat-linux-gnueabi-g++" \
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -v -x -o build/cam2ip.linux.arm7 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -tags cv2 -o build/cam2ip.linux.arm7.cv2 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip
PKG_CONFIG="/usr/bin/armv7a-hardfloat-linux-gnueabi-pkg-config" \
PKG_CONFIG_PATH="$RPI3/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$RPI3/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$RPI3/usr/lib" \
CGO_CFLAGS="-I$RPI3/usr/include" \
CC="armv7a-hardfloat-linux-gnueabi-gcc" CXX="armv7a-hardfloat-linux-gnueabi-g++" \
CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -o build/cam2ip.linux.arm7 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip

View File

@@ -28,7 +28,8 @@ type Server struct {
Rotate int
NoWebGL bool
NoWebGL bool
Timestamp bool
FileName string
@@ -49,19 +50,16 @@ 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, s.NoWebGL), basic))
http.Handle("/html", newAuthHandler(handlers.NewHTML(s.FrameWidth, s.FrameHeight, s.NoWebGL), basic))
http.Handle("/jpeg", newAuthHandler(handlers.NewJPEG(s.Reader), basic))
http.Handle("/mjpeg", newAuthHandler(handlers.NewMJPEG(s.Reader, s.Delay), basic))
http.Handle("/socket", handlers.NewSocket(s.Reader, s.Delay))
http.Handle("/socket", newAuthHandler(handlers.NewSocket(s.Reader, s.Delay), basic))
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
http.Handle("/", newAuthHandler(handlers.NewIndex(), basic))
srv := &http.Server{}

View File

@@ -1,4 +1,4 @@
// +build !cv3
// +build cv2,!cv4
// Package video.
package video
@@ -8,7 +8,7 @@ import (
"image"
"github.com/disintegration/imaging"
"github.com/lazywei/go-opencv/opencv"
"github.com/gen2brain/go-opencv/opencv"
)
// Options.
@@ -42,7 +42,7 @@ func (v *Video) Read() (img image.Image, err error) {
if v.video.GrabFrame() {
v.frame = v.video.RetrieveFrame(1)
if v.frame == nil {
err = fmt.Errorf("video: can not grab frame")
err = fmt.Errorf("video: can not retrieve frame")
return
}

View File

@@ -1,4 +1,4 @@
// +build cv3
// +build cv4,!cv2
// Package video.
package video
@@ -49,7 +49,7 @@ func (v *Video) Read() (img image.Image, err error) {
}
if v.frame == nil {
err = fmt.Errorf("video: can not grab frame")
err = fmt.Errorf("video: can not retrieve frame")
return
}

View File

@@ -11,7 +11,7 @@ import (
)
func TestVideo(t *testing.T) {
video, err := New("test.mp4")
video, err := New(video.Options{"test.mp4", 0})
if err != nil {
t.Fatal(err)
}