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