Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
359d3b5
Changes to allow framegrabbing.
aamcrae Mar 31, 2018
16a32b0
Change import
aamcrae Apr 1, 2018
a2cf408
Restore import path.
aamcrae Apr 4, 2018
89be1a0
Fixed formatting, removed check on buffer limit.
aamcrae Apr 5, 2018
da3d4aa
Track whether streaming or not.
aamcrae Apr 5, 2018
44bcc77
Move to new directory.
aamcrae Dec 23, 2018
cf9178b
Added http server.
aamcrae Dec 23, 2018
e64b7b4
Added set/get controls.
aamcrae Dec 24, 2018
8d3a808
Added set/get controls.
aamcrae Dec 24, 2018
4f2ad32
Added set/get controls.
aamcrae Dec 24, 2018
69a270b
Changed focus default
aamcrae Dec 31, 2018
00f9285
Initial SMA polling.
aamcrae Jan 1, 2019
00d8a1e
Merge branch 'master' of https://github.com/aamcrae/MeterMan
aamcrae Jan 1, 2019
4f4c6dd
Added systemd service file for imageserver
aamcrae Jan 1, 2019
4c85217
Add systemd service file for imageserver
aamcrae Jan 1, 2019
81bebf6
Added delay to start of imageserver/
aamcrae Jan 2, 2019
a6b0ab8
gofmt all files.
aamcrae Jan 6, 2019
138049c
Fixed get controls.
aamcrae Jan 6, 2019
f1acc79
Fix spelling mistake.
aamcrae Jan 12, 2019
7c57663
gofmt.
aamcrae Jan 12, 2019
025e1b0
gofmt on ioctl.
aamcrae Jan 12, 2019
c11a4ee
Changed import.
aamcrae Jan 12, 2019
1d69131
Changes to allow framegrabbing.
aamcrae Mar 31, 2018
2ce98f3
Change import
aamcrae Apr 1, 2018
e8542c3
Restore import path.
aamcrae Apr 4, 2018
bba6499
Fixed formatting, removed check on buffer limit.
aamcrae Apr 5, 2018
5b6eb99
Added set/get controls.
aamcrae Dec 24, 2018
efb38a7
Added set/get controls.
aamcrae Dec 24, 2018
e1776e2
Fixed get controls.
aamcrae Jan 6, 2019
01f6746
gofmt.
aamcrae Jan 12, 2019
21ec8f1
gofmt on ioctl.
aamcrae Jan 12, 2019
b1459fc
Changed import.
aamcrae Jan 12, 2019
ea7db1f
Fixed review comments.
aamcrae Jan 14, 2019
c4baa57
Fixed formatting.
aamcrae Jan 14, 2019
163d305
Replaced imports.
aamcrae Jan 14, 2019
065d561
Formatting.
aamcrae Jan 14, 2019
8482ae9
Used new webcam controls interface.
aamcrae Jan 14, 2019
cdc6efe
Create README.md
aamcrae Jan 14, 2019
ce6f496
Split frame.go
aamcrae Jan 15, 2019
85e7e91
Added comments.
aamcrae Jan 15, 2019
3702d0a
Added more info in getcontrols.
Jan 15, 2019
65ffcaa
Added mjpeg framer.
Jan 15, 2019
d781dcf
Added argument for device.
aamcrae Jan 15, 2019
8a6afa0
Add check for control query.
aamcrae Jan 15, 2019
9cf2f4e
Added check for missing Huffman tables.
aamcrae Jan 16, 2019
f9d7f1f
Formatted.
aamcrae Jan 16, 2019
4940379
Stop control query on any error.
aamcrae Jan 16, 2019
b91eaee
Formatted.
aamcrae Jan 16, 2019
87c2c28
Fixed huffman table processing.
aamcrae Jan 16, 2019
ecf5d70
Split cam to camera and frame.
aamcrae Jan 16, 2019
914d20a
Split cam to camera and frame.
aamcrae Jan 16, 2019
cc89b66
Fixed unused import.
aamcrae Jan 16, 2019
b9930e9
Removed unused errno reference. Fixed imports.
aamcrae Jan 16, 2019
0be3d72
Release call via defer.
aamcrae Jan 17, 2019
4c8af69
Renamed camera to snapper.
aamcrae Jan 17, 2019
c29f168
Renamed camera to snapper.
aamcrae Jan 17, 2019
670cca1
More comments.
aamcrae Jan 17, 2019
c7769d5
More comments.
aamcrae Jan 17, 2019
ae57abd
More comments.
aamcrae Jan 17, 2019
d106d87
Use PixelFormat for framers. Add jpeg framer.
aamcrae Jan 18, 2019
c9570ce
Use PixelFormat for framers. Add jpeg framer.
aamcrae Jan 18, 2019
decd959
Use PixelFormat for framers. Add jpeg framer.
aamcrae Jan 18, 2019
f0612f5
Refactored interface to framers.
aamcrae Jan 18, 2019
0bebb11
Refactored interface to framers.
aamcrae Jan 18, 2019
34afa54
Refactored interface to framers.
aamcrae Jan 18, 2019
7055439
Formatted.
aamcrae Jan 18, 2019
0149c94
Formatted.
aamcrae Jan 18, 2019
cf06d1b
Added RGB3 handler.
aamcrae Jan 18, 2019
bc16b10
Added RGB3 handler.
aamcrae Jan 18, 2019
2f90363
Make RGB processing common.
aamcrae Jan 18, 2019
5b40548
Make RGB processing common.
aamcrae Jan 18, 2019
26ab9f8
Make RGB processing common.
aamcrae Jan 18, 2019
50dae96
Create LICENSE
aamcrae Jan 28, 2019
8102d3b
Merge pull request #1 from aamcrae/add-license-1
aamcrae Jan 28, 2019
604c5a4
Add CONTRIBUTING file.
Jan 28, 2019
dcf3e23
Default controls to none.
aamcrae Mar 28, 2019
0f88c36
Merge branch 'controls'
aamcrae Mar 28, 2019
17aaef8
Merge remote-tracking branch 'upstream/master'
aamcrae Mar 28, 2019
543a685
Add finalizer to frames.
aamcrae Mar 28, 2019
eda67e9
Add finalizer to frames.
aamcrae Mar 28, 2019
4af642d
Flag option for image name.
Mar 28, 2019
f434b18
Merge branch 'master' of https://github.com/aamcrae/imageserver
Mar 28, 2019
ebc9ba8
Allow png and gif.
Mar 28, 2019
ec22b81
Handle multiple image types.
aamcrae Mar 28, 2019
b823a5c
Add some comments.
Mar 29, 2019
4e3a273
Add some comments.
Mar 29, 2019
c37b295
Use stride for line width.
Mar 29, 2019
e665af2
Use stride for line width.
Mar 29, 2019
6a86f0a
Use stride for line width.
Mar 29, 2019
f5f2515
Retrieve stride.
Mar 29, 2019
a087daa
Use retrieved stride and image size.
aamcrae Mar 29, 2019
cc1eb09
Use retrieved stride and image size.
aamcrae Mar 29, 2019
b60fbb3
Use retrieved stride and image size.
aamcrae Mar 29, 2019
76120cd
Use stride and image size.
aamcrae Mar 29, 2019
8abf46e
Verbose display of control setting.
aamcrae Mar 29, 2019
e46bfad
Added copyright header and ran go fmt.
Mar 29, 2019
91980ea
Added copyright header and ran go fmt.
Mar 29, 2019
d74bdc0
Added copyright header and ran go fmt.
Mar 29, 2019
8432ceb
Merge branch 'master' of https://github.com/aamcrae/imageserver
Mar 29, 2019
4ac3757
Move of frame code to webcam.
Mar 29, 2019
ae96374
Merge branch 'master' of ../imageserver
Mar 29, 2019
b4feb89
Move snapshot to webcam repo.
Mar 29, 2019
1bcd47b
Merge branch 'master' of ../imageserver
Mar 29, 2019
9af416a
Fix module path.
Mar 29, 2019
7c24922
Moved frame and snapshot to webcam repo.
Mar 29, 2019
4b4bab3
Moved snapshot to frame.
Mar 29, 2019
d9eccda
New reference for snapshot.
Mar 29, 2019
9a93557
Better error handling.
Mar 29, 2019
519d493
Fix error handling.
Mar 29, 2019
044868d
Add more controls, and allow control list.
aamcrae Mar 29, 2019
2769f32
No need to keep size in frame structure.
Apr 3, 2019
8682788
Add close if an error occurs.
Apr 3, 2019
f63d95c
Fix comparison.
Apr 5, 2019
aca197a
Close input channel on error.
aamcrae Apr 5, 2019
75f326f
Fix service name
Apr 5, 2019
76678a0
go fmt
aamcrae Apr 5, 2019
d79646c
Merge branch 'master' of https://github.com/aamcrae/imageserver
aamcrae Apr 5, 2019
550be15
Fix comments.
Apr 5, 2019
6bddef7
Move files.
Apr 5, 2019
db5e36a
Rm .gitignore
Apr 5, 2019
1258a6b
Merge branch 'master' of ../../x/imageserver
Apr 5, 2019
3f48f25
Add imageserver to ,gitignore
Apr 5, 2019
d05d7d2
Add imageserver to .gitignore
aamcrae Dec 2, 2019
098329c
Merge branch 'master' of https://github.com/aamcrae/webcam
aamcrae Dec 2, 2019
a8fde7f
Move snapshot to separate directory.
Feb 4, 2020
9d1c9d7
server.go: Add delay after streaming starts.
aamcrae Jul 7, 2020
ea077de
Merge branch 'master' of https://github.com/aamcrae/webcam
aamcrae Jul 7, 2020
29f5691
getcontrols: Sort controls by name
Jul 28, 2020
109e8c2
getcontrols: Fix panic arguments.
Jul 28, 2020
0ad1b6c
getcontrols: use byName
Jul 28, 2020
eb22337
webcam: go vet and go fmt
Jul 28, 2020
da587d1
getcontrols: Simplify sort.
Jul 29, 2020
4bfe5bc
imageserver: Add arguments to service
Oct 24, 2020
b40db1d
Change import path ready for pull request
Nov 25, 2020
84935a1
Update file headers for existing licence
Nov 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
examples/getcontrols/getcontrols
examples/http_mjpeg_streamer/http_mjpeg_streamer
examples/stdout_streamer/stdout_streamer
examples/imageserver/imageserver
26 changes: 23 additions & 3 deletions examples/getcontrols/getcontrols.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ package main
import (
"flag"
"fmt"
"sort"

"github.com/blackjack/webcam"
)

