mirror of
https://github.com/pion/mediadevices.git
synced 2026-04-22 15:57:27 +08:00
(3/3) Fix concurrency webcam windows (#684)
* Import wmcodecdsp and try to use their nv12 const * Remove ifndef * Enhance getCameraName function to retrieve friendly name from IPropertyBag, with fallback to display name. Update LDFLAGS in camera_windows.go to include oleaut32 library. * Don't mistake label for friendly name * Initialize COM for thread safety in Open and VideoRecord methods of camera_windows.go * Refactor camera handling in camera_windows.go and camera_windows.cpp - Introduced mutex for thread safety in camera struct to protect shared resources. - Updated Open method to initialize done channel and manage camera state. - Enhanced Close method to ensure proper cleanup and prevent double closing. - Improved resolution listing logic in camera_windows.cpp by ensuring media types are freed correctly. - Changed memory deletion from single to array deletion for camera properties. * Fix issues post-rebase * Refactor Close method * Transfer golang buffer management to c to avoid gc * Fix race condition in imageCallback by fixing unlock code location
This commit is contained in:
@@ -253,7 +253,6 @@ int listResolution(camera* cam, const char** errstr)
|
||||
ICaptureGraphBuilder2* captureGraph = nullptr;
|
||||
IAMStreamConfig* config = nullptr;
|
||||
IPin* src = nullptr;
|
||||
LPOLESTR name;
|
||||
|
||||
if (!selectCamera(cam, &moniker, errstr))
|
||||
{
|
||||
@@ -623,7 +622,7 @@ void freeCamera(camera* cam)
|
||||
|
||||
if (cam->props)
|
||||
{
|
||||
delete cam->props;
|
||||
delete[] cam->props;
|
||||
cam->props = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,15 @@ var (
|
||||
|
||||
type camera struct {
|
||||
name string
|
||||
// mu protects the fields under as per the mutex hat convention.
|
||||
mu sync.Mutex
|
||||
cam *C.camera
|
||||
closed bool
|
||||
ch chan []byte
|
||||
buf []byte
|
||||
done chan struct{}
|
||||
|
||||
cbuf unsafe.Pointer // C.malloc'd buffer for DirectShow writes
|
||||
bufLen int // byte length of cbuf
|
||||
bufGo []byte
|
||||
}
|
||||
|
||||
@@ -83,7 +89,20 @@ func DestroyObserver() error {
|
||||
}
|
||||
|
||||
func (c *camera) Open() error {
|
||||
// COM is per-thread on Windows. Go goroutines can run on any OS thread,
|
||||
// so ensure COM is initialized on the current one before making COM calls.
|
||||
C.CoInitializeEx(nil, C.COINIT_MULTITHREADED)
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.cam != nil {
|
||||
return fmt.Errorf("camera already open")
|
||||
}
|
||||
|
||||
c.ch = make(chan []byte)
|
||||
c.done = make(chan struct{})
|
||||
c.closed = false
|
||||
c.cam = &C.camera{
|
||||
name: C.CString(c.name),
|
||||
}
|
||||
@@ -91,6 +110,7 @@ func (c *camera) Open() error {
|
||||
var errStr *C.char
|
||||
if C.listResolution(c.cam, &errStr) != 0 {
|
||||
C.free(unsafe.Pointer(c.cam.name))
|
||||
c.cam = nil
|
||||
return fmt.Errorf("failed to open device: %s", C.GoString(errStr))
|
||||
}
|
||||
|
||||
@@ -101,36 +121,74 @@ func (c *camera) Open() error {
|
||||
func imageCallback(cam uintptr) {
|
||||
callbacksMu.RLock()
|
||||
cb, ok := callbacks[uintptr(unsafe.Pointer(cam))]
|
||||
callbacksMu.RUnlock()
|
||||
if !ok {
|
||||
callbacksMu.RUnlock()
|
||||
return
|
||||
}
|
||||
copy(cb.bufGo, unsafe.Slice((*byte)(cb.cbuf), cb.bufLen))
|
||||
callbacksMu.RUnlock()
|
||||
|
||||
copy(cb.bufGo, cb.buf)
|
||||
cb.ch <- cb.bufGo
|
||||
select {
|
||||
case cb.ch <- cb.bufGo:
|
||||
case <-cb.done:
|
||||
}
|
||||
}
|
||||
|
||||
func (c *camera) Close() error {
|
||||
callbacksMu.Lock()
|
||||
key := uintptr(unsafe.Pointer(c.cam))
|
||||
if _, ok := callbacks[key]; ok {
|
||||
delete(callbacks, key)
|
||||
c.mu.Lock()
|
||||
if c.closed {
|
||||
c.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
callbacksMu.Unlock()
|
||||
close(c.ch)
|
||||
|
||||
if c.cam != nil {
|
||||
C.free(unsafe.Pointer(c.cam.name))
|
||||
C.freeCamera(c.cam)
|
||||
c.closed = true
|
||||
cam := c.cam
|
||||
c.cam = nil
|
||||
cbuf := c.cbuf
|
||||
c.cbuf = nil
|
||||
done := c.done
|
||||
ch := c.ch
|
||||
c.mu.Unlock()
|
||||
|
||||
// Remove from callbacks map so no new imageCallback calls find this cam
|
||||
callbacksMu.Lock()
|
||||
delete(callbacks, uintptr(unsafe.Pointer(cam)))
|
||||
callbacksMu.Unlock()
|
||||
|
||||
if done != nil {
|
||||
close(done)
|
||||
}
|
||||
|
||||
if cam != nil {
|
||||
C.free(unsafe.Pointer(cam.name))
|
||||
C.freeCamera(cam)
|
||||
}
|
||||
|
||||
C.free(cbuf)
|
||||
|
||||
if ch != nil {
|
||||
close(ch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *camera) VideoRecord(p prop.Media) (video.Reader, error) {
|
||||
C.CoInitializeEx(nil, C.COINIT_MULTITHREADED)
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.cam == nil {
|
||||
return nil, fmt.Errorf("camera not open")
|
||||
}
|
||||
|
||||
nPix := p.Width * p.Height
|
||||
c.buf = make([]byte, nPix*2)
|
||||
c.bufGo = make([]byte, nPix*2)
|
||||
bufSize := nPix * 2
|
||||
c.cbuf = C.malloc(C.size_t(bufSize))
|
||||
if c.cbuf == nil {
|
||||
return nil, fmt.Errorf("failed to allocate frame buffer")
|
||||
}
|
||||
c.bufLen = bufSize
|
||||
c.bufGo = make([]byte, bufSize)
|
||||
c.cam.width = C.int(p.Width)
|
||||
c.cam.height = C.int(p.Height)
|
||||
|
||||
@@ -141,10 +199,12 @@ func (c *camera) VideoRecord(p prop.Media) (video.Reader, error) {
|
||||
c.cam.fcc = fourccYUY2
|
||||
}
|
||||
|
||||
c.cam.buf = C.size_t(uintptr(unsafe.Pointer(&c.buf[0])))
|
||||
c.cam.buf = C.size_t(uintptr(c.cbuf))
|
||||
|
||||
var errStr *C.char
|
||||
if C.openCamera(c.cam, &errStr) != 0 {
|
||||
C.free(c.cbuf)
|
||||
c.cbuf = nil
|
||||
return nil, fmt.Errorf("failed to open device: %s", C.GoString(errStr))
|
||||
}
|
||||
|
||||
@@ -184,6 +244,11 @@ func (c *camera) VideoRecord(p prop.Media) (video.Reader, error) {
|
||||
}
|
||||
|
||||
func (c *camera) Properties() []prop.Media {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.cam == nil {
|
||||
return nil
|
||||
}
|
||||
properties := []prop.Media{}
|
||||
for i := 0; i < int(c.cam.numProps); i++ {
|
||||
p := C.getProp(c.cam, C.int(i))
|
||||
|
||||
Reference in New Issue
Block a user