windows 摄像头采集方案
现在市场上大多数的摄像头和采集卡在Windows系统上的驱动基于WDM架构,微软定义了采集卡设备与上层程序间的通信驱动接口,这已经成为一种标准,因此,控制摄像头和采集图像基本都通过Directshow框架来实现。
webrtc 摄像头采集
由上面的介绍,我们知道在windows 几乎都dshow,那webrtc 摄像头采集也是采用的dshow
摄像头采集的模块
类图 ,VideoCaptureDS 是采集的实现类,DS 也就是DirectShow 的简写。
接下来,我们看一下VideoCaptureDS 具体实现的几个函数,想看懂代码,需要先对dshow 有所了解。可以先看下我另一篇文章windows 摄像头采集
1、根据名称获得 caputre_filter
2、创建 graph
3、通过graph 获得 media control ,用来控制采集
4、添加 caputre_filter 都添加到 graph 中,_outputCapturePin 采集pin
5、创建 CaptureSinkFilter,添加到 graph 中
6、链接 输出 和 sink 的输入
int32_t VideoCaptureDS::Init(const char* deviceUniqueIdUTF8) {
const int32_t nameLength = (int32_t)strlen((char*)deviceUniqueIdUTF8);
if (nameLength > kVideoCaptureUniqueNameLength)
return -1;
// Store the device name
_deviceUniqueId = new (std::nothrow) char[nameLength + 1];
memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
if (_dsInfo.Init() != 0)
return -1;
_captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8);
if (!_captureFilter) {
RTC_LOG(LS_INFO) << "Failed to create capture filter.";
return -1;
}
// Get the interface for DirectShow's GraphBuilder
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void**)&_graphBuilder);
if (FAILED(hr)) {
RTC_LOG(LS_INFO) << "Failed to create graph builder.";
return -1;
}
hr = _graphBuilder->QueryInterface(IID_IMediaControl, (void**)&_mediaControl);
if (FAILED(hr)) {
RTC_LOG(LS_INFO) << "Failed to create media control builder.";
return -1;
}
hr = _graphBuilder->AddFilter(_captureFilter, CAPTURE_FILTER_NAME);
if (FAILED(hr)) {
RTC_LOG(LS_INFO) << "Failed to add the capture device to the graph.";
return -1;
}
_outputCapturePin = GetOutputPin(_captureFilter, PIN_CATEGORY_CAPTURE);
if (!_outputCapturePin) {
RTC_LOG(LS_INFO) << "Failed to get output capture pin";
return -1;
}
// Create the sink filte used for receiving Captured frames.
sink_filter_ = new ComRefCount<CaptureSinkFilter>(this);
hr = _graphBuilder->AddFilter(sink_filter_, SINK_FILTER_NAME);
if (FAILED(hr)) {
RTC_LOG(LS_INFO) << "Failed to add the send filter to the graph.";
return -1;
}
_inputSendPin = GetInputPin(sink_filter_);
if (!_inputSendPin) {
RTC_LOG(LS_INFO) << "Failed to get input send pin";
return -1;
}
// Temporary connect here.
// This is done so that no one else can use the capture device.
if (SetCameraOutput(_requestedCapability) != 0) {
return -1;
}
hr = _mediaControl->Pause();
if (FAILED(hr)) {
RTC_LOG(LS_INFO)
<< "Failed to Pause the Capture device. Is it already occupied? " << hr;
return -1;
}
RTC_LOG(LS_INFO) << "Capture device '" << deviceUniqueIdUTF8
<< "' initialized.";
return 0;
}
链接 cpature output pin 和 sink input pin
int32_t VideoCaptureDS::SetCameraOutput(
const VideoCaptureCapability& requestedCapability) {
// Get the best matching capability
VideoCaptureCapability capability;
int32_t capabilityIndex;
// Store the new requested size
_requestedCapability = requestedCapability;
// Match the requested capability with the supported.
if ((capabilityIndex = _dsInfo.GetBestMatchedCapability(
_deviceUniqueId, _requestedCapability, capability)) < 0) {
return -1;
}
// Reduce the frame rate if possible.
if (capability.maxFPS > requestedCapability.maxFPS) {
capability.maxFPS = requestedCapability.maxFPS;
} else if (capability.maxFPS <= 0) {
capability.maxFPS = 30;
}
// Convert it to the windows capability index since they are not nexessary
// the same
VideoCaptureCapabilityWindows windowsCapability;
if (_dsInfo.GetWindowsCapability(capabilityIndex, windowsCapability) != 0) {
return -1;
}
IAMStreamConfig* streamConfig = NULL;
AM_MEDIA_TYPE* pmt = NULL;
VIDEO_STREAM_CONFIG_CAPS caps;
HRESULT hr = _outputCapturePin->QueryInterface(IID_IAMStreamConfig,
(void**)&streamConfig);
if (hr) {
RTC_LOG(LS_INFO) << "Can't get the Capture format settings.";
return -1;
}
// Get the windows capability from the capture device
bool isDVCamera = false;
hr = streamConfig->GetStreamCaps(windowsCapability.directShowCapabilityIndex,
&pmt, reinterpret_cast<BYTE*>(&caps));
if (hr == S_OK) {
if (pmt->formattype == FORMAT_VideoInfo2) {
VIDEOINFOHEADER2* h = reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat);
if (capability.maxFPS > 0 && windowsCapability.supportFrameRateControl) {
h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 / capability.maxFPS);
}
} else {
VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
if (capability.maxFPS > 0 && windowsCapability.supportFrameRateControl) {
h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 / capability.maxFPS);
}
}
// Set the sink filter to request this capability
sink_filter_->SetRequestedCapability(capability);
// Order the capture device to use this capability
hr += streamConfig->SetFormat(pmt);
// Check if this is a DV camera and we need to add MS DV Filter
if (pmt->subtype == MEDIASUBTYPE_dvsl ||
pmt->subtype == MEDIASUBTYPE_dvsd || pmt->subtype == MEDIASUBTYPE_dvhd)
isDVCamera = true; // This is a DV camera. Use MS DV filter
}
RELEASE_AND_CLEAR(streamConfig);
if (FAILED(hr)) {
RTC_LOG(LS_INFO) << "Failed to set capture device output format";
return -1;
}
if (isDVCamera) {
hr = ConnectDVCamera();
} else {
hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputSendPin, NULL);
}
if (hr != S_OK) {
RTC_LOG(LS_INFO) << "Failed to connect the Capture graph " << hr;
return -1;
}
return 0;
}
启动采集
int32_t VideoCaptureDS::StartCapture(const VideoCaptureCapability& capability) {
rtc::CritScope cs(&_apiCs);
HRESULT hr = _mediaControl->Run();
if (FAILED(hr)) {
RTC_LOG(LS_INFO) << "Failed to start the Capture device.";
return -1;
}
return 0;
}
数据流
获取数据的流程,是蓝色部分,代码不复杂,重点需要关注下VcmCapturer,他既是消费者,又是source。
webrtc 摄像头的使用
创建CapturerTrackSource 作为source 也就上图右下角的位置的,作为摄像头最上层的一个封装,是一个source , 然后封装为videotrack。添加Sink 进行消费。 如果想了解 track 和 source 的实现,可参考我另一篇文章。
rtc::scoped_refptr<CapturerTrackSource> video_device =
CapturerTrackSource::Create();
if (video_device) {
rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track_(
peer_connection_factory_->CreateVideoTrack(kVideoLabel, video_device));
main_wnd_->StartLocalRenderer(video_track_);
result_or_error = peer_connection_->AddTrack(video_track_, {kStreamId});
if (!result_or_error.ok()) {
RTC_LOG(LS_ERROR) << "Failed to add video track to PeerConnection: "
<< result_or_error.error().message();
}
} else {
RTC_LOG(LS_ERROR) << "OpenVideoCaptureDevice failed";
}