From d5870110d95b377858bfc9686f2b1398f526c20f Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 29 Oct 2018 16:27:46 +0100 Subject: [PATCH] Add support for native VFW implementation on Windows --- README.md | 25 +- cam2ip.go | 28 +- cam2ip_native.go => cam2ip_cv.go | 28 +- .../{camera_const.go => camera_const_cv.go} | 2 +- ..._const_native.go => camera_const_linux.go} | 2 +- camera/{camera.go => camera_cv2.go} | 2 +- camera/camera_cv3.go | 2 +- ...camera_native_linux.go => camera_linux.go} | 2 +- camera/camera_windows.go | 412 ++++++++++++++++++ make.bash | 38 +- video/{video.go => video_cv2.go} | 2 +- video/video_cv3.go | 2 +- 12 files changed, 494 insertions(+), 51 deletions(-) rename cam2ip_native.go => cam2ip_cv.go (70%) rename camera/{camera_const.go => camera_const_cv.go} (97%) rename camera/{camera_const_native.go => camera_const_linux.go} (96%) rename camera/{camera.go => camera_cv2.go} (98%) rename camera/{camera_native_linux.go => camera_linux.go} (99%) create mode 100644 camera/camera_windows.go rename video/{video.go => video_cv2.go} (98%) diff --git a/README.md b/README.md index dc14369..3bb6de2 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,19 @@ or ### Requirements -* [OpenCV](http://opencv.org/) -* [libjpeg-turbo](https://www.libjpeg-turbo.org/) +* [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. + +### Installation + + go get -u github.com/gen2brain/cam2ip ### Build tags -* `cv3` - build with `OpenCV` 3.x [gocv](https://github.com/hybridgroup/gocv), default is version 2.x via [go-opencv](https://github.com/lazywei/go-opencv) -* `native` - build with native Go [V4L](https://github.com/korandiz/v4l) implementation on Linux/RPi instead of `OpenCV` +* `cv2` - build with `OpenCV` 2.x ([go-opencv](https://github.com/lazywei/go-opencv)) +* `cv3` - build with `OpenCV` 3.x ([gocv](https://github.com/hybridgroup/gocv)) * `jpeg` - build with native Go `image/jpeg` instead of `libjpeg-turbo` ### Download @@ -25,13 +31,16 @@ or Binaries are compiled with static OpenCV/libjpeg-turbo libraries, they should just work: - [Linux 64bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-64bit.tar.gz) - - [Linux 64bit native](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-64bit-native.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 native](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi-native.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 native](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5-RPi3-native.tar.gz) - - [Windows 32bit](https://github.com/gen2brain/cam2ip/releases/download/1.5/cam2ip-1.5.zip) + - [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 diff --git a/cam2ip.go b/cam2ip.go index 8ec2a96..3d3bd95 100644 --- a/cam2ip.go +++ b/cam2ip.go @@ -1,5 +1,3 @@ -// +build !native - package main import ( @@ -9,12 +7,11 @@ import ( "github.com/gen2brain/cam2ip/camera" "github.com/gen2brain/cam2ip/server" - "github.com/gen2brain/cam2ip/video" ) const ( name = "cam2ip" - version = "1.4" + version = "1.5" ) func main() { @@ -28,7 +25,6 @@ func main() { flag.BoolVar(&srv.NoWebGL, "nowebgl", false, "Disable WebGL drawing of images (html handler)") 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") flag.Parse() srv.Name = name @@ -50,24 +46,14 @@ func main() { } } - if srv.FileName != "" { - vid, err := video.New(video.Options{srv.FileName, srv.Rotate}) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err.Error()) - os.Exit(1) - } - - srv.Reader = vid - } else { - cam, err := camera.New(camera.Options{srv.Index, srv.Rotate, srv.FrameWidth, srv.FrameHeight}) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err.Error()) - os.Exit(1) - } - - srv.Reader = cam + cam, err := camera.New(camera.Options{srv.Index, srv.Rotate, srv.FrameWidth, srv.FrameHeight}) + 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) diff --git a/cam2ip_native.go b/cam2ip_cv.go similarity index 70% rename from cam2ip_native.go rename to cam2ip_cv.go index 33e8ee8..1f73022 100644 --- a/cam2ip_native.go +++ b/cam2ip_cv.go @@ -1,4 +1,4 @@ -// +build native +// +build cv2,cv3 package main @@ -9,11 +9,12 @@ import ( "github.com/gen2brain/cam2ip/camera" "github.com/gen2brain/cam2ip/server" + "github.com/gen2brain/cam2ip/video" ) const ( name = "cam2ip" - version = "1.4" + version = "1.5" ) func main() { @@ -27,6 +28,7 @@ func main() { flag.BoolVar(&srv.NoWebGL, "nowebgl", false, "Disable WebGL drawing of images (html handler)") 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") flag.Parse() srv.Name = name @@ -48,13 +50,23 @@ func main() { } } - cam, err := camera.New(camera.Options{srv.Index, srv.Rotate, srv.FrameWidth, srv.FrameHeight}) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err.Error()) - os.Exit(1) - } + if srv.FileName != "" { + vid, err := video.New(video.Options{srv.FileName, srv.Rotate}) + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } + + srv.Reader = vid + } else { + cam, err := camera.New(camera.Options{srv.Index, srv.Rotate, srv.FrameWidth, srv.FrameHeight}) + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + os.Exit(1) + } - srv.Reader = cam + srv.Reader = cam + } defer srv.Reader.Close() diff --git a/camera/camera_const.go b/camera/camera_const_cv.go similarity index 97% rename from camera/camera_const.go rename to camera/camera_const_cv.go index 5d5a756..d18c1bd 100644 --- a/camera/camera_const.go +++ b/camera/camera_const_cv.go @@ -1,4 +1,4 @@ -// +build !native +// +build cv2 cv3 package camera diff --git a/camera/camera_const_native.go b/camera/camera_const_linux.go similarity index 96% rename from camera/camera_const_native.go rename to camera/camera_const_linux.go index 1eb1e83..4e7fce8 100644 --- a/camera/camera_const_native.go +++ b/camera/camera_const_linux.go @@ -1,4 +1,4 @@ -// +build native +// +build !cv2,!cv3 package camera diff --git a/camera/camera.go b/camera/camera_cv2.go similarity index 98% rename from camera/camera.go rename to camera/camera_cv2.go index 808e5ef..909cd20 100644 --- a/camera/camera.go +++ b/camera/camera_cv2.go @@ -1,4 +1,4 @@ -// +build !cv3,!native +// +build cv2,!cv3 // Package camera. package camera diff --git a/camera/camera_cv3.go b/camera/camera_cv3.go index 546cca1..822c9f1 100644 --- a/camera/camera_cv3.go +++ b/camera/camera_cv3.go @@ -1,4 +1,4 @@ -// +build cv3,!native +// +build cv3,!cv2 // Package camera. package camera diff --git a/camera/camera_native_linux.go b/camera/camera_linux.go similarity index 99% rename from camera/camera_native_linux.go rename to camera/camera_linux.go index d26aeb8..fde7e6e 100644 --- a/camera/camera_native_linux.go +++ b/camera/camera_linux.go @@ -1,4 +1,4 @@ -// +build native +// +build !cv2,!cv3 // Package camera. package camera diff --git a/camera/camera_windows.go b/camera/camera_windows.go new file mode 100644 index 0000000..ce813ba --- /dev/null +++ b/camera/camera_windows.go @@ -0,0 +1,412 @@ +// +build !cv2,!cv3 + +// Package camera. +package camera + +import ( + "bytes" + "fmt" + "image" + "syscall" + "unsafe" + + "github.com/disintegration/imaging" +) + +// Options. +type Options struct { + Index int + Rotate int + Width float64 + Height float64 +} + +// 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 + 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) + } + + 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 +} diff --git a/make.bash b/make.bash index fa11a7c..2e8c009 100755 --- a/make.bash +++ b/make.bash @@ -13,14 +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 -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 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 -tags native -o build/cam2ip.linux.native.amd64 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip +CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -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" \ @@ -28,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 -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 -o build/cam2ip.386.cv2.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" 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" \ +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 PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \ PKG_CONFIG_PATH="$MINGW64/usr/lib/pkgconfig" \ @@ -36,7 +44,23 @@ 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.exe.amd64 -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/cam2ip +CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -tags cv2 -o build/cam2ip.amd64.cv2.exe -ldflags "-linkmode external -s -w '-extldflags=-static'" github.com/gen2brain/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 + +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 -tags cv2 -o build/cam2ip.linux.arm.cv2 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip PKG_CONFIG="/usr/bin/armv6j-hardfloat-linux-gnueabi-pkg-config" \ PKG_CONFIG_PATH="$RPI/usr/lib/pkgconfig" \ @@ -52,7 +76,7 @@ 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 -tags native -o build/cam2ip.linux.native.arm -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip +CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -tags jpeg -o build/cam2ip.linux.arm.nocgo -ldflags "-s -w" github.com/gen2brain/cam2ip PKG_CONFIG="/usr/bin/armv7a-hardfloat-linux-gnueabi-pkg-config" \ PKG_CONFIG_PATH="$RPI3/usr/lib/pkgconfig" \ @@ -60,7 +84,7 @@ 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 +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 PKG_CONFIG="/usr/bin/armv7a-hardfloat-linux-gnueabi-pkg-config" \ PKG_CONFIG_PATH="$RPI3/usr/lib/pkgconfig" \ @@ -68,4 +92,4 @@ 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 -tags native -o build/cam2ip.linux.native.arm7 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip +CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -o build/cam2ip.linux.arm7 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip diff --git a/video/video.go b/video/video_cv2.go similarity index 98% rename from video/video.go rename to video/video_cv2.go index 55338f3..a11ad53 100644 --- a/video/video.go +++ b/video/video_cv2.go @@ -1,4 +1,4 @@ -// +build !cv3,!native +// +build cv2,!cv3 // Package video. package video diff --git a/video/video_cv3.go b/video/video_cv3.go index ae1bb26..2ba1254 100644 --- a/video/video_cv3.go +++ b/video/video_cv3.go @@ -1,4 +1,4 @@ -// +build cv3,!native +// +build cv3,!cv2 // Package video. package video