Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions pkg/driver/camera/camera_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include <unistd.h>

#include <dshow.h>
#include <amvideo.h>
#include <dvdmedia.h>
#include <qedit.h>
#include <mmsystem.h>

Expand Down Expand Up @@ -119,7 +121,7 @@ int freeCameraList(cameraList* list, const char** errstr)
}
delete list->name;
}
return 1;
return 0;
}

// selectCamera stores pointer to the selected device IMoniker* according to the configs in camera*.
Expand Down Expand Up @@ -175,7 +177,8 @@ int selectCamera(camera* cam, IMoniker** monikerSelected, const char** errstr)
fail:
safeRelease(&sysDevEnum);
safeRelease(&enumMon);
return 1;
// Signal failure. Callers treat non-zero as success.
return 0;
}

// listResolution stores list of the device to camera*.
Expand All @@ -195,7 +198,17 @@ int listResolution(camera* cam, const char** errstr)
goto fail;
}

moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter);
if (moniker == nullptr)
{
*errstr = errEnumDevice;
goto fail;
}

if (FAILED(moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter)))
{
*errstr = errEnumDevice;
goto fail;
}
safeRelease(&moniker);

src = getPin(captureFilter, PINDIR_OUTPUT);
Expand Down Expand Up @@ -232,14 +245,40 @@ int listResolution(camera* cam, const char** errstr)
continue;

if (mediaType->majortype != MEDIATYPE_Video ||
mediaType->formattype != FORMAT_VideoInfo ||
mediaType->pbFormat == nullptr)
continue;

VIDEOINFOHEADER* videoInfoHdr = (VIDEOINFOHEADER*)mediaType->pbFormat;
cam->props[iProp].width = videoInfoHdr->bmiHeader.biWidth;
cam->props[iProp].height = videoInfoHdr->bmiHeader.biHeight;
cam->props[iProp].fcc = videoInfoHdr->bmiHeader.biCompression;
int width = 0;
int height = 0;
uint32_t fcc = 0;
if (mediaType->formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER* videoInfoHdr = (VIDEOINFOHEADER*)mediaType->pbFormat;
width = videoInfoHdr->bmiHeader.biWidth;
height = videoInfoHdr->bmiHeader.biHeight;
fcc = (uint32_t)videoInfoHdr->bmiHeader.biCompression;
}
else if (mediaType->formattype == FORMAT_VideoInfo2)
{
VIDEOINFOHEADER2* videoInfoHdr = (VIDEOINFOHEADER2*)mediaType->pbFormat;
width = videoInfoHdr->bmiHeader.biWidth;
height = videoInfoHdr->bmiHeader.biHeight;
fcc = (uint32_t)videoInfoHdr->bmiHeader.biCompression;
}
else
{
continue;
}

// biHeight can be negative (top-down). Normalize to a positive size.
if (width < 0)
width = -width;
if (height < 0)
height = -height;

cam->props[iProp].width = width;
cam->props[iProp].height = height;
cam->props[iProp].fcc = fcc;
iProp++;
}
cam->numProps = iProp;
Expand Down Expand Up @@ -283,7 +322,18 @@ int openCamera(camera* cam, const char** errstr)
{
goto fail;
}
moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter);

if (moniker == nullptr)
{
*errstr = errEnumDevice;
goto fail;
}

if (FAILED(moniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter)))
{
*errstr = errEnumDevice;
goto fail;
}
safeRelease(&moniker);

if (FAILED(CoCreateInstance(
Expand Down
61 changes: 41 additions & 20 deletions pkg/driver/camera/camera_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"image"
"io"
"sync"
"sync/atomic"
"unsafe"

"github.com/pion/mediadevices/pkg/driver"
Expand All @@ -25,11 +26,12 @@ var (
)

type camera struct {
name string
cam *C.camera
ch chan []byte
buf []byte
bufGo []byte
name string
cam *C.camera
ch chan []byte
buf []byte
bufGo []byte
closed atomic.Bool
}

func init() {
Expand Down Expand Up @@ -75,6 +77,7 @@ func DestroyObserver() error {
}

func (c *camera) Open() error {
c.closed.Store(false)
c.ch = make(chan []byte)
c.cam = &C.camera{
name: C.CString(c.name),
Expand All @@ -96,19 +99,35 @@ func imageCallback(cam uintptr) {
if !ok {
return
}
if cb.closed.Load() {
return
}

copy(cb.bufGo, cb.buf)
cb.ch <- cb.bufGo
// The native capture graph may deliver frames concurrently with teardown.
// Avoid panicking on send-to-closed-channel, and avoid blocking the callback.
defer func() {
_ = recover()
}()
select {
case cb.ch <- cb.bufGo:
default:
}
}

func (c *camera) Close() error {
c.closed.Store(true)

callbacksMu.Lock()
key := uintptr(unsafe.Pointer(c.cam))
if _, ok := callbacks[key]; ok {
delete(callbacks, key)
}
callbacksMu.Unlock()
close(c.ch)
if c.ch != nil {
close(c.ch)
c.ch = nil
}

if c.cam != nil {
C.free(unsafe.Pointer(c.cam.name))
Expand Down Expand Up @@ -158,20 +177,22 @@ func (c *camera) Properties() []prop.Media {
properties := []prop.Media{}
for i := 0; i < int(c.cam.numProps); i++ {
p := C.getProp(c.cam, C.int(i))
// TODO: support other FOURCC
if p.fcc == fourccYUY2 {
properties = append(properties, prop.Media{
Video: prop.Video{
Width: int(p.width),
Height: int(p.height),
FrameFormat: frame.FormatYUY2,
},
})
w := int(p.width)
h := int(p.height)
if w <= 0 || h <= 0 {
continue
}
// We request YUY2 from the DirectShow SampleGrabber.
// Even if the camera's native caps are MJPG/etc, DirectShow can often insert
// a decoder/colorspace converter so the grabber still receives YUY2.
properties = append(properties, prop.Media{
Video: prop.Video{
Width: w,
Height: h,
FrameFormat: frame.FormatYUY2,
},
})
}

return properties
}

const (
fourccYUY2 = 0x32595559
)
Loading