├── .travis.yml ├── LICENSE ├── README.md ├── raspicam.go ├── still.go ├── still_test.go └── vid.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - tip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013, David Howden 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # raspicam Go API 2 | [![Build Status](https://travis-ci.org/dhowden/raspicam.svg?branch=master)](https://travis-ci.org/dhowden/raspicam) 3 | [![GoDoc](https://godoc.org/github.com/dhowden/raspicam?status.svg)](https://godoc.org/github.com/dhowden/raspicam) 4 | 5 | Provides a simple Go interface to the [Raspberry Pi Camera board](http://www.raspberrypi.org/archives/tag/camera-board). 6 | 7 | For the moment we call the existing command line tools, though eventually hope to hook directly into the C API. 8 | 9 | We aim to create an idiomatic Go API whilst mirroring the functionality of the existing command line tools to allow for the best interoperability. 10 | 11 | ## Status 12 | 13 | Implemented: 14 | 15 | - Interface to raspistill 16 | - Interface to raspiyuv 17 | - Interface to raspivid 18 | 19 | Todo: 20 | 21 | - More tests! 22 | 23 | ## Installation 24 | 25 | go get github.com/dhowden/raspicam 26 | 27 | ## Getting Started 28 | 29 | ### Write to a file 30 | 31 | package main 32 | 33 | import ( 34 | "fmt" 35 | "log" 36 | "os" 37 | 38 | "github.com/dhowden/raspicam" 39 | ) 40 | 41 | func main() { 42 | f, err := os.Create(os.Args[1]) 43 | if err != nil { 44 | fmt.Fprintf(os.Stderr, "create file: %v", err) 45 | return 46 | } 47 | defer f.Close() 48 | 49 | s := raspicam.NewStill() 50 | errCh := make(chan error) 51 | go func() { 52 | for x := range errCh { 53 | fmt.Fprintf(os.Stderr, "%v\n", x) 54 | } 55 | }() 56 | log.Println("Capturing image...") 57 | raspicam.Capture(s, f, errCh) 58 | } 59 | 60 | 61 | ### Simple Raspicam TCP Server 62 | 63 | package main 64 | 65 | import ( 66 | "log" 67 | "fmt" 68 | "net" 69 | "os" 70 | 71 | "github.com/dhowden/raspicam" 72 | ) 73 | 74 | func main() { 75 | listener, err := net.Listen("tcp", "0.0.0.0:6666") 76 | if err != nil { 77 | fmt.Fprintf(os.Stderr, "listen: %v", err) 78 | return 79 | } 80 | log.Println("Listening on 0.0.0.0:6666") 81 | 82 | for { 83 | conn, err := listener.Accept() 84 | if err != nil { 85 | fmt.Fprintf(os.Stderr, "accept: %v", err) 86 | return 87 | } 88 | log.Printf("Accepted connection from: %v\n", conn.RemoteAddr()) 89 | go func() { 90 | s := raspicam.NewStill() 91 | errCh := make(chan error) 92 | go func() { 93 | for x := range errCh { 94 | fmt.Fprintf(os.Stderr, "%v\n", x) 95 | } 96 | }() 97 | log.Println("Capturing image...") 98 | raspicam.Capture(s, conn, errCh) 99 | log.Println("Done") 100 | conn.Close() 101 | }() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /raspicam.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, David Howden 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package raspicam provides basic Go APIs for interacting with the Raspberry Pi 6 | // camera. 7 | // 8 | // All captures are prepared by first creating a CaptureCommand (Still, StillYUV or 9 | // Vid structs via calls to the NewStill, NewStillYUV or NewVid functions respectively). 10 | // The Capture function can then be used to perform the capture. 11 | package raspicam 12 | 13 | import ( 14 | "bufio" 15 | "fmt" 16 | "io" 17 | "os/exec" 18 | "strconv" 19 | "strings" 20 | "time" 21 | ) 22 | 23 | // ExposureMode is an enumeration of supported exposure modes. 24 | type ExposureMode uint 25 | 26 | const ( 27 | ExposureOff ExposureMode = iota 28 | ExposureAuto 29 | ExposureNight 30 | ExposureNightPreview 31 | ExposureBacklight 32 | ExposureSpotlight 33 | ExposureSports 34 | ExposureSnow 35 | ExposureBeach 36 | ExposureVerylong 37 | ExposureFixedFPS 38 | ExposureAntishake 39 | ExposureFireworks 40 | ) 41 | 42 | var exposureModes = [...]string{ 43 | "off", 44 | "auto", 45 | "night", 46 | "nightpreview", 47 | "backlight", 48 | "spotlight", 49 | "sports", 50 | "snow", 51 | "beach", 52 | "verylong", 53 | "fixedfps", 54 | "antishake", 55 | "fireworks", 56 | } 57 | 58 | // String returns the command line parameter for the given ExposureMode. 59 | func (e ExposureMode) String() string { return exposureModes[e] } 60 | 61 | // A MeteringMode specificies an exposure metering mode. 62 | type MeteringMode uint 63 | 64 | const ( 65 | MeteringAverage MeteringMode = iota 66 | MeteringSpot 67 | MeteringBacklit 68 | MeteringMatrix 69 | ) 70 | 71 | var exposureMeteringMode = [...]string{ 72 | "average", 73 | "spot", 74 | "backlit", 75 | "matrix", 76 | } 77 | 78 | // String returns the command line parameter for the given MeteringMode. 79 | func (m MeteringMode) String() string { return exposureMeteringMode[m] } 80 | 81 | // An AWBMode is an enumeration of the auto white balance modes. 82 | type AWBMode uint 83 | 84 | const ( 85 | AWBOff AWBMode = iota 86 | AWBAuto 87 | AWBSunlight 88 | AWBCloudy 89 | AWBShade 90 | AWBTungsten 91 | AWBFluorescent 92 | AWBIncandescent 93 | AWBFlash 94 | AWBHorizon 95 | ) 96 | 97 | var awbModes = [...]string{ 98 | "off", 99 | "auto", 100 | "sun", 101 | "cloud", 102 | "shade", 103 | "tungsten", 104 | "fluorescent", 105 | "incandescent", 106 | "flash", 107 | "horizon", 108 | } 109 | 110 | // String returns the command line parameter for the given AWBMode. 111 | func (a AWBMode) String() string { return awbModes[a] } 112 | 113 | // An ImageFX specifies an image effect for the camera. 114 | type ImageFX uint 115 | 116 | const ( 117 | FXNone ImageFX = iota 118 | FXNegative 119 | FXSolarize 120 | FXPosterize 121 | FXWhiteboard 122 | FXBlackboard 123 | FXSketch 124 | FXDenoise 125 | FXEmboss 126 | FXOilpaint 127 | FXHatch 128 | FXGpen 129 | FXPastel 130 | FXWatercolour 131 | FXFilm 132 | FXBlur 133 | FXSaturation 134 | FXColourSwap 135 | FXWashedOut 136 | FXPosterise 137 | FXColourPoint 138 | FXColourBalance 139 | FXCartoon 140 | ) 141 | 142 | var imageFXModes = [...]string{ 143 | "none", 144 | "negative", 145 | "solarise", 146 | "sketch", 147 | "denoise", 148 | "emboss", 149 | "oilpaint", 150 | "hatch", 151 | "gpen", 152 | "pastel", 153 | "watercolour", 154 | "film", 155 | "blur", 156 | "saturation", 157 | "colourswap", 158 | "washedout", 159 | "posterise", 160 | "colourpoint", 161 | "colourbalance", 162 | "cartoon", 163 | } 164 | 165 | // String returns the command-line parameter for the given imageFX. 166 | func (i ImageFX) String() string { return imageFXModes[i] } 167 | 168 | // ColourFX represents colour effects parameters. 169 | type ColourFX struct { 170 | Enabled bool 171 | U, V int 172 | } 173 | 174 | // String returns the command parameter for the given ColourFX. 175 | func (c ColourFX) String() string { 176 | return fmt.Sprintf("%v:%v", c.U, c.V) 177 | } 178 | 179 | // FloatRect contains the information necessary to construct a rectangle 180 | // with dimensions in floating point. 181 | type FloatRect struct { 182 | X, Y, W, H float64 183 | } 184 | 185 | // String returns the command parameter for the given FloatRect. 186 | func (r *FloatRect) String() string { 187 | return fmt.Sprintf("%v, %v, %v, %v", r.X, r.Y, r.W, r.H) 188 | } 189 | 190 | // The default RegionOfInterest setup. 191 | var defaultRegionOfInterest = FloatRect{W: 1.0, H: 1.0} 192 | 193 | // Camera represents a camera configuration. 194 | type Camera struct { 195 | Sharpness int // -100 to 100 196 | Contrast int // -100 to 100 197 | Brightness int // 0 to 100 198 | Saturation int // -100 to 100 199 | ISO int // TODO: what range? (see RaspiCamControl.h) 200 | VideoStabilisation bool 201 | ExposureCompensation int // -10 to 10? (see RaspiCamControl.h) 202 | ExposureMode ExposureMode 203 | MeteringMode MeteringMode 204 | AWBMode AWBMode 205 | ImageEffect ImageFX 206 | ColourEffects ColourFX 207 | Rotation int // 0 to 359 208 | HFlip, VFlip bool 209 | RegionOfInterest FloatRect // Assumes Normalised to [0.0,1.0] 210 | ShutterSpeed time.Duration 211 | } 212 | 213 | // The default Camera setup. 214 | var defaultCamera = Camera{ 215 | Brightness: 50, 216 | ISO: 400, 217 | ExposureMode: ExposureAuto, 218 | MeteringMode: MeteringAverage, 219 | AWBMode: AWBAuto, 220 | ImageEffect: FXNone, 221 | ColourEffects: ColourFX{U: 128, V: 128}, 222 | RegionOfInterest: defaultRegionOfInterest, 223 | } 224 | 225 | // String returns the parameters necessary to construct the 226 | // equivalent command line arguments for the raspicam tools. 227 | func (c *Camera) String() string { 228 | return paramString(c) 229 | } 230 | 231 | // params is a wrapper around a string slice which adds convenience 232 | // methods for adding different types of parameters 233 | type params []string 234 | 235 | func (ps *params) add(xs ...string) { *ps = append(*ps, xs...) } 236 | func (ps *params) addInt(x string, n int) { *ps = append(*ps, x, strconv.Itoa(n)) } 237 | func (ps *params) addInt64(x string, n int64) { *ps = append(*ps, x, strconv.FormatInt(n, 10)) } 238 | 239 | func paramString(x interface{ params() []string }) string { 240 | return strings.Join(x.params(), " ") 241 | } 242 | 243 | func (c *Camera) params() []string { 244 | var out params 245 | if c.Sharpness != defaultCamera.Sharpness { 246 | out.addInt("--sharpness", c.Sharpness) 247 | } 248 | if c.Contrast != defaultCamera.Contrast { 249 | out.addInt("--contrast", c.Contrast) 250 | } 251 | if c.Brightness != defaultCamera.Brightness { 252 | out.addInt("--brightness", c.Brightness) 253 | } 254 | if c.Saturation != defaultCamera.Saturation { 255 | out.addInt("--saturation", c.Saturation) 256 | } 257 | if c.ISO != defaultCamera.ISO { 258 | out.addInt("--ISO", c.ISO) 259 | } 260 | if c.VideoStabilisation { 261 | out.add("--vstab") 262 | } 263 | if c.ExposureCompensation != defaultCamera.ExposureCompensation { 264 | out.addInt("--ev", c.ExposureCompensation) 265 | } 266 | if c.ExposureMode != defaultCamera.ExposureMode { 267 | out.add("--exposure", c.ExposureMode.String()) 268 | } 269 | if c.MeteringMode != defaultCamera.MeteringMode { 270 | out.add("--metering", c.MeteringMode.String()) 271 | } 272 | if c.AWBMode != defaultCamera.AWBMode { 273 | out.add("--awb", c.AWBMode.String()) 274 | } 275 | if c.ImageEffect != defaultCamera.ImageEffect { 276 | out.add("--imxfx", c.ImageEffect.String()) 277 | } 278 | if c.ColourEffects.Enabled { 279 | out.add("--colfx", c.ColourEffects.String()) 280 | } 281 | if c.MeteringMode != defaultCamera.MeteringMode { 282 | out.add("--metering", c.MeteringMode.String()) 283 | } 284 | if c.Rotation != defaultCamera.Rotation { 285 | out.addInt("--rotation", c.Rotation) 286 | } 287 | if c.HFlip { 288 | out.add("--hflip") 289 | } 290 | if c.VFlip { 291 | out.add("--vflip") 292 | } 293 | if c.RegionOfInterest != defaultCamera.RegionOfInterest { 294 | out.add("--roi", c.RegionOfInterest.String()) 295 | } 296 | if c.ShutterSpeed != defaultCamera.ShutterSpeed { 297 | out.addInt64("--shutter", int64(c.ShutterSpeed/time.Microsecond)) 298 | } 299 | return out 300 | } 301 | 302 | // Rect represents a rectangle defined by integer parameters. 303 | type Rect struct { 304 | X, Y, Width, Height uint32 305 | } 306 | 307 | // String returns the parameter string for the given Rect. 308 | func (r *Rect) String() string { 309 | return fmt.Sprintf("%v, %v, %v, %v", r.X, r.Y, r.Width, r.Height) 310 | } 311 | 312 | // PreviewMode represents an enumeration of preview modes. 313 | type PreviewMode uint 314 | 315 | const ( 316 | PreviewFullscreen PreviewMode = iota // Enabled by default 317 | PreviewWindow 318 | PreviewDisabled 319 | ) 320 | 321 | var previewModes = [...]string{ 322 | "fullscreen", 323 | "preview", 324 | "nopreview", 325 | } 326 | 327 | // String returns the parameter string for the given PreviewMode. 328 | func (p PreviewMode) String() string { return previewModes[p] } 329 | 330 | // Preview contains the settings for the camera previews. 331 | type Preview struct { 332 | Mode PreviewMode 333 | Opacity int // Opacity of window (0 = transparent, 255 = opaque) 334 | Rect Rect // Used when Mode is PreviewWindow 335 | } 336 | 337 | // The default Preview setup. 338 | var defaultPreview = Preview{ 339 | Mode: PreviewFullscreen, 340 | Opacity: 255, 341 | Rect: Rect{X: 0, Y: 0, Width: 1024, Height: 768}, 342 | } 343 | 344 | // String returns the parameter string for the given Preview. 345 | func (p *Preview) String() string { 346 | return paramString(p) 347 | } 348 | 349 | func (p *Preview) params() []string { 350 | var out params 351 | if p.Mode == PreviewWindow { 352 | out.add("--"+p.Mode.String(), p.Rect.String()) 353 | } else { 354 | if p.Mode != defaultPreview.Mode { 355 | out.add("--" + p.Mode.String()) 356 | } 357 | } 358 | if p.Opacity != defaultPreview.Opacity { 359 | out.addInt("--opacity", p.Opacity) 360 | } 361 | return out 362 | } 363 | 364 | // CaptureCommand represents a prepared capture command. 365 | type CaptureCommand interface { 366 | Cmd() string 367 | Params() []string 368 | } 369 | 370 | // Capture runs the given CaptureCommand and writes the result to the given 371 | // writer. Any errors are sent back on the given error channel, which is closed 372 | // before the function returns. 373 | func Capture(c CaptureCommand, w io.Writer, errCh chan<- error) { 374 | done := make(chan struct{}) 375 | defer func() { 376 | <-done 377 | close(errCh) 378 | }() 379 | 380 | cmd := exec.Command(c.Cmd(), c.Params()...) 381 | 382 | stdout, err := cmd.StdoutPipe() 383 | if err != nil { 384 | errCh <- err 385 | return 386 | } 387 | 388 | stderr, err := cmd.StderrPipe() 389 | if err != nil { 390 | errCh <- err 391 | return 392 | } 393 | 394 | go func() { 395 | errScanner := bufio.NewScanner(stderr) 396 | for errScanner.Scan() { 397 | errCh <- fmt.Errorf("%v: %v", c.Cmd(), errScanner.Text()) 398 | } 399 | if err := errScanner.Err(); err != nil { 400 | errCh <- err 401 | } 402 | close(done) 403 | }() 404 | 405 | if err := cmd.Start(); err != nil { 406 | errCh <- fmt.Errorf("starting: %v", err) 407 | return 408 | } 409 | defer func() { 410 | if err := cmd.Wait(); err != nil { 411 | errCh <- fmt.Errorf("waiting: %v", err) 412 | } 413 | }() 414 | 415 | _, err = io.Copy(w, stdout) 416 | if err != nil { 417 | errCh <- err 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /still.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, David Howden 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package raspicam 6 | 7 | import ( 8 | "time" 9 | ) 10 | 11 | // DefaultRaspiStillCommand is the default command for capturing stills. 12 | var DefaultRaspiStillCommand = "raspistill" 13 | 14 | // DefaultRaspiStillYUVCommand is the default command for capturing YUV stills. 15 | var DefaultRaspiStillYUVCommand = "raspiyuv" 16 | 17 | // Encoding represents an enumeration of the supported encoding types. 18 | type Encoding uint 19 | 20 | const ( 21 | EncodingJPEG Encoding = iota 22 | EncodingBMP 23 | EncodingGIF 24 | EncodingPNG 25 | ) 26 | 27 | var encodings = [...]string{ 28 | "jpg", 29 | "bmp", 30 | "gif", 31 | "png", 32 | } 33 | 34 | // String returns the parameter string for the given encoding. 35 | func (s Encoding) String() string { 36 | return encodings[s] 37 | } 38 | 39 | // BaseStill represents the common elements between a Still and StillYUV 40 | // as described in their equivalents found in RaspiStill.c and RaspiStillYUV.c 41 | // respectively. 42 | type BaseStill struct { 43 | CamSelect int 44 | Timeout time.Duration // Delay before image is taken 45 | Width, Height int // Image dimensions 46 | Timelapse int // The number of pictures to take in the given timeout interval 47 | Camera Camera 48 | Preview Preview 49 | 50 | // The command to run when making a still capture. If left blank, the default 51 | // command is used. 52 | Command string 53 | 54 | // Additional arguments. Default is empty. 55 | Args []string 56 | } 57 | 58 | // The default BaseStill setup. 59 | var defaultBaseStill = BaseStill{ 60 | Timeout: 5 * time.Second, 61 | Width: 2592, 62 | Height: 1944, 63 | Camera: defaultCamera, 64 | Preview: defaultPreview, 65 | } 66 | 67 | // String returns the parameter string for the given BaseStill. 68 | func (s *BaseStill) String() string { 69 | return paramString(s) 70 | } 71 | 72 | func (s *BaseStill) params() []string { 73 | var out params 74 | out.add("--output", "-") 75 | if s.CamSelect != defaultStill.CamSelect { 76 | out.addInt("--camselect", s.CamSelect) 77 | } 78 | if s.Timeout != defaultStill.Timeout { 79 | out.addInt64("--timeout", int64(s.Timeout/time.Millisecond)) 80 | } 81 | if s.Width != defaultStill.Width { 82 | out.addInt("--width", s.Width) 83 | } 84 | if s.Height != defaultStill.Height { 85 | out.addInt("--height", s.Height) 86 | } 87 | out.add(s.Camera.params()...) 88 | out.add(s.Preview.params()...) 89 | return out 90 | } 91 | 92 | // The default Still setup. 93 | var defaultStill = Still{ 94 | BaseStill: defaultBaseStill, 95 | Quality: 85, 96 | Encoding: EncodingJPEG, 97 | } 98 | 99 | // Still represents the configuration necessary to call raspistill. 100 | type Still struct { 101 | BaseStill 102 | Quality int // Quality (for lossy encoding) 103 | Raw bool // Want a raw image? 104 | Encoding Encoding 105 | } 106 | 107 | // String returns the parameter string for the given Still struct. 108 | func (s *Still) String() string { 109 | return paramString(s) 110 | } 111 | 112 | func (s *Still) params() []string { 113 | out := params(s.BaseStill.params()) 114 | if s.Quality != defaultStill.Quality { 115 | out.addInt("--quality", s.Quality) 116 | } 117 | if s.Raw { 118 | out.add("--raw") 119 | } 120 | if s.Encoding != defaultStill.Encoding { 121 | out.add("--encoding", s.Encoding.String()) 122 | } 123 | return out 124 | } 125 | 126 | // Cmd returns the raspicam command for a Still. 127 | func (s *Still) Cmd() string { 128 | if s.BaseStill.Command != "" { 129 | return s.BaseStill.Command 130 | } 131 | return DefaultRaspiStillCommand 132 | } 133 | 134 | // Params returns the parameters to be used in the command execution. 135 | func (s *Still) Params() []string { 136 | return append(s.params(), s.BaseStill.Args...) 137 | } 138 | 139 | // NewStill returns a *Still with the default values set by the raspistill command 140 | // (see userland/linux/apps/raspicam/RaspiStill.c). 141 | func NewStill() *Still { 142 | newStill := defaultStill 143 | return &newStill 144 | } 145 | 146 | // StillYUV represents the configuration necessary to call raspistillYUV. 147 | type StillYUV struct { 148 | BaseStill 149 | UseRGB bool // Output RGB data rather than YUV 150 | } 151 | 152 | // The default StillYUV setup. 153 | var defaultStillYUV = StillYUV{ 154 | BaseStill: defaultBaseStill, 155 | } 156 | 157 | // String returns the parameter string for the given StillYUV struct. 158 | func (s *StillYUV) String() string { 159 | return paramString(s) 160 | } 161 | 162 | func (s *StillYUV) params() []string { 163 | out := params(s.BaseStill.params()) 164 | if s.UseRGB { 165 | out.add("--rgb") 166 | } 167 | return out 168 | } 169 | 170 | // Cmd returns the raspicam command for a StillYUV. 171 | func (s *StillYUV) Cmd() string { 172 | if s.BaseStill.Command != "" { 173 | return s.BaseStill.Command 174 | } 175 | return DefaultRaspiStillYUVCommand 176 | } 177 | 178 | // Params returns the parameters to be used in the command execution. 179 | func (s *StillYUV) Params() []string { 180 | return append(s.params(), s.BaseStill.Args...) 181 | } 182 | 183 | // NewStillYUV returns a *StillYUV with the default values set by the raspiyuv command 184 | // (see userland/linux/apps/raspicam/RaspiStillYUV.c). 185 | func NewStillYUV() *StillYUV { 186 | newStillYUV := defaultStillYUV 187 | return &newStillYUV 188 | } 189 | -------------------------------------------------------------------------------- /still_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, David Howden 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package raspicam 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | func TestDefaultParams(t *testing.T) { 14 | const paramsOut = "--output -" 15 | 16 | testNames := [...]string{"Still", "StillYUV", "Vid"} 17 | testCases := [...]CaptureCommand{NewStill(), NewStillYUV(), NewVid()} 18 | 19 | for i, test := range testNames { 20 | paramString := strings.Join(testCases[i].Params(), " ") 21 | if paramString != paramsOut { 22 | t.Errorf("%v: Param() returned %v, expected %v", test, paramString, paramsOut) 23 | } 24 | } 25 | } 26 | 27 | func TestDefaultParamsOptionalArgs(t *testing.T) { 28 | const paramsOut = "--output - arg1 arg2" 29 | 30 | args := strings.Fields("arg1 arg2") 31 | 32 | still := NewStill() 33 | still.Command = "test" 34 | still.Args = args 35 | 36 | stillYUV := NewStillYUV() 37 | stillYUV.BaseStill.Command = "test" 38 | stillYUV.BaseStill.Args = args 39 | 40 | vid := NewVid() 41 | vid.Command = "test" 42 | vid.Args = args 43 | 44 | testNames := [...]string{"Still", "StillYUV", "Vid"} 45 | testCases := [...]CaptureCommand{still, stillYUV, vid} 46 | 47 | for i, test := range testNames { 48 | paramString := strings.Join(testCases[i].Params(), " ") 49 | if paramString != paramsOut { 50 | t.Errorf("%v: Param() returned %v, expected %v", test, paramString, paramsOut) 51 | } 52 | } 53 | } 54 | 55 | func TestBasicParams(t *testing.T) { 56 | const paramsOut = "--output - --timeout 10000 --width 100 --height 1000" 57 | 58 | testNames := [...]string{"Still", "StillYUV", "Vid"} 59 | 60 | still := NewStill() 61 | still.Timeout = 10 * time.Second 62 | still.Width = 100 63 | still.Height = 1000 64 | 65 | stillYUV := NewStillYUV() 66 | stillYUV.Timeout = 10 * time.Second 67 | stillYUV.Width = 100 68 | stillYUV.Height = 1000 69 | 70 | vid := NewVid() 71 | vid.Timeout = 10 * time.Second 72 | vid.Width = 100 73 | vid.Height = 1000 74 | 75 | testCases := [...]CaptureCommand{still, stillYUV, vid} 76 | 77 | for i, test := range testNames { 78 | paramString := strings.Join(testCases[i].Params(), " ") 79 | if paramString != paramsOut { 80 | t.Errorf("%v: Param() returned %v, expected %v", test, paramString, paramsOut) 81 | } 82 | } 83 | } 84 | 85 | func TestCameraParams(t *testing.T) { 86 | const paramsOut = "--output - --timeout 10000 --sharpness 11 --contrast 13 --brightness 12" 87 | 88 | testNames := [...]string{"Still", "StillYUV", "Vid"} 89 | 90 | still := NewStill() 91 | still.Timeout = 10 * time.Second 92 | still.Camera.Sharpness = 11 93 | still.Camera.Brightness = 12 94 | still.Camera.Contrast = 13 95 | 96 | stillYUV := NewStillYUV() 97 | stillYUV.Timeout = 10 * time.Second 98 | stillYUV.Camera.Sharpness = 11 99 | stillYUV.Camera.Brightness = 12 100 | stillYUV.Camera.Contrast = 13 101 | 102 | vid := NewVid() 103 | vid.Timeout = 10 * time.Second 104 | vid.Camera.Sharpness = 11 105 | vid.Camera.Brightness = 12 106 | vid.Camera.Contrast = 13 107 | 108 | testCases := [...]CaptureCommand{still, stillYUV, vid} 109 | 110 | for i, test := range testNames { 111 | paramString := strings.Join(testCases[i].Params(), " ") 112 | if paramString != paramsOut { 113 | t.Errorf("%v: Param() returned %v, expected %v", test, paramString, paramsOut) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /vid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, David Howden 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package raspicam 6 | 7 | import ( 8 | "time" 9 | ) 10 | 11 | // DefaultRaspiVidCommmand is the default command for capturing video. 12 | var DefaultRaspiVidCommmand = "raspivid" 13 | 14 | // Vid represents the the configuration used to call raspivid. 15 | type Vid struct { 16 | Timeout time.Duration // Delay before image is taken 17 | Width, Height int // Image dimensions 18 | Bitrate int // Requested bitrate 19 | Framerate int // Requested framerate (fps) 20 | IntraPeriod int // Intra-refresh period (key frame rate) 21 | 22 | // Flag to specify whether encoder works in place or creates a new buffer. 23 | // Result is preview can display either the camera output or the encoder 24 | // output (with compression artifacts). 25 | ImmutableInput bool 26 | Camera Camera 27 | Preview Preview 28 | 29 | // The command to run when making a video capture. If left blank, the default 30 | // command is used. 31 | Command string 32 | 33 | // Additional arguments. Default is empty. 34 | Args []string 35 | } 36 | 37 | // The default Vid setup. 38 | // TODO: Framerate is set via a macro, should really call raspivid to get default. 39 | var defaultVid = Vid{ 40 | Timeout: 5 * time.Second, 41 | Width: 1920, 42 | Height: 1080, 43 | Bitrate: 17000000, 44 | Framerate: 30, 45 | ImmutableInput: true, 46 | Camera: defaultCamera, 47 | Preview: defaultPreview, 48 | } 49 | 50 | // String returns the parameter string for the given Vid struct. 51 | func (v *Vid) String() string { 52 | return paramString(v) 53 | } 54 | 55 | func (v *Vid) params() []string { 56 | var out params 57 | out.add("--output", "-") 58 | if v.Timeout != defaultVid.Timeout { 59 | out.addInt64("--timeout", int64(v.Timeout/time.Millisecond)) 60 | } 61 | if v.Width != defaultVid.Width { 62 | out.addInt("--width", v.Width) 63 | } 64 | if v.Height != defaultVid.Height { 65 | out.addInt("--height", v.Height) 66 | } 67 | if v.Bitrate != defaultVid.Bitrate { 68 | out.addInt("--bitrate", v.Bitrate) 69 | } 70 | if v.Framerate != defaultVid.Framerate { 71 | out.addInt("--framerate", v.Framerate) 72 | } 73 | if v.IntraPeriod != defaultVid.IntraPeriod { 74 | out.addInt("--intra", v.IntraPeriod) 75 | } 76 | out.add(v.Camera.params()...) 77 | out.add(v.Preview.params()...) 78 | return out 79 | } 80 | 81 | // Cmd returns the raspicam command for a Vid. 82 | func (v *Vid) Cmd() string { 83 | if v.Command != "" { 84 | return v.Command 85 | } 86 | return DefaultRaspiVidCommmand 87 | } 88 | 89 | // Params returns the parameters to be used in the command execution. 90 | func (v *Vid) Params() []string { 91 | return append(v.params(), v.Args...) 92 | } 93 | 94 | // NewVid returns a new *Vid struct setup with the default configuration. 95 | func NewVid() *Vid { 96 | newVid := defaultVid 97 | return &newVid 98 | } 99 | --------------------------------------------------------------------------------