-
Notifications
You must be signed in to change notification settings - Fork 126
RSDK-12758 — Support webcam unplug replug recovery on darwin #5618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| //go:build darwin | ||
|
|
||
| package videosource | ||
|
|
||
| import ( | ||
| mediadevicescamera "github.com/pion/mediadevices/pkg/driver/camera" | ||
|
|
||
| "go.viam.com/rdk/logging" | ||
| ) | ||
|
|
||
| // startCameraObserver starts the Darwin camera device observer for hot-plug support. | ||
| // This should be called after SetupObserver has been called from the main thread. | ||
| // startCameraObserver is idempotent and can safely be called multiple times. | ||
| func startCameraObserver(logger logging.Logger) { | ||
| if err := mediadevicescamera.StartObserver(); err != nil { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this need to be cleaned up ever?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it should be cleaned up by |
||
| logger.Errorw("failed to start darwin mediadevices camera observer; webcams will not handle hot unplug/replug", "error", err) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| //go:build !darwin | ||
|
|
||
| package videosource | ||
|
|
||
| import "go.viam.com/rdk/logging" | ||
|
|
||
| // startCameraObserver is a no-op on non-Darwin platforms. | ||
| // Camera hot-plug detection via AVFoundation observers is only supported on macOS. | ||
| func startCameraObserver(_ logging.Logger) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,6 +132,13 @@ func NewWebcam( | |
| conf resource.Config, | ||
| logger logging.Logger, | ||
| ) (camera.Camera, error) { | ||
| // Start camera observer for hot-plug support (darwin only, no-op on other platforms). | ||
| // SetupObserver and DestroyObserver are called in RDK's entrypoint main.go to satisfy | ||
| // AVFoundation's threading requirements. startCameraObserver is idempotent and safely | ||
| // starts the observer for this component. | ||
| // See web/cmd/server/observer_darwin.go for details on threading. | ||
| startCameraObserver(logger) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it safe to call startCameraObserver multiple times?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I wrote it so that we can make it work like this in RDK (idempotent) |
||
|
|
||
| c := &webcam{ | ||
| Named: conf.ResourceName().AsNamed(), | ||
| logger: logger.WithFields("camera_name", conf.ResourceName().ShortName()), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| //go:build darwin | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| mediadevicescamera "github.com/pion/mediadevices/pkg/driver/camera" | ||
|
|
||
| "go.viam.com/rdk/logging" | ||
| ) | ||
|
|
||
| // setupCameraObserver initializes the Darwin camera device observer for hot-plug support. | ||
| // | ||
| // On Darwin/macOS, SetupObserver must be called from the main.go because AVFoundation requires | ||
| // that camera device notification events and Key-Value Observation (KVO) updates occur on the main thread. | ||
| // The mediadevices library uses runtime.LockOSThread() to pin the background goroutine to whatever thread | ||
| // calls SetupObserver, so calling it in main() ensures that we run on the correct thread. | ||
| // | ||
| // This is why SetupObserver is called here rather than in the webcam component constructor: | ||
| // component constructors can be invoked from arbitrary goroutines, which would violate | ||
| // AVFoundation's threading requirements. StartObserver (called in webcam.go) can safely run from | ||
| // any thread after SetupObserver has been called. | ||
| // | ||
| // See: https://github.com/pion/mediadevices/pull/670 | ||
| func setupCameraObserver(logger logging.Logger) func() { | ||
| if err := mediadevicescamera.SetupObserver(); err != nil { | ||
| logger.Errorw("failed to set up darwin mediadevices camera observer; webcams will not handle hot unplug/replug", "error", err) | ||
| } | ||
| return func() { | ||
| if err := mediadevicescamera.DestroyObserver(); err != nil { | ||
| logger.Errorw("failed to destroy darwin mediadevices camera observer", "error", err) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| //go:build !darwin | ||
abe-winter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| package main | ||
|
|
||
| import "go.viam.com/rdk/logging" | ||
|
|
||
| // setupCameraObserver is a no-op on non-Darwin platforms. | ||
| // Camera hot-plug detection via AVFoundation observers is only supported on macOS. | ||
| // On Linux, poll-based device enumeration is used instead (checking /dev/video* files). | ||
| // On Windows, Media Foundation APIs handle device enumeration differently. | ||
| func setupCameraObserver(_ logging.Logger) func() { | ||
| return func() {} | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens if you get the order wrong? crash?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StartObserveractually callsSetupObserverunder the hood, but we callSetupObserverfirst in the entrypointmain.goto allow the objective-c NSRunLoop to set up on the main thread. It would be the "wrong" order if we only calledStartObserverin the webcam code and didn't callSetupObserverin main.go