var device = flag.String("input", "/dev/video0", "Input video device")

type control struct {
id webcam.ControlID
name string
min, max int32
}

func main() {
flag.Parse()
cam, err := webcam.Open(*device)
if err != nil {
panic(err.Error())
panic(fmt.Errorf("%s: %v", *device, err.Error()))
}
defer cam.Close()

Expand All @@ -34,7 +41,20 @@ func main() {

cmap := cam.GetControls()
fmt.Println("Available controls: ")
for id, c := range cmap {
fmt.Printf("ID:%08x %-32s Min: %4d Max: %5d\n", id, c.Name, c.Min, c.Max)
var clist []control
for id, cm := range cmap {
var c control
c.id = id
c.name = cm.Name
c.min = cm.Min
c.max = cm.Max
clist = append(clist, c)
}
sort.Slice(clist, func(i, j int) bool {
return clist[i].name < clist[j].name
})
for _, cl := range clist {
fmt.Printf("ID:%08x %-32s Min: %4d Max: %5d\n", cl.id,
cl.name, cl.min, cl.max)
}
}
2 changes: 1 addition & 1 deletion examples/http_mjpeg_streamer/webcam.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ FMT:
}

fmt.Fprintln(os.Stderr, "Requesting", format_desc[format], size.GetString())
f, w, h, err := cam.SetImageFormat(format, uint32(size.MaxWidth), uint32(size.MaxHeight))
f, w, h, _, _, err := cam.SetImageFormat(format, uint32(size.MaxWidth), uint32(size.MaxHeight))
if err != nil {
log.Println("SetImageFormat return error", err)
return
Expand Down
2 changes: 2 additions & 0 deletions examples/imageserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# imageserver
A simple web server to capture still images from a webcam.
16 changes: 16 additions & 0 deletions examples/imageserver/imageserver.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[Unit]
Description=webcam image server
After=network.target

[Service]
User=root
Type=simple
TimeoutStopSec=10
ExecStart=/usr/local/bin/imageserver --delay=2 --controls=focus=175,power_line_frequency=1

Restart=on-failure
RestartSec=15s
SuccessExitStatus=SIGKILL

[Install]
WantedBy=default.target
148 changes: 148 additions & 0 deletions examples/imageserver/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Program that serves images taken from a webcam.
package main

import (
"flag"
"fmt"
"image/gif"
"image/jpeg"
"image/png"
"log"
"net/http"
"strconv"
"strings"
"time"

"github.com/blackjack/webcam"
"github.com/blackjack/webcam/frame"
"github.com/blackjack/webcam/snapshot"
)

var port = flag.Int("port", 8080, "Web server port number")
var path = flag.String("path", "image", "Image base filename")
var device = flag.String("input", "/dev/video0", "Input video device")
var resolution = flag.String("resolution", "800x600", "Camera resolution")
var format = flag.String("format", "YUYV", "Pixel format of camera")
var controls = flag.String("controls", "",
"Control parameters for camera (use --controls=list to list controls)")
var startDelay = flag.Int("delay", 2, "Delay at start (seconds)")
var verbose = flag.Bool("v", false, "Log more information")

var cnames map[string]webcam.ControlID = map[string]webcam.ControlID{
"focus": 0x009a090a,
"power_line_frequency": 0x00980918,
"brightness": 0x00980900,
"contrast": 0x00980901,
"autoiso": 0x009a0918,
"autoexp": 0x009a0901,
"saturation": 0x00980902,
"sharpness": 0x0098091b,
"rotate": 0x00980922,
"stabilization": 0x009a0916,
}

func main() {
flag.Parse()
s := strings.Split(*resolution, "x")
if len(s) != 2 {
log.Fatalf("%s: Illegal resolution", *resolution)
}
x, err := strconv.Atoi(s[0])
if err != nil {
log.Fatalf("%s: illegal width: %v", s[0], err)
}
y, err := strconv.Atoi(s[1])
if err != nil {
log.Fatalf("%s: illegal height: %v", s[1], err)
}
if *controls == "list" {
fmt.Printf("Control list (not all cameras may support all options):\n")
for c, _ := range cnames {
fmt.Printf(" %s\n", c)
}
return
}
if *startDelay != 0 {
time.Sleep(time.Duration(*startDelay) * time.Second)
}
cm := snapshot.NewSnapper()
if err := cm.Open(*device, frame.FourCC(*format), x, y); err != nil {
log.Fatalf("%s: %v", *device, err)
}
if *startDelay != 0 {
time.Sleep(time.Duration(*startDelay) * time.Second)
}
defer cm.Close()
// Set camera controls.
if len(*controls) != 0 {
for _, control := range strings.Split(*controls, ",") {
s := strings.Split(control, "=")
if len(s) != 2 {
log.Fatalf("Bad control option: %s", control)
}
id, ok := cnames[s[0]]
if !ok {
log.Fatalf("%s: Unknown control", s[0])
}
val, err := strconv.Atoi(s[1])
if err != nil {
log.Fatalf("Bad control value: %s (%v)", control, err)
}
if *verbose {
fmt.Printf("Setting control '%s' to %d\n", s[0], val)
}
if err = cm.SetControl(id, int32(val)); err != nil {
log.Fatalf("SetControl error: %s (%v)", control, err)
}
}
}
encodeJpeg := func(w http.ResponseWriter, f frame.Frame) error {
w.Header().Set("Content-Type", "image/jpeg")
return jpeg.Encode(w, f, nil)
}
encodePNG := func(w http.ResponseWriter, f frame.Frame) error {
w.Header().Set("Content-Type", "image/png")
return png.Encode(w, f)
}
encodeGIF := func(w http.ResponseWriter, f frame.Frame) error {
w.Header().Set("Content-Type", "image/gif")
return gif.Encode(w, f, nil)
}
http.Handle(fmt.Sprintf("/%s.jpg", *path), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
publishImage(cm, w, r, encodeJpeg)
}))
http.Handle(fmt.Sprintf("/%s.jpeg", *path), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
publishImage(cm, w, r, encodeJpeg)
}))
http.Handle(fmt.Sprintf("/%s.png", *path), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
publishImage(cm, w, r, encodePNG)
}))
http.Handle(fmt.Sprintf("/%s.gif", *path), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
publishImage(cm, w, r, encodeGIF)
}))
url := fmt.Sprintf(":%d", *port)
if *verbose {
log.Printf("Starting server on %s", url)
}
server := &http.Server{Addr: url}
log.Fatal(server.ListenAndServe())
}

