github.com/aamcrae/webcam@v0.0.0-20210915060337-934acc13bdc3/webcam.go (about)

     1  // Library for working with webcams and other video capturing devices.
     2  // It depends entirely on v4l2 framework, thus will compile and work
     3  // only on Linux machine
     4  package webcam
     5  
     6  import (
     7  	"errors"
     8  	"golang.org/x/sys/unix"
     9  )
    10  
    11  // Webcam object
    12  type Webcam struct {
    13  	fd        uintptr
    14  	bufcount  uint32
    15  	buffers   [][]byte
    16  	streaming bool
    17  }
    18  
    19  type ControlID uint32
    20  
    21  type Control struct {
    22  	Name string
    23  	Min  int32
    24  	Max  int32
    25  }
    26  
    27  // Open a webcam with a given path
    28  // Checks if device is a v4l2 device and if it is
    29  // capable to stream video
    30  func Open(path string) (*Webcam, error) {
    31  
    32  	handle, err := unix.Open(path, unix.O_RDWR|unix.O_NONBLOCK, 0666)
    33  	fd := uintptr(handle)
    34  
    35  	if fd < 0 || err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	supportsVideoCapture, supportsVideoStreaming, err := checkCapabilities(fd)
    40  
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	if !supportsVideoCapture {
    46  		return nil, errors.New("Not a video capture device")
    47  	}
    48  
    49  	if !supportsVideoStreaming {
    50  		return nil, errors.New("Device does not support the streaming I/O method")
    51  	}
    52  
    53  	w := new(Webcam)
    54  	w.fd = uintptr(fd)
    55  	w.bufcount = 256
    56  	return w, nil
    57  }
    58  
    59  // Returns image formats supported by the device alongside with
    60  // their text description
    61  // Not that this function is somewhat experimental. Frames are not ordered in
    62  // any meaning, also duplicates can occur so it's up to developer to clean it up.
    63  // See http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-enum-framesizes.html
    64  // for more information
    65  func (w *Webcam) GetSupportedFormats() map[PixelFormat]string {
    66  
    67  	result := make(map[PixelFormat]string)
    68  	var err error
    69  	var code uint32
    70  	var desc string
    71  	var index uint32
    72  
    73  	for index = 0; err == nil; index++ {
    74  		code, desc, err = getPixelFormat(w.fd, index)
    75  
    76  		if err != nil {
    77  			break
    78  		}
    79  
    80  		result[PixelFormat(code)] = desc
    81  	}
    82  
    83  	return result
    84  }
    85  
    86  // Returns supported frame sizes for a given image format
    87  func (w *Webcam) GetSupportedFrameSizes(f PixelFormat) []FrameSize {
    88  	result := make([]FrameSize, 0)
    89  
    90  	var index uint32
    91  	var err error
    92  
    93  	for index = 0; err == nil; index++ {
    94  		s, err := getFrameSize(w.fd, index, uint32(f))
    95  
    96  		if err != nil {
    97  			break
    98  		}
    99  
   100  		result = append(result, s)
   101  	}
   102  
   103  	return result
   104  }
   105  
   106  // Sets desired image format and frame size
   107  // Note, that device driver can change that values.
   108  // Resulting values are returned by a function
   109  // alongside with an error if any
   110  func (w *Webcam) SetImageFormat(f PixelFormat, width, height uint32) (PixelFormat, uint32, uint32, uint32, uint32, error) {
   111  
   112  	code := uint32(f)
   113  	cw := width
   114  	ch := height
   115  	var stride uint32
   116  	var size uint32
   117  
   118  	err := setImageFormat(w.fd, &code, &width, &height, &stride, &size)
   119  
   120  	if err != nil {
   121  		return 0, 0, 0, 0, 0, err
   122  	} else {
   123  		return PixelFormat(code), cw, ch, stride, size, nil
   124  	}
   125  }
   126  
   127  // Set the number of frames to be buffered.
   128  // Not allowed if streaming is already on.
   129  func (w *Webcam) SetBufferCount(count uint32) error {
   130  	if w.streaming {
   131  		return errors.New("Cannot set buffer count when streaming")
   132  	}
   133  	w.bufcount = count
   134  	return nil
   135  }
   136  
   137  // Get a map of available controls.
   138  func (w *Webcam) GetControls() map[ControlID]Control {
   139  	cmap := make(map[ControlID]Control)
   140  	for _, c := range queryControls(w.fd) {
   141  		cmap[ControlID(c.id)] = Control{c.name, c.min, c.max}
   142  	}
   143  	return cmap
   144  }
   145  
   146  // Get the value of a control.
   147  func (w *Webcam) GetControl(id ControlID) (int32, error) {
   148  	return getControl(w.fd, uint32(id))
   149  }
   150  
   151  // Set a control.
   152  func (w *Webcam) SetControl(id ControlID, value int32) error {
   153  	return setControl(w.fd, uint32(id), value)
   154  }
   155  
   156  // Start streaming process
   157  func (w *Webcam) StartStreaming() error {
   158  	if w.streaming {
   159  		return errors.New("Already streaming")
   160  	}
   161  
   162  	err := mmapRequestBuffers(w.fd, &w.bufcount)
   163  
   164  	if err != nil {
   165  		return errors.New("Failed to map request buffers: " + string(err.Error()))
   166  	}
   167  
   168  	w.buffers = make([][]byte, w.bufcount, w.bufcount)
   169  	for index, _ := range w.buffers {
   170  		var length uint32
   171  
   172  		buffer, err := mmapQueryBuffer(w.fd, uint32(index), &length)
   173  
   174  		if err != nil {
   175  			return errors.New("Failed to map memory: " + string(err.Error()))
   176  		}
   177  
   178  		w.buffers[index] = buffer
   179  	}
   180  
   181  	for index, _ := range w.buffers {
   182  
   183  		err := mmapEnqueueBuffer(w.fd, uint32(index))
   184  
   185  		if err != nil {
   186  			return errors.New("Failed to enqueue buffer: " + string(err.Error()))
   187  		}
   188  
   189  	}
   190  
   191  	err = startStreaming(w.fd)
   192  
   193  	if err != nil {
   194  		return errors.New("Failed to start streaming: " + string(err.Error()))
   195  	}
   196  	w.streaming = true
   197  
   198  	return nil
   199  }
   200  
   201  // Read a single frame from the webcam
   202  // If frame cannot be read at the moment
   203  // function will return empty slice
   204  func (w *Webcam) ReadFrame() ([]byte, error) {
   205  	result, index, err := w.GetFrame()
   206  	if err == nil {
   207  		w.ReleaseFrame(index)
   208  	}
   209  	return result, err
   210  }
   211  
   212  // Get a single frame from the webcam and return the frame and
   213  // the buffer index. To return the buffer, ReleaseFrame must be called.
   214  // If frame cannot be read at the moment
   215  // function will return empty slice
   216  func (w *Webcam) GetFrame() ([]byte, uint32, error) {
   217  	var index uint32
   218  	var length uint32
   219  
   220  	err := mmapDequeueBuffer(w.fd, &index, &length)
   221  
   222  	if err != nil {
   223  		return nil, 0, err
   224  	}
   225  
   226  	return w.buffers[int(index)][:length], index, nil
   227  
   228  }
   229  
   230  // Release the frame buffer that was obtained via GetFrame
   231  func (w *Webcam) ReleaseFrame(index uint32) error {
   232  	return mmapEnqueueBuffer(w.fd, index)
   233  }
   234  
   235  // Wait until frame could be read
   236  func (w *Webcam) WaitForFrame(timeout uint32) error {
   237  
   238  	count, err := waitForFrame(w.fd, timeout)
   239  
   240  	if count < 0 || err != nil {
   241  		return err
   242  	} else if count == 0 {
   243  		return new(Timeout)
   244  	} else {
   245  		return nil
   246  	}
   247  }
   248  
   249  func (w *Webcam) StopStreaming() error {
   250  	if !w.streaming {
   251  		return errors.New("Request to stop streaming when not streaming")
   252  	}
   253  	w.streaming = false
   254  	for _, buffer := range w.buffers {
   255  		err := mmapReleaseBuffer(buffer)
   256  		if err != nil {
   257  			return err
   258  		}
   259  	}
   260  
   261  	return stopStreaming(w.fd)
   262  }
   263  
   264  // Close the device
   265  func (w *Webcam) Close() error {
   266  	if w.streaming {
   267  		w.StopStreaming()
   268  	}
   269  
   270  	err := unix.Close(int(w.fd))
   271  
   272  	return err
   273  }
   274  
   275  // Sets automatic white balance correction
   276  func (w *Webcam) SetAutoWhiteBalance(val bool) error {
   277  	v := int32(0)
   278  	if val {
   279  		v = 1
   280  	}
   281  	return setControl(w.fd, V4L2_CID_AUTO_WHITE_BALANCE, v)
   282  }