blob: 46747ba1574c091946a0fded3d3085cf1025d5fa [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
xians@webrtc.org843c8c72012-02-20 08:45:02 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000011#include "webrtc/modules/video_capture/windows/video_capture_ds.h"
perkj@webrtc.org0cc68dc2011-09-12 08:53:36 +000012
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000013#include "webrtc/modules/video_capture/video_capture_config.h"
14#include "webrtc/modules/video_capture/windows/help_functions_ds.h"
15#include "webrtc/modules/video_capture/windows/sink_filter_ds.h"
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020016#include "webrtc/rtc_base/logging.h"
perkj@webrtc.org0cc68dc2011-09-12 08:53:36 +000017
niklase@google.com470e71d2011-07-07 08:21:25 +000018#include <Dvdmedia.h> // VIDEOINFOHEADER2
19
20namespace webrtc
21{
22namespace videocapturemodule
23{
nisseb29b9c82016-12-12 00:22:56 -080024VideoCaptureDS::VideoCaptureDS()
25 : _captureFilter(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +000026 _graphBuilder(NULL), _mediaControl(NULL), _sinkFilter(NULL),
mflodman@webrtc.org8df26002012-02-24 10:06:30 +000027 _inputSendPin(NULL), _outputCapturePin(NULL), _dvFilter(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +000028 _inputDvPin(NULL), _outputDvPin(NULL)
29{
30}
31
32VideoCaptureDS::~VideoCaptureDS()
33{
34 if (_mediaControl)
35 {
36 _mediaControl->Stop();
37 }
38 if (_graphBuilder)
39 {
40 if (_sinkFilter)
41 _graphBuilder->RemoveFilter(_sinkFilter);
42 if (_captureFilter)
43 _graphBuilder->RemoveFilter(_captureFilter);
niklase@google.com470e71d2011-07-07 08:21:25 +000044 if (_dvFilter)
45 _graphBuilder->RemoveFilter(_dvFilter);
46 }
braveyao@webrtc.org651c05e2014-10-13 02:11:55 +000047 RELEASE_AND_CLEAR(_inputSendPin);
48 RELEASE_AND_CLEAR(_outputCapturePin);
49
niklase@google.com470e71d2011-07-07 08:21:25 +000050 RELEASE_AND_CLEAR(_captureFilter); // release the capture device
51 RELEASE_AND_CLEAR(_sinkFilter);
niklase@google.com470e71d2011-07-07 08:21:25 +000052 RELEASE_AND_CLEAR(_dvFilter);
53
54 RELEASE_AND_CLEAR(_mediaControl);
niklase@google.com470e71d2011-07-07 08:21:25 +000055
niklase@google.com470e71d2011-07-07 08:21:25 +000056 RELEASE_AND_CLEAR(_inputDvPin);
57 RELEASE_AND_CLEAR(_outputDvPin);
58
59 RELEASE_AND_CLEAR(_graphBuilder);
60}
61
nisseb29b9c82016-12-12 00:22:56 -080062int32_t VideoCaptureDS::Init(const char* deviceUniqueIdUTF8)
niklase@google.com470e71d2011-07-07 08:21:25 +000063{
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +000064 const int32_t nameLength =
65 (int32_t) strlen((char*) deviceUniqueIdUTF8);
niklase@google.com470e71d2011-07-07 08:21:25 +000066 if (nameLength > kVideoCaptureUniqueNameLength)
67 return -1;
68
69 // Store the device name
leozwang@webrtc.org4add6bc2012-03-01 19:57:13 +000070 _deviceUniqueId = new (std::nothrow) char[nameLength + 1];
niklase@google.com470e71d2011-07-07 08:21:25 +000071 memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
72
73 if (_dsInfo.Init() != 0)
74 return -1;
75
76 _captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8);
77 if (!_captureFilter)
78 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020079 LOG(LS_INFO) << "Failed to create capture filter.";
niklase@google.com470e71d2011-07-07 08:21:25 +000080 return -1;
81 }
82
83 // Get the interface for DirectShow's GraphBuilder
84 HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
85 CLSCTX_INPROC_SERVER, IID_IGraphBuilder,
86 (void **) &_graphBuilder);
87 if (FAILED(hr))
88 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020089 LOG(LS_INFO) << "Failed to create graph builder.";
niklase@google.com470e71d2011-07-07 08:21:25 +000090 return -1;
91 }
92
93 hr = _graphBuilder->QueryInterface(IID_IMediaControl,
94 (void **) &_mediaControl);
95 if (FAILED(hr))
96 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020097 LOG(LS_INFO) << "Failed to create media control builder.";
niklase@google.com470e71d2011-07-07 08:21:25 +000098 return -1;
99 }
100 hr = _graphBuilder->AddFilter(_captureFilter, CAPTURE_FILTER_NAME);
101 if (FAILED(hr))
102 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200103 LOG(LS_INFO) << "Failed to add the capture device to the graph.";
niklase@google.com470e71d2011-07-07 08:21:25 +0000104 return -1;
105 }
106
107 _outputCapturePin = GetOutputPin(_captureFilter, PIN_CATEGORY_CAPTURE);
108
109 // Create the sink filte used for receiving Captured frames.
110 _sinkFilter = new CaptureSinkFilter(SINK_FILTER_NAME, NULL, &hr,
nisseb29b9c82016-12-12 00:22:56 -0800111 *this);
niklase@google.com470e71d2011-07-07 08:21:25 +0000112 if (hr != S_OK)
113 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200114 LOG(LS_INFO) << "Failed to create send filter";
niklase@google.com470e71d2011-07-07 08:21:25 +0000115 return -1;
116 }
117 _sinkFilter->AddRef();
118
119 hr = _graphBuilder->AddFilter(_sinkFilter, SINK_FILTER_NAME);
120 if (FAILED(hr))
121 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200122 LOG(LS_INFO) << "Failed to add the send filter to the graph.";
niklase@google.com470e71d2011-07-07 08:21:25 +0000123 return -1;
124 }
125 _inputSendPin = GetInputPin(_sinkFilter);
126
niklase@google.com470e71d2011-07-07 08:21:25 +0000127 // Temporary connect here.
128 // This is done so that no one else can use the capture device.
129 if (SetCameraOutput(_requestedCapability) != 0)
130 {
131 return -1;
132 }
133 hr = _mediaControl->Pause();
134 if (FAILED(hr))
135 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200136 LOG(LS_INFO)
137 << "Failed to Pause the Capture device. Is it already occupied? "
138 << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000139 return -1;
140 }
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200141 LOG(LS_INFO) << "Capture device '" << deviceUniqueIdUTF8
142 << "' initialized.";
niklase@google.com470e71d2011-07-07 08:21:25 +0000143 return 0;
144}
145
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000146int32_t VideoCaptureDS::StartCapture(
niklase@google.com470e71d2011-07-07 08:21:25 +0000147 const VideoCaptureCapability& capability)
148{
kthelgasonff046c72017-03-31 02:03:55 -0700149 rtc::CritScope cs(&_apiCs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000150
151 if (capability != _requestedCapability)
152 {
153 DisconnectGraph();
154
155 if (SetCameraOutput(capability) != 0)
156 {
157 return -1;
158 }
159 }
160 HRESULT hr = _mediaControl->Run();
161 if (FAILED(hr))
162 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200163 LOG(LS_INFO) << "Failed to start the Capture device.";
niklase@google.com470e71d2011-07-07 08:21:25 +0000164 return -1;
165 }
166 return 0;
167}
168
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000169int32_t VideoCaptureDS::StopCapture()
niklase@google.com470e71d2011-07-07 08:21:25 +0000170{
kthelgasonff046c72017-03-31 02:03:55 -0700171 rtc::CritScope cs(&_apiCs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000172
173 HRESULT hr = _mediaControl->Pause();
174 if (FAILED(hr))
175 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200176 LOG(LS_INFO) << "Failed to stop the capture graph. " << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000177 return -1;
178 }
179 return 0;
180}
181bool VideoCaptureDS::CaptureStarted()
182{
183 OAFilterState state = 0;
184 HRESULT hr = _mediaControl->GetState(1000, &state);
185 if (hr != S_OK && hr != VFW_S_CANT_CUE)
186 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200187 LOG(LS_INFO) << "Failed to get the CaptureStarted status";
niklase@google.com470e71d2011-07-07 08:21:25 +0000188 }
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200189 LOG(LS_INFO) << "CaptureStarted " << state;
niklase@google.com470e71d2011-07-07 08:21:25 +0000190 return state == State_Running;
191
192}
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000193int32_t VideoCaptureDS::CaptureSettings(
niklase@google.com470e71d2011-07-07 08:21:25 +0000194 VideoCaptureCapability& settings)
195{
196 settings = _requestedCapability;
197 return 0;
198}
199
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000200int32_t VideoCaptureDS::SetCameraOutput(
niklase@google.com470e71d2011-07-07 08:21:25 +0000201 const VideoCaptureCapability& requestedCapability)
202{
203
204 // Get the best matching capability
205 VideoCaptureCapability capability;
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000206 int32_t capabilityIndex;
niklase@google.com470e71d2011-07-07 08:21:25 +0000207
208 // Store the new requested size
209 _requestedCapability = requestedCapability;
210 // Match the requested capability with the supported.
211 if ((capabilityIndex = _dsInfo.GetBestMatchedCapability(_deviceUniqueId,
212 _requestedCapability,
213 capability)) < 0)
214 {
215 return -1;
216 }
217 //Reduce the frame rate if possible.
218 if (capability.maxFPS > requestedCapability.maxFPS)
219 {
220 capability.maxFPS = requestedCapability.maxFPS;
braveyao@webrtc.orgad69ca72012-07-25 03:02:15 +0000221 } else if (capability.maxFPS <= 0)
222 {
223 capability.maxFPS = 30;
niklase@google.com470e71d2011-07-07 08:21:25 +0000224 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000225
226 // Convert it to the windows capability index since they are not nexessary
227 // the same
228 VideoCaptureCapabilityWindows windowsCapability;
229 if (_dsInfo.GetWindowsCapability(capabilityIndex, windowsCapability) != 0)
230 {
231 return -1;
232 }
233
234 IAMStreamConfig* streamConfig = NULL;
235 AM_MEDIA_TYPE *pmt = NULL;
236 VIDEO_STREAM_CONFIG_CAPS caps;
237
238 HRESULT hr = _outputCapturePin->QueryInterface(IID_IAMStreamConfig,
239 (void**) &streamConfig);
240 if (hr)
241 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200242 LOG(LS_INFO) << "Can't get the Capture format settings.";
niklase@google.com470e71d2011-07-07 08:21:25 +0000243 return -1;
244 }
245
246 //Get the windows capability from the capture device
247 bool isDVCamera = false;
248 hr = streamConfig->GetStreamCaps(
249 windowsCapability.directShowCapabilityIndex,
250 &pmt, reinterpret_cast<BYTE*> (&caps));
251 if (!FAILED(hr))
252 {
253 if (pmt->formattype == FORMAT_VideoInfo2)
254 {
255 VIDEOINFOHEADER2* h =
256 reinterpret_cast<VIDEOINFOHEADER2*> (pmt->pbFormat);
257 if (capability.maxFPS > 0
258 && windowsCapability.supportFrameRateControl)
259 {
260 h->AvgTimePerFrame = REFERENCE_TIME(10000000.0
261 / capability.maxFPS);
262 }
263 }
264 else
265 {
266 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>
267 (pmt->pbFormat);
268 if (capability.maxFPS > 0
269 && windowsCapability.supportFrameRateControl)
270 {
271 h->AvgTimePerFrame = REFERENCE_TIME(10000000.0
272 / capability.maxFPS);
273 }
274
275 }
276
277 // Set the sink filter to request this capability
278 _sinkFilter->SetMatchingMediaType(capability);
279 //Order the capture device to use this capability
280 hr += streamConfig->SetFormat(pmt);
281
282 //Check if this is a DV camera and we need to add MS DV Filter
283 if (pmt->subtype == MEDIASUBTYPE_dvsl
284 || pmt->subtype == MEDIASUBTYPE_dvsd
285 || pmt->subtype == MEDIASUBTYPE_dvhd)
286 isDVCamera = true; // This is a DV camera. Use MS DV filter
287 }
288 RELEASE_AND_CLEAR(streamConfig);
289
290 if (FAILED(hr))
291 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200292 LOG(LS_INFO) << "Failed to set capture device output format";
niklase@google.com470e71d2011-07-07 08:21:25 +0000293 return -1;
294 }
295
mflodman@webrtc.org8df26002012-02-24 10:06:30 +0000296 if (isDVCamera)
niklase@google.com470e71d2011-07-07 08:21:25 +0000297 {
298 hr = ConnectDVCamera();
299 }
300 else
301 {
302 hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputSendPin,
303 NULL);
304 }
305 if (hr != S_OK)
306 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200307 LOG(LS_INFO) << "Failed to connect the Capture graph " << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000308 return -1;
309 }
310 return 0;
311}
312
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000313int32_t VideoCaptureDS::DisconnectGraph()
niklase@google.com470e71d2011-07-07 08:21:25 +0000314{
315 HRESULT hr = _mediaControl->Stop();
316 hr += _graphBuilder->Disconnect(_outputCapturePin);
317 hr += _graphBuilder->Disconnect(_inputSendPin);
318
niklase@google.com470e71d2011-07-07 08:21:25 +0000319 //if the DV camera filter exist
320 if (_dvFilter)
321 {
322 _graphBuilder->Disconnect(_inputDvPin);
323 _graphBuilder->Disconnect(_outputDvPin);
324 }
325 if (hr != S_OK)
326 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200327 LOG(LS_ERROR)
328 << "Failed to Stop the Capture device for reconfiguration "
329 << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000330 return -1;
331 }
332 return 0;
333}
334HRESULT VideoCaptureDS::ConnectDVCamera()
335{
336 HRESULT hr = S_OK;
337
338 if (!_dvFilter)
339 {
340 hr = CoCreateInstance(CLSID_DVVideoCodec, NULL, CLSCTX_INPROC,
341 IID_IBaseFilter, (void **) &_dvFilter);
342 if (hr != S_OK)
343 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200344 LOG(LS_INFO) << "Failed to create the dv decoder: " << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000345 return hr;
346 }
347 hr = _graphBuilder->AddFilter(_dvFilter, L"VideoDecoderDV");
348 if (hr != S_OK)
349 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200350 LOG(LS_INFO) << "Failed to add the dv decoder to the graph: " << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000351 return hr;
352 }
353 _inputDvPin = GetInputPin(_dvFilter);
354 if (_inputDvPin == NULL)
355 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200356 LOG(LS_INFO) << "Failed to get input pin from DV decoder";
niklase@google.com470e71d2011-07-07 08:21:25 +0000357 return -1;
358 }
tommi@webrtc.org81878772012-11-20 13:35:33 +0000359 _outputDvPin = GetOutputPin(_dvFilter, GUID_NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000360 if (_outputDvPin == NULL)
361 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200362 LOG(LS_INFO) << "Failed to get output pin from DV decoder";
niklase@google.com470e71d2011-07-07 08:21:25 +0000363 return -1;
364 }
365 }
366 hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputDvPin, NULL);
367 if (hr != S_OK)
368 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200369 LOG(LS_INFO) << "Failed to connect capture device to the dv devoder: "
370 << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000371 return hr;
372 }
373
374 hr = _graphBuilder->ConnectDirect(_outputDvPin, _inputSendPin, NULL);
375 if (hr != S_OK)
376 {
kjellander73674f82016-01-19 05:49:18 -0800377 if (hr == HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES))
niklase@google.com470e71d2011-07-07 08:21:25 +0000378 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200379 LOG(LS_INFO) << "Failed to connect the capture device, busy";
niklase@google.com470e71d2011-07-07 08:21:25 +0000380 }
381 else
382 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200383 LOG(LS_INFO)
384 << "Failed to connect capture device to the send graph: "
385 << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000386 }
387 return hr;
388 }
389 return hr;
390}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000391} // namespace videocapturemodule
392} // namespace webrtc