func publishImage(cm *snapshot.Snapper, w http.ResponseWriter, r *http.Request, encode func(http.ResponseWriter, frame.Frame) error) {
if *verbose {
log.Printf("URL request: %v", r.URL)
}
f, err := cm.Snap()
if err != nil {
log.Printf("Getframe: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
defer f.Release()
if err := encode(w, f); err != nil {
log.Printf("Error writing image: %v\n", err)
w.WriteHeader(http.StatusInternalServerError)
} else if *verbose {
log.Printf("Wrote image successfully\n")
}
}
2 changes: 1 addition & 1 deletion examples/stdout_streamer/stdout_streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func main() {
choice = readChoice(fmt.Sprintf("Choose format [1-%d]: ", len(frames)))
size := frames[choice-1]

f, w, h, err := cam.SetImageFormat(format, uint32(size.MaxWidth), uint32(size.MaxHeight))
f, w, h, _, _, err := cam.SetImageFormat(format, uint32(size.MaxWidth), uint32(size.MaxHeight))

if err != nil {
panic(err.Error())
Expand Down
53 changes: 53 additions & 0 deletions frame/frame.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// package frame wraps raw webcam frames as an image.
package frame

import (
"fmt"
"image"

"github.com/blackjack/webcam"
)

type FourCC string

// Release is called when the frame is no longer in use.
// The implementation may set a finalizer on the frame as a precaution
// in case Release is not called (which would cause a kernel resource leak).
type Frame interface {
image.Image
Release()
}

var framerFactoryMap = map[FourCC]func(int, int, int, int) func([]byte, func()) (Frame, error){}

// RegisterFramer registers a framer factory for a format.
// Note that only one handler can be registered for any single format.
func RegisterFramer(format FourCC, factory func(int, int, int, int) func([]byte, func()) (Frame, error)) {
framerFactoryMap[format] = factory
}

// GetFramer returns a function that wraps the frame for this format.
func GetFramer(format FourCC, w, h, stride, size int) (func([]byte, func()) (Frame, error), error) {
if factory, ok := framerFactoryMap[format]; ok {
return factory(w, h, stride, size), nil
}
return nil, fmt.Errorf("No handler for format '%s'", format)
}

// PixelFormatToFourCC converts the v4l2 PixelFormat to a FourCC.
func PixelFormatToFourCC(pf webcam.PixelFormat) FourCC {
b := make([]byte, 4)
b[0] = byte(pf)
b[1] = byte(pf >> 8)
b[2] = byte(pf >> 16)
b[3] = byte(pf >> 24)
return FourCC(b)
}

// FourCCToPixelFormat converts the four character string to a v4l2 PixelFormat.
func FourCCToPixelFormat(f FourCC) (webcam.PixelFormat, error) {
if len(f) != 4 {
return 0, fmt.Errorf("%s: Illegal FourCC", f)
}
return webcam.PixelFormat(uint32(f[0]) | uint32(f[1])<<8 | uint32(f[2])<<16 | uint32(f[3])<<24), nil
}
61 changes: 61 additions & 0 deletions frame/jpeg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package frame

import (
"bytes"
"image"
"image/color"
"image/jpeg"
"runtime"
)

type fJPEG struct {
img image.Image
release func()
}

// Register this framer for this format.
func init() {
RegisterFramer("JPEG", newJPEGFramer)
}

// Return a framer for JPEG.
func newJPEGFramer(w, h, stride, size int) func([]byte, func()) (Frame, error) {
return jpegFramer
}

// Wrap a jpeg block in a Frame so that it can be used as an image.
func jpegFramer(f []byte, rel func()) (Frame, error) {
img, err := jpeg.Decode(bytes.NewBuffer(f))
if err != nil {
if rel != nil {
rel()
}
return nil, err
}
fr := &fJPEG{img: img, release: rel}
runtime.SetFinalizer(fr, func(obj Frame) {
obj.Release()
})
return fr, nil
}

func (f *fJPEG) ColorModel() color.Model {
return f.img.ColorModel()
}

func (f *fJPEG) Bounds() image.Rectangle {
return f.img.Bounds()
}

func (f *fJPEG) At(x, y int) color.Color {
return f.img.At(x, y)
}

// Done with frame, release back to camera (if required).
func (f *fJPEG) Release() {
if f.release != nil {
f.release()
// Make sure it only gets called once.
f.release = nil
}
}
Loading