github.com/blackjack/webcam@v0.0.0-20230509180125-87693b3f29dc/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  	"reflect"
     9  	"unsafe"
    10  
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  // Webcam object
    15  type Webcam struct {
    16  	fd        uintptr
    17  	bufcount  uint32
    18  	buffers   [][]byte
    19  	streaming bool
    20  }
    21  
    22  type ControlID uint32
    23  
    24  type Control struct {
    25  	Name string
    26  	Min  int32
    27  	Max  int32
    28  	Type int32
    29  	Step int32
    30  }
    31  
    32  // Open a webcam with a given path
    33  // Checks if device is a v4l2 device and if it is
    34  // capable to stream video
    35  func Open(path string) (*Webcam, error) {
    36  
    37  	handle, err := unix.Open(path, unix.O_RDWR|unix.O_NONBLOCK, 0666)
    38  	fd := uintptr(handle)
    39  
    40  	if fd < 0 || err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	supportsVideoCapture, supportsVideoStreaming, err := checkCapabilities(fd)
    45  
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	if !supportsVideoCapture {
    51  		return nil, errors.New("Not a video capture device")
    52  	}
    53  
    54  	if !supportsVideoStreaming {
    55  		return nil, errors.New("Device does not support the streaming I/O method")
    56  	}
    57  
    58  	w := new(Webcam)
    59  	w.fd = fd
    60  	w.bufcount = 256
    61  	return w, nil
    62  }
    63  
    64  // Returns image formats supported by the device alongside with
    65  // their text description
    66  // Note that this function is somewhat experimental. Frames are not ordered in
    67  // any meaning, also duplicates can occur so it's up to developer to clean it up.
    68  // See http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-enum-framesizes.html
    69  // for more information
    70  func (w *Webcam) GetSupportedFormats() map[PixelFormat]string {
    71  
    72  	result := make(map[PixelFormat]string)
    73  	var err error
    74  	var code uint32
    75  	var desc string
    76  	var index uint32
    77  
    78  	for index = 0; err == nil; index++ {
    79  		code, desc, err = getPixelFormat(w.fd, index)
    80  
    81  		if err != nil {
    82  			break
    83  		}
    84  
    85  		result[PixelFormat(code)] = desc
    86  	}
    87  
    88  	return result
    89  }
    90  
    91  // GetName returns the human-readable name of the device
    92  func (w *Webcam) GetName() (string, error) {
    93  	return getName(w.fd)
    94  }
    95  
    96  // GetBusInfo returns the location of the device in the system
    97  func (w *Webcam) GetBusInfo() (string, error) {
    98  	return getBusInfo(w.fd)
    99  }
   100  
   101  // SelectInput selects the current video input.
   102  func (w *Webcam) SelectInput(index uint32) error {
   103  	return selectInput(w.fd, index)
   104  }
   105  
   106  // GetInput queries the current video input.
   107  func (w *Webcam) GetInput() (int32, error) {
   108  	return getInput(w.fd)
   109  }
   110  
   111  // Returns supported frame sizes for a given image format
   112  func (w *Webcam) GetSupportedFrameSizes(f PixelFormat) []FrameSize {
   113  	result := make([]FrameSize, 0)
   114  
   115  	var index uint32
   116  	var err error
   117  
   118  	for index = 0; err == nil; index++ {
   119  		s, err := getFrameSize(w.fd, index, uint32(f))
   120  
   121  		if err != nil {
   122  			break
   123  		}
   124  
   125  		result = append(result, s)
   126  	}
   127  
   128  	return result
   129  }
   130  
   131  // GetSupportedFramerates returns supported frame rates for a given image format and frame size.
   132  func (w *Webcam) GetSupportedFramerates(fp PixelFormat, width uint32, height uint32) []FrameRate {
   133  	var result []FrameRate
   134  	var index uint32
   135  	var err error
   136  
   137  	// keep incrementing the index value until we get an EINVAL error
   138  	index = 0
   139  	for err == nil {
   140  		r, err := getFrameInterval(w.fd, index, uint32(fp), width, height)
   141  		if err != nil {
   142  			break
   143  		}
   144  		result = append(result, r)
   145  		index++
   146  	}
   147  
   148  	return result
   149  }
   150  
   151  // Sets desired image format and frame size
   152  // Note, that device driver can change that values.
   153  // Resulting values are returned by a function
   154  // alongside with an error if any
   155  func (w *Webcam) SetImageFormat(f PixelFormat, width, height uint32) (PixelFormat, uint32, uint32, error) {
   156  
   157  	code := uint32(f)
   158  	cw := width
   159  	ch := height
   160  
   161  	err := setImageFormat(w.fd, &code, &width, &height)
   162  
   163  	if err != nil {
   164  		return 0, 0, 0, err
   165  	} else {
   166  		return PixelFormat(code), cw, ch, nil
   167  	}
   168  }
   169  
   170  // Set the number of frames to be buffered.
   171  // Not allowed if streaming is already on.
   172  func (w *Webcam) SetBufferCount(count uint32) error {
   173  	if w.streaming {
   174  		return errors.New("Cannot set buffer count when streaming")
   175  	}
   176  	w.bufcount = count
   177  	return nil
   178  }
   179  
   180  // Get a map of available controls.
   181  func (w *Webcam) GetControls() map[ControlID]Control {
   182  	cmap := make(map[ControlID]Control)
   183  	for _, c := range queryControls(w.fd) {
   184  		cmap[ControlID(c.id)] = Control{
   185  			Name: c.name,
   186  			Min:  c.min,
   187  			Max:  c.max,
   188  			Type: int32(c.c_type),
   189  			Step: c.step,
   190  		}
   191  	}
   192  	return cmap
   193  }
   194  
   195  // Get the value of a control.
   196  func (w *Webcam) GetControl(id ControlID) (int32, error) {
   197  	return getControl(w.fd, uint32(id))
   198  }
   199  
   200  // Set a control.
   201  func (w *Webcam) SetControl(id ControlID, value int32) error {
   202  	return setControl(w.fd, uint32(id), value)
   203  }
   204  
   205  // Get the framerate.
   206  func (w *Webcam) GetFramerate() (float32, error) {
   207  	return getFramerate(w.fd)
   208  }
   209  
   210  // Set FPS
   211  func (w *Webcam) SetFramerate(fps float32) error {
   212  	return setFramerate(w.fd, 1000, uint32(1000*(fps)))
   213  }
   214  
   215  // Start streaming process
   216  func (w *Webcam) StartStreaming() error {
   217  	if w.streaming {
   218  		return errors.New("Already streaming")
   219  	}
   220  
   221  	err := mmapRequestBuffers(w.fd, &w.bufcount)
   222  
   223  	if err != nil {
   224  		return errors.New("Failed to map request buffers: " + string(err.Error()))
   225  	}
   226  
   227  	w.buffers = make([][]byte, w.bufcount, w.bufcount)
   228  	for index, _ := range w.buffers {
   229  		var length uint32
   230  
   231  		buffer, err := mmapQueryBuffer(w.fd, uint32(index), &length)
   232  
   233  		if err != nil {
   234  			return errors.New("Failed to map memory: " + string(err.Error()))
   235  		}
   236  
   237  		w.buffers[index] = buffer
   238  	}
   239  
   240  	for index, _ := range w.buffers {
   241  
   242  		err := mmapEnqueueBuffer(w.fd, uint32(index))
   243  
   244  		if err != nil {
   245  			return errors.New("Failed to enqueue buffer: " + string(err.Error()))
   246  		}
   247  
   248  	}
   249  
   250  	err = startStreaming(w.fd)
   251  
   252  	if err != nil {
   253  		return errors.New("Failed to start streaming: " + string(err.Error()))
   254  	}
   255  	w.streaming = true
   256  
   257  	return nil
   258  }
   259  
   260  // Read a single frame from the webcam
   261  // If frame cannot be read at the moment
   262  // function will return empty slice
   263  func (w *Webcam) ReadFrame() ([]byte, error) {
   264  	result, index, err := w.GetFrame()
   265  	if err == nil {
   266  		w.ReleaseFrame(index)
   267  	}
   268  	return result, err
   269  }
   270  
   271  // Get a single frame from the webcam and return the frame and
   272  // the buffer index. To return the buffer, ReleaseFrame must be called.
   273  // If frame cannot be read at the moment
   274  // function will return empty slice
   275  func (w *Webcam) GetFrame() ([]byte, uint32, error) {
   276  	var index uint32
   277  	var length uint32
   278  
   279  	err := mmapDequeueBuffer(w.fd, &index, &length)
   280  
   281  	if err != nil {
   282  		return nil, 0, err
   283  	}
   284  
   285  	return w.buffers[int(index)][:length], index, nil
   286  
   287  }
   288  
   289  // Release the frame buffer that was obtained via GetFrame
   290  func (w *Webcam) ReleaseFrame(index uint32) error {
   291  	return mmapEnqueueBuffer(w.fd, index)
   292  }
   293  
   294  // Wait until frame could be read
   295  func (w *Webcam) WaitForFrame(timeout uint32) error {
   296  
   297  	count, err := waitForFrame(w.fd, timeout)
   298  
   299  	if count < 0 || err != nil {
   300  		return err
   301  	} else if count == 0 {
   302  		return new(Timeout)
   303  	} else {
   304  		return nil
   305  	}
   306  }
   307  
   308  func (w *Webcam) StopStreaming() error {
   309  	if !w.streaming {
   310  		return errors.New("Request to stop streaming when not streaming")
   311  	}
   312  	w.streaming = false
   313  	for _, buffer := range w.buffers {
   314  		err := mmapReleaseBuffer(buffer)
   315  		if err != nil {
   316  			return err
   317  		}
   318  	}
   319  
   320  	return stopStreaming(w.fd)
   321  }
   322  
   323  // Close the device
   324  func (w *Webcam) Close() error {
   325  	if w.streaming {
   326  		w.StopStreaming()
   327  	}
   328  
   329  	err := unix.Close(int(w.fd))
   330  
   331  	return err
   332  }
   333  
   334  // Sets automatic white balance correction
   335  func (w *Webcam) SetAutoWhiteBalance(val bool) error {
   336  	v := int32(0)
   337  	if val {
   338  		v = 1
   339  	}
   340  	return setControl(w.fd, V4L2_CID_AUTO_WHITE_BALANCE, v)
   341  }
   342  
   343  func gobytes(p unsafe.Pointer, n int) []byte {
   344  
   345  	h := reflect.SliceHeader{uintptr(p), n, n}
   346  	s := *(*[]byte)(unsafe.Pointer(&h))
   347  
   348  	return s
   349  }