github.com/aamcrae/webcam@v0.0.0-20210915060337-934acc13bdc3/snapshot/snapshot.go (about) 1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // snapshot extracts image frames from a video stream. 16 package snapshot 17 18 import ( 19 "fmt" 20 21 "github.com/aamcrae/webcam" 22 "github.com/aamcrae/webcam/frame" 23 ) 24 25 const ( 26 defaultTimeout = 5 27 defaultBuffers = 16 28 ) 29 30 type snap struct { 31 frm []byte 32 index uint32 33 } 34 35 type Snapper struct { 36 cam *webcam.Webcam 37 Timeout uint32 38 Buffers uint32 39 framer func([]byte, func()) (frame.Frame, error) 40 stop chan struct{} 41 stream chan snap 42 } 43 44 // NewSnapper creates a new Snapper. 45 func NewSnapper() *Snapper { 46 return &Snapper{Timeout: defaultTimeout, Buffers: defaultBuffers} 47 } 48 49 // Close releases all current frames and shuts down the webcam. 50 func (c *Snapper) Close() { 51 if c.cam != nil { 52 c.stop <- struct{}{} 53 // Flush any remaining frames. 54 for f := range c.stream { 55 c.cam.ReleaseFrame(f.index) 56 } 57 c.cam.StopStreaming() 58 c.cam.Close() 59 c.cam = nil 60 } 61 } 62 63 // Open initialises the webcam ready for use, and begins streaming. 64 func (c *Snapper) Open(device string, format frame.FourCC, w, h int) (ret error) { 65 pf, err := frame.FourCCToPixelFormat(format) 66 if err != nil { 67 return err 68 } 69 if c.cam != nil { 70 c.Close() 71 } 72 cam, err := webcam.Open(device) 73 if err != nil { 74 return err 75 } 76 // Add a defer function that closes the camera in the event of an error. 77 defer func() { 78 if ret != nil { 79 close(c.stream) 80 c.Close() 81 } 82 }() 83 c.cam = cam 84 c.stop = make(chan struct{}, 1) 85 c.stream = make(chan snap, 0) 86 // Get the supported formats and their descriptions. 87 _, ok := c.cam.GetSupportedFormats()[pf] 88 if !ok { 89 return fmt.Errorf("%s: unsupported format: %s", device, format) 90 } 91 var found bool 92 for _, value := range c.cam.GetSupportedFrameSizes(pf) { 93 if Match(value, w, h) { 94 found = true 95 break 96 } 97 } 98 if !found { 99 return fmt.Errorf("%s: unsupported resolution: %dx%d", device, w, h) 100 } 101 npf, nw, nh, stride, size, err := c.cam.SetImageFormat(pf, uint32(w), uint32(h)) 102 103 if err != nil { 104 return err 105 } 106 if npf != pf || w != int(nw) || h != int(nh) { 107 fmt.Printf("Asked for %08x %dx%d, got %08x %dx%d\n", pf, w, h, npf, nw, nh) 108 } 109 if c.framer, err = frame.GetFramer(format, w, h, int(stride), int(size)); err != nil { 110 return err 111 } 112 113 c.cam.SetBufferCount(c.Buffers) 114 c.cam.SetAutoWhiteBalance(true) 115 if err := c.cam.StartStreaming(); err != nil { 116 return err 117 } 118 go c.capture() 119 return nil 120 } 121 122 // Snap returns one frame from the camera. 123 func (c *Snapper) Snap() (frame.Frame, error) { 124 snap, ok := <-c.stream 125 if !ok { 126 return nil, fmt.Errorf("No frame received") 127 } 128 return c.framer(snap.frm, func() { 129 c.cam.ReleaseFrame(snap.index) 130 }) 131 } 132 133 // capture continually reads frames and either discards the frames or 134 // sends them to a channel that is ready. 135 func (c *Snapper) capture() { 136 for { 137 err := c.cam.WaitForFrame(c.Timeout) 138 139 switch err.(type) { 140 case nil: 141 case *webcam.Timeout: 142 continue 143 default: 144 panic(err) 145 } 146 147 frame, index, err := c.cam.GetFrame() 148 if err != nil { 149 panic(err) 150 } 151 select { 152 // Only executed if stream is ready to receive. 153 case c.stream <- snap{frame, index}: 154 // Signal to stop streaming. 155 case <-c.stop: 156 // Finish up. 157 c.cam.ReleaseFrame(index) 158 close(c.stream) 159 return 160 default: 161 c.cam.ReleaseFrame(index) 162 } 163 } 164 } 165 166 // GetControl returns the current value of a camera control. 167 func (c *Snapper) GetControl(id webcam.ControlID) (int32, error) { 168 return c.cam.GetControl(id) 169 } 170 171 // SetControl sets the selected camera control. 172 func (c *Snapper) SetControl(id webcam.ControlID, value int32) error { 173 return c.cam.SetControl(id, value) 174 } 175 176 // Return true if frame size can accomodate request. 177 func Match(fs webcam.FrameSize, w, h int) bool { 178 return canFit(fs.MinWidth, fs.MaxWidth, fs.StepWidth, uint32(w)) && 179 canFit(fs.MinHeight, fs.MaxHeight, fs.StepHeight, uint32(h)) 180 } 181 182 func canFit(min, max, step, val uint32) bool { 183 // Fixed size exact match. 184 if min == max && step == 0 && val == min { 185 return true 186 } 187 return step != 0 && val >= min && val <= max && ((val-min)%step) == 0 188 }