mirror of
https://github.com/gen2brain/cam2ip.git
synced 2025-12-15 20:08:30 +00:00
Add support for YUYV/YUY2 format
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
package camera
|
package camera
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
// Options .
|
// Options .
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Index int
|
Index int
|
||||||
@@ -10,3 +16,84 @@ type Options struct {
|
|||||||
Timestamp bool
|
Timestamp bool
|
||||||
TimeFormat string
|
TimeFormat string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
yuy2FourCC = fourcc("YUY2")
|
||||||
|
yuyvFourCC = fourcc("YUYV")
|
||||||
|
mjpgFourCC = fourcc("MJPG")
|
||||||
|
)
|
||||||
|
|
||||||
|
func fourcc(b string) uint32 {
|
||||||
|
return uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bmp24ToRgba(data []byte, dst *image.RGBA) error {
|
||||||
|
r := bytes.NewReader(data)
|
||||||
|
|
||||||
|
width := dst.Bounds().Dx()
|
||||||
|
height := dst.Bounds().Dy()
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := dst.Pix[y*dst.Stride : y*dst.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// yuy2ToYCbCr422 converts a YUY2 (YUYV) byte slice to an image.YCbCr with YCbCrSubsampleRatio422 (I422).
|
||||||
|
func yuy2ToYCbCr422(data []byte, dst *image.YCbCr) error {
|
||||||
|
if dst.SubsampleRatio != image.YCbCrSubsampleRatio422 {
|
||||||
|
return fmt.Errorf("subsample ratio must be 422, got %s", dst.SubsampleRatio.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
width := dst.Bounds().Dx()
|
||||||
|
height := dst.Bounds().Dy()
|
||||||
|
|
||||||
|
if width%2 != 0 {
|
||||||
|
return fmt.Errorf("width must be even for YUY2")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) != width*height*2 {
|
||||||
|
return fmt.Errorf("invalid data length for YUY2")
|
||||||
|
}
|
||||||
|
|
||||||
|
stride := width * 2 // 2 bytes per pixel
|
||||||
|
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
for x := 0; x < width; x += 2 {
|
||||||
|
idx := y*stride + x*2
|
||||||
|
|
||||||
|
y0 := data[idx+0]
|
||||||
|
cb := data[idx+1]
|
||||||
|
y1 := data[idx+2]
|
||||||
|
cr := data[idx+3]
|
||||||
|
|
||||||
|
// Y plane: every pixel
|
||||||
|
dst.Y[y*dst.YStride+x+0] = y0
|
||||||
|
dst.Y[y*dst.YStride+x+1] = y1
|
||||||
|
|
||||||
|
// Cb/Cr plane: every 2 pixels (422)
|
||||||
|
off := y*dst.CStride + x/2
|
||||||
|
dst.Cb[off] = cb
|
||||||
|
dst.Cr[off] = cr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ package camera
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"io"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/korandiz/v4l"
|
"github.com/korandiz/v4l"
|
||||||
"github.com/korandiz/v4l/fmt/mjpeg"
|
|
||||||
|
|
||||||
im "github.com/gen2brain/cam2ip/image"
|
im "github.com/gen2brain/cam2ip/image"
|
||||||
)
|
)
|
||||||
@@ -17,12 +18,14 @@ import (
|
|||||||
type Camera struct {
|
type Camera struct {
|
||||||
opts Options
|
opts Options
|
||||||
camera *v4l.Device
|
camera *v4l.Device
|
||||||
|
config v4l.DeviceConfig
|
||||||
|
ycbcr *image.YCbCr
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns new Camera for given camera index.
|
// New returns new Camera for given camera index.
|
||||||
func New(opts Options) (camera *Camera, err error) {
|
func New(opts Options) (c *Camera, err error) {
|
||||||
camera = &Camera{}
|
c = &Camera{}
|
||||||
camera.opts = opts
|
c.opts = opts
|
||||||
|
|
||||||
devices := v4l.FindDevices()
|
devices := v4l.FindDevices()
|
||||||
if len(devices) < opts.Index+1 {
|
if len(devices) < opts.Index+1 {
|
||||||
@@ -31,40 +34,65 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
camera.camera, err = v4l.Open(devices[opts.Index].Path)
|
c.camera, err = v4l.Open(devices[opts.Index].Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("camera: %w", err)
|
err = fmt.Errorf("camera: %w", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if camera.camera == nil {
|
if c.camera == nil {
|
||||||
err = fmt.Errorf("camera: can not open camera %d", opts.Index)
|
err = fmt.Errorf("camera: can not open camera %d", opts.Index)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := camera.camera.GetConfig()
|
configs, e := c.camera.ListConfigs()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
err = fmt.Errorf("camera: %w", err)
|
err = fmt.Errorf("camera: can not list configs: %w", e)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Format = mjpeg.FourCC
|
formats := make([]uint32, 0)
|
||||||
config.Width = int(opts.Width)
|
for _, config := range configs {
|
||||||
config.Height = int(opts.Height)
|
formats = append(formats, config.Format)
|
||||||
|
}
|
||||||
|
|
||||||
err = camera.camera.SetConfig(config)
|
c.config, err = c.camera.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("camera: %w", err)
|
err = fmt.Errorf("camera: can not get config: %w", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = camera.camera.TurnOn()
|
if slices.Contains(formats, mjpgFourCC) {
|
||||||
|
c.config.Format = mjpgFourCC
|
||||||
|
} else if slices.Contains(formats, yuyvFourCC) {
|
||||||
|
c.config.Format = yuyvFourCC
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("camera: unsupported format %d", c.config.Format)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.config.Width = int(opts.Width)
|
||||||
|
c.config.Height = int(opts.Height)
|
||||||
|
|
||||||
|
err = c.camera.SetConfig(c.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("camera: %w", err)
|
err = fmt.Errorf("camera: format %d: can not set config: %w", c.config.Format, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.config.Format == yuyvFourCC {
|
||||||
|
c.ycbcr = image.NewYCbCr(image.Rect(0, 0, int(c.opts.Width), int(c.opts.Height)), image.YCbCrSubsampleRatio422)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.camera.TurnOn()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("camera: format %d: can not turn on: %w", c.config.Format, err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -76,16 +104,35 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
func (c *Camera) Read() (img image.Image, err error) {
|
func (c *Camera) Read() (img image.Image, err error) {
|
||||||
buffer, err := c.camera.Capture()
|
buffer, err := c.camera.Capture()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("camera: can not grab frame: %w", err)
|
err = fmt.Errorf("camera: format %d: can not grab frame: %w", c.config.Format, err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err = im.NewDecoder(buffer).Decode()
|
switch c.config.Format {
|
||||||
if err != nil {
|
case yuy2FourCC, yuyvFourCC:
|
||||||
err = fmt.Errorf("camera: %w", err)
|
data, e := io.ReadAll(buffer)
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("camera: format %d: can not read buffer: %w", c.config.Format, e)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e = yuy2ToYCbCr422(data, c.ycbcr)
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.config.Format, e)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
img = c.ycbcr
|
||||||
|
case mjpgFourCC:
|
||||||
|
img, err = im.NewDecoder(buffer).Decode()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("camera: format %d: can not decode frame: %w", c.config.Format, err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.opts.Rotate != 0 {
|
if c.opts.Rotate != 0 {
|
||||||
@@ -97,7 +144,7 @@ func (c *Camera) Read() (img image.Image, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.opts.Timestamp {
|
if c.opts.Timestamp {
|
||||||
img, err = im.Timestamp(img, "")
|
img, err = im.Timestamp(img, c.opts.TimeFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -106,7 +153,7 @@ func (c *Camera) Read() (img image.Image, err error) {
|
|||||||
// Close closes camera.
|
// Close closes camera.
|
||||||
func (c *Camera) Close() (err error) {
|
func (c *Camera) Close() (err error) {
|
||||||
if c.camera == nil {
|
if c.camera == nil {
|
||||||
err = fmt.Errorf("camera: camera is not opened")
|
err = fmt.Errorf("camera: close: camera is not opened")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (c *Camera) Read() (img image.Image, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.opts.Timestamp {
|
if c.opts.Timestamp {
|
||||||
img, err = im.Timestamp(img, "")
|
img, err = im.Timestamp(img, c.opts.TimeFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestCamera(t *testing.T) {
|
func TestCamera(t *testing.T) {
|
||||||
camera, err := New(Options{0, 0, 640, 480, false})
|
camera, err := New(Options{0, 0, "", 640, 480, false, ""})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ func TestCamera(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = image.NewEncoder(io.Discard).Encode(img)
|
err = image.NewEncoder(io.Discard, 75).Encode(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ func init() {
|
|||||||
type Camera struct {
|
type Camera struct {
|
||||||
opts Options
|
opts Options
|
||||||
camera syscall.Handle
|
camera syscall.Handle
|
||||||
frame *image.RGBA
|
rgba *image.RGBA
|
||||||
|
ycbcr *image.YCbCr
|
||||||
hdr *videoHdr
|
hdr *videoHdr
|
||||||
instance syscall.Handle
|
instance syscall.Handle
|
||||||
className string
|
className string
|
||||||
|
format uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns new Camera for given camera index.
|
// New returns new Camera for given camera index.
|
||||||
@@ -34,8 +36,6 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
camera.opts = opts
|
camera.opts = opts
|
||||||
camera.className = "capWindowClass"
|
camera.className = "capWindowClass"
|
||||||
|
|
||||||
camera.frame = image.NewRGBA(image.Rect(0, 0, int(camera.opts.Width), int(camera.opts.Height)))
|
|
||||||
|
|
||||||
go func(c *Camera) {
|
go func(c *Camera) {
|
||||||
fn := func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
|
fn := func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
|
||||||
switch msg {
|
switch msg {
|
||||||
@@ -45,6 +45,7 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
postQuitMessage(0)
|
postQuitMessage(0)
|
||||||
default:
|
default:
|
||||||
ret := defWindowProc(hwnd, msg, wparam, lparam)
|
ret := defWindowProc(hwnd, msg, wparam, lparam)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,9 +62,11 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hwnd, err := createWindow(0, c.className, "", wsOverlappedWindow, cwUseDefault, cwUseDefault,
|
hwnd, e := createWindow(0, c.className, "", wsOverlappedWindow, cwUseDefault, cwUseDefault,
|
||||||
int64(c.opts.Width)+100, int64(c.opts.Height)+100, 0, 0, c.instance)
|
int64(c.opts.Width)+100, int64(c.opts.Height)+100, 0, 0, c.instance)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
|
err = e
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,9 +78,13 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
ret := sendMessage(c.camera, wmCapDriverConnect, uintptr(c.opts.Index), 0)
|
ret := sendMessage(c.camera, wmCapDriverConnect, uintptr(c.opts.Index), 0)
|
||||||
if int(ret) == 0 {
|
if int(ret) == 0 {
|
||||||
err = fmt.Errorf("camera: can not open camera %d", c.opts.Index)
|
err = fmt.Errorf("camera: can not open camera %d", c.opts.Index)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMessage(c.camera, wmCapSetPreview, 0, 0)
|
||||||
|
sendMessage(c.camera, wmCapSetOverlay, 0, 0)
|
||||||
|
|
||||||
var bi bitmapInfo
|
var bi bitmapInfo
|
||||||
size := sendMessage(c.camera, wmCapGetVideoformat, 0, 0)
|
size := sendMessage(c.camera, wmCapGetVideoformat, 0, 0)
|
||||||
sendMessage(c.camera, wmCapGetVideoformat, size, uintptr(unsafe.Pointer(&bi)))
|
sendMessage(c.camera, wmCapGetVideoformat, size, uintptr(unsafe.Pointer(&bi)))
|
||||||
@@ -87,17 +94,36 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
|
|
||||||
ret = sendMessage(c.camera, wmCapSetVideoformat, size, uintptr(unsafe.Pointer(&bi)))
|
ret = sendMessage(c.camera, wmCapSetVideoformat, size, uintptr(unsafe.Pointer(&bi)))
|
||||||
if int(ret) == 0 {
|
if int(ret) == 0 {
|
||||||
err = fmt.Errorf("camera: can not set video format")
|
err = fmt.Errorf("camera: can not set video format: %dx%d, %d", int(c.opts.Width), int(c.opts.Height), c.format)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.format = bi.BmiHeader.BiCompression
|
||||||
sendMessage(c.camera, wmCapSetCallbackFrame, 0, syscall.NewCallback(c.callback))
|
sendMessage(c.camera, wmCapSetCallbackFrame, 0, syscall.NewCallback(c.callback))
|
||||||
|
|
||||||
|
switch c.format {
|
||||||
|
case 0:
|
||||||
|
if bi.BmiHeader.BiBitCount != 24 {
|
||||||
|
err = fmt.Errorf("camera: unsupported format %d; bitcount: %d", c.format, bi.BmiHeader.BiBitCount)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.rgba = image.NewRGBA(image.Rect(0, 0, int(c.opts.Width), int(c.opts.Height)))
|
||||||
|
case yuy2FourCC, yuyvFourCC:
|
||||||
|
c.ycbcr = image.NewYCbCr(image.Rect(0, 0, int(c.opts.Width), int(c.opts.Height)), image.YCbCrSubsampleRatio422)
|
||||||
|
case mjpgFourCC:
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("camera: unsupported format %d", c.format)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var msg msgW
|
var msg msgW
|
||||||
ok, _ := getMessage(&msg, hwnd, 0, 0)
|
ok, _ := getMessage(&msg, 0, 0, 0)
|
||||||
if ok {
|
if ok {
|
||||||
//translateMessage(&msg)
|
|
||||||
dispatchMessage(&msg)
|
dispatchMessage(&msg)
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
@@ -112,40 +138,44 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
|
|
||||||
// Read reads next frame from camera and returns image.
|
// Read reads next frame from camera and returns image.
|
||||||
func (c *Camera) Read() (img image.Image, err error) {
|
func (c *Camera) Read() (img image.Image, err error) {
|
||||||
ret := sendMessage(c.camera, wmCapGrabFrameNoStop, 0, 0)
|
ret := sendMessage(c.camera, wmCapGrabFrame, 0, 0)
|
||||||
if int(ret) == 0 {
|
if int(ret) == 0 {
|
||||||
err = fmt.Errorf("camera: can not grab frame")
|
err = fmt.Errorf("camera: can not grab frame")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := (*[1 << 24]uint8)(unsafe.Pointer(c.hdr.LpData))[0:c.hdr.DwBytesUsed]
|
data := unsafe.Slice((*byte)(unsafe.Pointer(c.hdr.LpData)), c.hdr.DwBufferLength)
|
||||||
r := bytes.NewReader(data)
|
|
||||||
|
|
||||||
width := int(c.opts.Width)
|
switch c.format {
|
||||||
height := int(c.opts.Height)
|
case 0:
|
||||||
|
e := bmp24ToRgba(data, c.rgba)
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.format, e)
|
||||||
|
|
||||||
// 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: %w", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p := c.frame.Pix[y*c.frame.Stride : y*c.frame.Stride+width*4]
|
img = c.rgba
|
||||||
for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
|
case yuy2FourCC, yuyvFourCC:
|
||||||
// BMP images are stored in BGR order rather than RGB order.
|
e := yuy2ToYCbCr422(data, c.ycbcr)
|
||||||
p[i+0] = b[j+2]
|
if e != nil {
|
||||||
p[i+1] = b[j+1]
|
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.format, e)
|
||||||
p[i+2] = b[j+0]
|
|
||||||
p[i+3] = 0xFF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img = c.frame
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
img = c.ycbcr
|
||||||
|
case mjpgFourCC:
|
||||||
|
i, e := im.NewDecoder(bytes.NewReader(data)).Decode()
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("camera: format %d: can not retrieve frame: %w", c.format, e)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
img = i
|
||||||
|
}
|
||||||
|
|
||||||
if c.opts.Rotate != 0 {
|
if c.opts.Rotate != 0 {
|
||||||
img = im.Rotate(img, c.opts.Rotate)
|
img = im.Rotate(img, c.opts.Rotate)
|
||||||
@@ -173,7 +203,9 @@ func (c *Camera) Close() (err error) {
|
|||||||
|
|
||||||
// callback function.
|
// callback function.
|
||||||
func (c *Camera) callback(hwnd syscall.Handle, hdr *videoHdr) uintptr {
|
func (c *Camera) callback(hwnd syscall.Handle, hdr *videoHdr) uintptr {
|
||||||
c.hdr = hdr
|
if hdr != nil {
|
||||||
|
c.hdr = hdr
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -183,16 +215,15 @@ var (
|
|||||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
avicap32 = syscall.NewLazyDLL("avicap32.dll")
|
avicap32 = syscall.NewLazyDLL("avicap32.dll")
|
||||||
|
|
||||||
createWindowExW = user32.NewProc("CreateWindowExW")
|
createWindowExW = user32.NewProc("CreateWindowExW")
|
||||||
destroyWindowW = user32.NewProc("DestroyWindow")
|
destroyWindowW = user32.NewProc("DestroyWindow")
|
||||||
defWindowProcW = user32.NewProc("DefWindowProcW")
|
defWindowProcW = user32.NewProc("DefWindowProcW")
|
||||||
dispatchMessageW = user32.NewProc("DispatchMessageW")
|
dispatchMessageW = user32.NewProc("DispatchMessageW")
|
||||||
translateMessageW = user32.NewProc("TranslateMessage")
|
getMessageW = user32.NewProc("GetMessageW")
|
||||||
getMessageW = user32.NewProc("GetMessageW")
|
sendMessageW = user32.NewProc("SendMessageW")
|
||||||
sendMessageW = user32.NewProc("SendMessageW")
|
postQuitMessageW = user32.NewProc("PostQuitMessage")
|
||||||
postQuitMessageW = user32.NewProc("PostQuitMessage")
|
registerClassExW = user32.NewProc("RegisterClassExW")
|
||||||
registerClassExW = user32.NewProc("RegisterClassExW")
|
unregisterClassW = user32.NewProc("UnregisterClassW")
|
||||||
unregisterClassW = user32.NewProc("UnregisterClassW")
|
|
||||||
|
|
||||||
getModuleHandleW = kernel32.NewProc("GetModuleHandleW")
|
getModuleHandleW = kernel32.NewProc("GetModuleHandleW")
|
||||||
capCreateCaptureWindowW = avicap32.NewProc("capCreateCaptureWindowW")
|
capCreateCaptureWindowW = avicap32.NewProc("capCreateCaptureWindowW")
|
||||||
@@ -209,6 +240,8 @@ const (
|
|||||||
wmCapDriverDisconnect = wmCapStart + 11
|
wmCapDriverDisconnect = wmCapStart + 11
|
||||||
wmCapGetVideoformat = wmCapStart + 44
|
wmCapGetVideoformat = wmCapStart + 44
|
||||||
wmCapSetVideoformat = wmCapStart + 45
|
wmCapSetVideoformat = wmCapStart + 45
|
||||||
|
wmCapSetPreview = wmCapStart + 50
|
||||||
|
wmCapSetOverlay = wmCapStart + 51
|
||||||
wmCapGrabFrame = wmCapStart + 60
|
wmCapGrabFrame = wmCapStart + 60
|
||||||
wmCapGrabFrameNoStop = wmCapStart + 61
|
wmCapGrabFrameNoStop = wmCapStart + 61
|
||||||
wmCapStop = wmCapStart + 68
|
wmCapStop = wmCapStart + 68
|
||||||
@@ -337,11 +370,6 @@ func dispatchMessage(msg *msgW) {
|
|||||||
dispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
|
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
|
// 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) {
|
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))
|
ret, _, err := getMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(msgFilterMin), uintptr(msgFilterMax))
|
||||||
|
|||||||
Reference in New Issue
Block a user