Procházet zdrojové kódy

Merge pull request #26 from vladimirvivien/resource-leak-refactor

Fix for memory leak in stream loop
Vladimir Vivien před 3 roky
rodič
revize
8b53b15f80
4 změnil soubory, kde provedl 25 přidání a 25 odebrání
  1. 19 2
      device/device.go
  2. 2 1
      examples/webcam/webcam.go
  3. 0 18
      v4l2/streaming.go
  4. 4 4
      v4l2/streaming_loop.go

+ 19 - 2
device/device.go

@@ -388,13 +388,14 @@ func (d *Device) startStreamLoop(ctx context.Context) error {
 		fd := d.Fd()
 		ioMemType := d.MemIOType()
 		bufType := d.BufferType()
+		waitForRead := v4l2.WaitForRead(d)
 
 		for {
 			select {
 			// handle stream capture (read from driver)
-			case <-v4l2.WaitForRead(d):
+			case <-waitForRead:
 				//TODO add better error-handling, for now just panic
-				buff, err := v4l2.CaptureBuffer(fd, ioMemType, bufType)
+				buff, err := d.prepareCaptureBuffer(fd, ioMemType, bufType)
 				if err != nil {
 					panic(fmt.Errorf("stream loop: capture buffer: %s", err).Error())
 				}
@@ -410,3 +411,19 @@ func (d *Device) startStreamLoop(ctx context.Context) error {
 
 	return nil
 }
+
+// prepareCaptureBuffer prepares a frame buffer for stream capture
+func (d *Device) prepareCaptureBuffer(fd uintptr, ioType v4l2.IOType, bufType v4l2.BufType) (v4l2.Buffer, error) {
+	bufInfo, err := v4l2.DequeueBuffer(fd, ioType, bufType)
+	if err != nil {
+		return v4l2.Buffer{}, fmt.Errorf("capture buffer info: dequeue: %w", err)
+	}
+
+	// requeue/clear used buffer, prepare for next read
+	if _, err := v4l2.QueueBuffer(fd, ioType, bufType, bufInfo.Index); err != nil {
+		return v4l2.Buffer{}, fmt.Errorf("capture buffer info: queue: %w", err)
+	}
+
+	// return captured buffer info
+	return bufInfo, nil
+}

+ 2 - 1
examples/webcam/webcam.go

@@ -52,7 +52,8 @@ func serveVideoStream(w http.ResponseWriter, req *http.Request) {
 	w.Header().Set("Content-Type", fmt.Sprintf("multipart/x-mixed-replace; boundary=%s", boundaryName))
 	w.WriteHeader(http.StatusOK)
 
-	for frame := range frames {
+	var frame []byte
+	for frame = range frames {
 		if len(frame) == 0 {
 			log.Print("skipping empty frame")
 			continue

+ 0 - 18
v4l2/streaming.go

@@ -54,7 +54,6 @@ type RequestBuffers struct {
 // after streaming IO has been initialized.
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_buffer
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1037
-//
 type Buffer struct {
 	Index     uint32
 	Type      uint32
@@ -103,7 +102,6 @@ type BufferInfo struct {
 // Plane (see struct v4l2_plane) represents a plane in a multi-planar buffers
 // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_plane
 // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L990
-//
 type Plane struct {
 	BytesUsed  uint32
 	Length     uint32
@@ -262,19 +260,3 @@ func DequeueBuffer(fd uintptr, ioType IOType, bufType BufType) (Buffer, error) {
 
 	return makeBuffer(v4l2Buf), nil
 }
-
-// CaptureBuffer captures a frame buffer from the device
-func CaptureBuffer(fd uintptr, ioType IOType, bufType BufType) (Buffer, error) {
-	bufInfo, err := DequeueBuffer(fd, ioType, bufType)
-	if err != nil {
-		return Buffer{}, fmt.Errorf("capture frame: dequeue: %w", err)
-	}
-
-	// requeue/clear used buffer, prepare for next read
-	if _, err := QueueBuffer(fd, ioType, bufType, bufInfo.Index); err != nil {
-		return Buffer{}, fmt.Errorf("capture frame: queue: %w", err)
-	}
-
-	// return captured buffer
-	return bufInfo, nil
-}

+ 4 - 4
v4l2/streaming_loop.go

@@ -4,12 +4,12 @@ import (
 	sys "golang.org/x/sys/unix"
 )
 
+// WaitForRead returns a channel that can be used to be notified when
+// a device's is ready to be read.
 func WaitForRead(dev Device) <-chan struct{} {
 	sigChan := make(chan struct{})
 
-	fd := dev.Fd()
-
-	go func() {
+	go func(fd uintptr) {
 		defer close(sigChan)
 		var fdsRead sys.FdSet
 		fdsRead.Set(int(fd))
@@ -22,7 +22,7 @@ func WaitForRead(dev Device) <-chan struct{} {
 			}
 			sigChan <- struct{}{}
 		}
-	}()
+	}(dev.Fd())
 
 	return sigChan
 }