Add context with timeout to darwin Read() calls (#674)

* Add timeout

* Clean
This commit is contained in:
sean yu
2025-12-10 22:30:30 -05:00
committed by GitHub
parent 7b76fa0ce4
commit 0e726eac75
2 changed files with 46 additions and 5 deletions

View File

@@ -136,11 +136,26 @@ func (rc *ReadCloser) dataCb(data []byte) {
// - For video, each call will return a frame.
// - For audio, each call will return a chunk which its size configured by Latency
func (rc *ReadCloser) Read() ([]byte, func(), error) {
data, ok := <-rc.dataChan
if !ok {
return nil, func() {}, io.EOF
data, release, err := rc.ReadContext(context.Background())
if err != nil {
return nil, release, err
}
return data, release, nil
}
// ReadContext is Read but with a context for better error handling e.g. timeout, cancellation, etc.
func (rc *ReadCloser) ReadContext(ctx context.Context) ([]byte, func(), error) {
release := func() {} // no-op to satisfy Reader interface
select {
case <-ctx.Done():
return nil, release, ctx.Err()
case data, ok := <-rc.dataChan:
if !ok {
return nil, release, io.EOF
}
return data, release, nil
}
return data, func() {}, nil
}
// Close closes the capturing session, and no data will flow anymore

View File

@@ -1,7 +1,11 @@
package camera
import (
"context"
"errors"
"image"
"io"
"time"
"github.com/pion/mediadevices/pkg/avfoundation"
"github.com/pion/mediadevices/pkg/driver"
@@ -14,8 +18,11 @@ type camera struct {
device avfoundation.Device
session *avfoundation.Session
rcClose func()
cancel context.CancelFunc
}
const readTimeout = 3 * time.Second
func init() {
Initialize()
}
@@ -53,6 +60,11 @@ func (cam *camera) Close() error {
if cam.rcClose != nil {
cam.rcClose()
}
if cam.cancel != nil {
cam.cancel()
}
return cam.session.Close()
}
@@ -66,10 +78,24 @@ func (cam *camera) VideoRecord(property prop.Media) (video.Reader, error) {
if err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
cam.cancel = cancel
cam.rcClose = rc.Close
r := video.ReaderFunc(func() (image.Image, func(), error) {
frame, _, err := rc.Read()
if ctx.Err() != nil {
// Return EOF if the camera is already closed.
return nil, func() {}, io.EOF
}
readCtx, cancel := context.WithTimeout(ctx, readTimeout)
defer cancel()
frame, _, err := rc.ReadContext(readCtx)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
return nil, func() {}, io.EOF
}
return nil, func() {}, err
}
return decoder.Decode(frame, property.Width, property.Height)