mirror of
https://github.com/gen2brain/cam2ip.git
synced 2025-12-13 02:48:31 +00:00
Add some options
This commit is contained in:
10
README.md
10
README.md
@@ -51,12 +51,18 @@ Usage: cam2ip [<flags>]
|
||||
Frame width [CAM2IP_WIDTH] (default "640")
|
||||
--height
|
||||
Frame height [CAM2IP_HEIGHT] (default "480")
|
||||
--quality
|
||||
Image quality [CAM2IP_QUALITY] (default "75")
|
||||
--rotate
|
||||
Rotate image, valid values are 90, 180, 270 [CAM2IP_ROTATE] (default "0")
|
||||
--flip
|
||||
Flip image, valid values are horizontal and vertical [CAM2IP_FLIP] (default "")
|
||||
--no-webgl
|
||||
Disable WebGL drawing of images (html handler) [CAM2IP_NO_WEBGL] (default "false")
|
||||
Disable WebGL drawing of image (html handler) [CAM2IP_NO_WEBGL] (default "false")
|
||||
--timestamp
|
||||
Draws timestamp on images [CAM2IP_TIMESTAMP] (default "false")
|
||||
Draws timestamp on image [CAM2IP_TIMESTAMP] (default "false")
|
||||
--time-format
|
||||
Time format [CAM2IP_TIME_FORMAT] (default "2006-01-02 15:04:05")
|
||||
--bind-addr
|
||||
Bind address [CAM2IP_BIND_ADDR] (default ":56000")
|
||||
--htpasswd-file
|
||||
|
||||
@@ -2,9 +2,11 @@ package camera
|
||||
|
||||
// Options .
|
||||
type Options struct {
|
||||
Index int
|
||||
Rotate int
|
||||
Width float64
|
||||
Height float64
|
||||
Timestamp bool
|
||||
Index int
|
||||
Rotate int
|
||||
Flip string
|
||||
Width float64
|
||||
Height float64
|
||||
Timestamp bool
|
||||
TimeFormat string
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ func (c *Camera) Read() (img image.Image, err error) {
|
||||
img = im.Rotate(img, c.opts.Rotate)
|
||||
}
|
||||
|
||||
if c.opts.Flip != "" {
|
||||
img = im.Flip(img, c.opts.Flip)
|
||||
}
|
||||
|
||||
if c.opts.Timestamp {
|
||||
img, err = im.Timestamp(img, "")
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ func (c *Camera) Read() (img image.Image, err error) {
|
||||
img = im.Rotate(img, c.opts.Rotate)
|
||||
}
|
||||
|
||||
if c.opts.Flip != "" {
|
||||
img = im.Flip(img, c.opts.Flip)
|
||||
}
|
||||
|
||||
if c.opts.Timestamp {
|
||||
img, err = im.Timestamp(img, "")
|
||||
}
|
||||
|
||||
@@ -34,11 +34,6 @@ func New(opts Options) (camera *Camera, err error) {
|
||||
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) {
|
||||
@@ -56,12 +51,18 @@ func New(opts Options) (camera *Camera, err error) {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.instance, err = getModuleHandle()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
}
|
||||
@@ -92,7 +93,18 @@ func New(opts Options) (camera *Camera, err error) {
|
||||
|
||||
sendMessage(c.camera, wmCapSetCallbackFrame, 0, syscall.NewCallback(c.callback))
|
||||
|
||||
messageLoop(c.camera)
|
||||
for {
|
||||
var msg msgW
|
||||
ok, _ := getMessage(&msg, hwnd, 0, 0)
|
||||
if ok {
|
||||
//translateMessage(&msg)
|
||||
dispatchMessage(&msg)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}(camera)
|
||||
|
||||
return
|
||||
@@ -139,20 +151,14 @@ func (c *Camera) Read() (img image.Image, err error) {
|
||||
img = im.Rotate(img, c.opts.Rotate)
|
||||
}
|
||||
|
||||
if c.opts.Timestamp {
|
||||
img, err = im.Timestamp(img, "")
|
||||
if c.opts.Flip != "" {
|
||||
img = im.Flip(img, c.opts.Flip)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if c.opts.Timestamp {
|
||||
img, err = im.Timestamp(img, c.opts.TimeFormat)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -321,8 +327,9 @@ func destroyWindow(hwnd syscall.Handle) error {
|
||||
|
||||
// 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)
|
||||
ret, _, _ := defWindowProcW.Call(uintptr(hwnd), uintptr(msg), wparam, lparam)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-dispatchmessagew
|
||||
@@ -348,6 +355,7 @@ func getMessage(msg *msgW, hwnd syscall.Handle, msgFilterMin, msgFilterMax uint3
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -375,6 +383,7 @@ func registerClass(className string, instance syscall.Handle, fn interface{}) er
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -388,19 +397,3 @@ func capCreateCaptureWindow(lpszWindowName string, dwStyle, x, y, width, height
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -21,17 +21,21 @@ func main() {
|
||||
|
||||
flag.IntVar(&srv.Index, "index", 0, "Camera index [CAM2IP_INDEX]")
|
||||
flag.IntVar(&srv.Delay, "delay", 10, "Delay between frames, in milliseconds [CAM2IP_DELAY]")
|
||||
flag.Float64Var(&srv.FrameWidth, "width", 640, "Frame width [CAM2IP_WIDTH]")
|
||||
flag.Float64Var(&srv.FrameHeight, "height", 480, "Frame height [CAM2IP_HEIGHT]")
|
||||
flag.Float64Var(&srv.Width, "width", 640, "Frame width [CAM2IP_WIDTH]")
|
||||
flag.Float64Var(&srv.Height, "height", 480, "Frame height [CAM2IP_HEIGHT]")
|
||||
flag.IntVar(&srv.Quality, "quality", 75, "Image quality [CAM2IP_QUALITY]")
|
||||
flag.IntVar(&srv.Rotate, "rotate", 0, "Rotate image, valid values are 90, 180, 270 [CAM2IP_ROTATE]")
|
||||
flag.BoolVar(&srv.NoWebGL, "no-webgl", false, "Disable WebGL drawing of images (html handler) [CAM2IP_NO_WEBGL]")
|
||||
flag.BoolVar(&srv.Timestamp, "timestamp", false, "Draws timestamp on images [CAM2IP_TIMESTAMP]")
|
||||
flag.StringVar(&srv.Flip, "flip", "", "Flip image, valid values are horizontal and vertical [CAM2IP_FLIP]")
|
||||
flag.BoolVar(&srv.NoWebGL, "no-webgl", false, "Disable WebGL drawing of image (html handler) [CAM2IP_NO_WEBGL]")
|
||||
flag.BoolVar(&srv.Timestamp, "timestamp", false, "Draws timestamp on image [CAM2IP_TIMESTAMP]")
|
||||
flag.StringVar(&srv.TimeFormat, "time-format", "2006-01-02 15:04:05", "Time format [CAM2IP_TIME_FORMAT]")
|
||||
flag.StringVar(&srv.Bind, "bind-addr", ":56000", "Bind address [CAM2IP_BIND_ADDR]")
|
||||
flag.StringVar(&srv.Htpasswd, "htpasswd-file", "", "Path to htpasswd file, if empty auth is disabled [CAM2IP_HTPASSWD_FILE]")
|
||||
|
||||
flag.Usage = func() {
|
||||
stderr("Usage: %s [<flags>]\n", name)
|
||||
order := []string{"index", "delay", "width", "height", "rotate", "no-webgl", "timestamp", "bind-addr", "htpasswd-file"}
|
||||
order := []string{"index", "delay", "width", "height", "quality", "rotate", "flip", "no-webgl",
|
||||
"timestamp", "time-format", "bind-addr", "htpasswd-file"}
|
||||
|
||||
for _, name := range order {
|
||||
f := flag.Lookup(name)
|
||||
@@ -55,11 +59,13 @@ func main() {
|
||||
}
|
||||
|
||||
cam, err := camera.New(camera.Options{
|
||||
Index: srv.Index,
|
||||
Rotate: srv.Rotate,
|
||||
Width: srv.FrameWidth,
|
||||
Height: srv.FrameHeight,
|
||||
Timestamp: srv.Timestamp,
|
||||
Index: srv.Index,
|
||||
Rotate: srv.Rotate,
|
||||
Flip: srv.Flip,
|
||||
Width: srv.Width,
|
||||
Height: srv.Height,
|
||||
Timestamp: srv.Timestamp,
|
||||
TimeFormat: srv.TimeFormat,
|
||||
})
|
||||
if err != nil {
|
||||
stderr("%s\n", err.Error())
|
||||
|
||||
@@ -12,11 +12,11 @@ type HTML struct {
|
||||
}
|
||||
|
||||
// NewHTML returns new HTML handler.
|
||||
func NewHTML(width, height float64, nogl bool) *HTML {
|
||||
func NewHTML(width, height float64, noWebGL bool) *HTML {
|
||||
h := &HTML{}
|
||||
|
||||
tpl := htmlWebGL
|
||||
if nogl {
|
||||
if noWebGL {
|
||||
tpl = html
|
||||
}
|
||||
tpl = strings.Replace(tpl, "{WIDTH}", fmt.Sprintf("%.0f", width), -1)
|
||||
|
||||
@@ -9,12 +9,13 @@ import (
|
||||
|
||||
// JPEG handler.
|
||||
type JPEG struct {
|
||||
reader ImageReader
|
||||
reader ImageReader
|
||||
quality int
|
||||
}
|
||||
|
||||
// NewJPEG returns new JPEG handler.
|
||||
func NewJPEG(reader ImageReader) *JPEG {
|
||||
return &JPEG{reader}
|
||||
func NewJPEG(reader ImageReader, quality int) *JPEG {
|
||||
return &JPEG{reader, quality}
|
||||
}
|
||||
|
||||
// ServeHTTP handles requests on incoming connections.
|
||||
@@ -36,7 +37,7 @@ func (j *JPEG) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = image.NewEncoder(w).Encode(img)
|
||||
err = image.NewEncoder(w, j.quality).Encode(img)
|
||||
if err != nil {
|
||||
log.Printf("jpeg: encode: %v", err)
|
||||
|
||||
|
||||
@@ -13,13 +13,14 @@ import (
|
||||
|
||||
// MJPEG handler.
|
||||
type MJPEG struct {
|
||||
reader ImageReader
|
||||
delay int
|
||||
reader ImageReader
|
||||
delay int
|
||||
quality int
|
||||
}
|
||||
|
||||
// NewMJPEG returns new MJPEG handler.
|
||||
func NewMJPEG(reader ImageReader, delay int) *MJPEG {
|
||||
return &MJPEG{reader, delay}
|
||||
func NewMJPEG(reader ImageReader, delay, quality int) *MJPEG {
|
||||
return &MJPEG{reader, delay, quality}
|
||||
}
|
||||
|
||||
// ServeHTTP handles requests on incoming connections.
|
||||
@@ -61,7 +62,7 @@ loop:
|
||||
continue
|
||||
}
|
||||
|
||||
err = image.NewEncoder(partWriter).Encode(img)
|
||||
err = image.NewEncoder(partWriter, m.quality).Encode(img)
|
||||
if err != nil {
|
||||
log.Printf("mjpeg: encode: %v", err)
|
||||
continue
|
||||
|
||||
@@ -14,13 +14,14 @@ import (
|
||||
|
||||
// Socket handler.
|
||||
type Socket struct {
|
||||
reader ImageReader
|
||||
delay int
|
||||
reader ImageReader
|
||||
delay int
|
||||
quality int
|
||||
}
|
||||
|
||||
// NewSocket returns new socket handler.
|
||||
func NewSocket(reader ImageReader, delay int) *Socket {
|
||||
return &Socket{reader, delay}
|
||||
func NewSocket(reader ImageReader, delay, quality int) *Socket {
|
||||
return &Socket{reader, delay, quality}
|
||||
}
|
||||
|
||||
// ServeHTTP handles requests on incoming connections.
|
||||
@@ -43,7 +44,7 @@ func (s *Socket) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w := new(bytes.Buffer)
|
||||
|
||||
err = image.NewEncoder(w).Encode(img)
|
||||
err = image.NewEncoder(w, s.quality).Encode(img)
|
||||
if err != nil {
|
||||
log.Printf("socket: encode: %v", err)
|
||||
continue
|
||||
|
||||
@@ -10,18 +10,19 @@ import (
|
||||
)
|
||||
|
||||
// NewEncoder returns a new Encoder.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{w}
|
||||
func NewEncoder(w io.Writer, quality int) *Encoder {
|
||||
return &Encoder{w, quality}
|
||||
}
|
||||
|
||||
// Encoder struct.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
w io.Writer
|
||||
quality int
|
||||
}
|
||||
|
||||
// Encode encodes image to JPEG.
|
||||
func (e Encoder) Encode(img image.Image) error {
|
||||
err := jpeg.Encode(e.w, img, &jpeg.Options{Quality: 75})
|
||||
err := jpeg.Encode(e.w, img, &jpeg.Options{Quality: e.quality})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -11,19 +11,20 @@ import (
|
||||
)
|
||||
|
||||
// NewEncoder returns a new Encoder.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{w}
|
||||
func NewEncoder(w io.Writer, quality int) *Encoder {
|
||||
return &Encoder{w, quality}
|
||||
}
|
||||
|
||||
// Encoder struct.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
w io.Writer
|
||||
quality int
|
||||
}
|
||||
|
||||
// Encode encodes image to JPEG.
|
||||
func (e Encoder) Encode(img image.Image) error {
|
||||
return jpegli.Encode(e.w, img, &jpegli.EncodingOptions{
|
||||
Quality: 75,
|
||||
Quality: e.quality,
|
||||
ProgressiveLevel: 0,
|
||||
ChromaSubsampling: image.YCbCrSubsampleRatio420,
|
||||
DCTMethod: jpegli.DCTIFast,
|
||||
|
||||
@@ -11,19 +11,20 @@ import (
|
||||
)
|
||||
|
||||
// NewEncoder returns a new Encoder.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{w}
|
||||
func NewEncoder(w io.Writer, quality int) *Encoder {
|
||||
return &Encoder{w, quality}
|
||||
}
|
||||
|
||||
// Encoder struct.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
w io.Writer
|
||||
quality int
|
||||
}
|
||||
|
||||
// Encode encodes image to JPEG.
|
||||
func (e Encoder) Encode(img image.Image) error {
|
||||
return jpeg.Encode(e.w, img, &jpeg.EncoderOptions{
|
||||
Quality: 75,
|
||||
Quality: e.quality,
|
||||
DCTMethod: jpeg.DCTIFast,
|
||||
ProgressiveMode: false,
|
||||
OptimizeCoding: false,
|
||||
|
||||
@@ -24,11 +24,18 @@ func Rotate(img image.Image, angle int) image.Image {
|
||||
return img
|
||||
}
|
||||
|
||||
func Timestamp(img image.Image, format string) (image.Image, error) {
|
||||
if format == "" {
|
||||
format = "2006-01-02 15:04:05"
|
||||
func Flip(img image.Image, dir string) image.Image {
|
||||
switch dir {
|
||||
case "horizontal":
|
||||
img = transform.FlipH(img)
|
||||
case "vertical":
|
||||
img = transform.FlipV(img)
|
||||
}
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
func Timestamp(img image.Image, format string) (image.Image, error) {
|
||||
dimg, ok := img.(draw.Image)
|
||||
if !ok {
|
||||
return img, fmt.Errorf("camera: %T is not a drawable image type", img)
|
||||
|
||||
@@ -16,19 +16,23 @@ type Server struct {
|
||||
Name string
|
||||
Version string
|
||||
|
||||
Bind string
|
||||
Htpasswd string
|
||||
|
||||
Index int
|
||||
Delay int
|
||||
|
||||
FrameWidth float64
|
||||
FrameHeight float64
|
||||
Width float64
|
||||
Height float64
|
||||
|
||||
Rotate int
|
||||
Quality int
|
||||
Rotate int
|
||||
Flip string
|
||||
|
||||
NoWebGL bool
|
||||
Timestamp bool
|
||||
NoWebGL bool
|
||||
|
||||
Timestamp bool
|
||||
TimeFormat string
|
||||
|
||||
Bind string
|
||||
Htpasswd string
|
||||
|
||||
Reader handlers.ImageReader
|
||||
}
|
||||
@@ -48,10 +52,10 @@ func (s *Server) ListenAndServe() error {
|
||||
basic = auth.NewBasicAuthenticator(realm, auth.HtpasswdFileProvider(s.Htpasswd))
|
||||
}
|
||||
|
||||
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", newAuthHandler(handlers.NewSocket(s.Reader, s.Delay), basic))
|
||||
http.Handle("/html", newAuthHandler(handlers.NewHTML(s.Width, s.Height, s.NoWebGL), basic))
|
||||
http.Handle("/jpeg", newAuthHandler(handlers.NewJPEG(s.Reader, s.Quality), basic))
|
||||
http.Handle("/mjpeg", newAuthHandler(handlers.NewMJPEG(s.Reader, s.Delay, s.Quality), basic))
|
||||
http.Handle("/socket", newAuthHandler(handlers.NewSocket(s.Reader, s.Delay, s.Quality), basic))
|
||||
|
||||
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
Reference in New Issue
Block a user