blob: 4c2d52419ef5d39b03409a33b0e66daabd91c825 [file] [log] [blame]
andrew@webrtc.orga7b57da2012-10-22 18:19:23 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
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
11#include "video_capture_windows.h"
12
13#include "../video_capture_config.h"
14#include "critical_section_wrapper.h"
15#include "help_functions_windows.h"
16#include "sink_filter_windows.h"
17#include "trace.h"
18
19#include <Dvdmedia.h> // VIDEOINFOHEADER2
20
21namespace webrtc
22{
23namespace videocapturemodule
24{
25VideoCaptureDS::VideoCaptureDS(const WebRtc_Word32 id)
26 : VideoCaptureImpl(id), _dsInfo(id), _captureFilter(NULL),
27 _graphBuilder(NULL), _mediaControl(NULL), _sinkFilter(NULL),
28 _inputSendPin(NULL), _outputCapturePin(NULL), _dvFilter(NULL),
29 _inputDvPin(NULL), _outputDvPin(NULL)
30{
31}
32
33VideoCaptureDS::~VideoCaptureDS()
34{
35 if (_mediaControl)
36 {
37 _mediaControl->Stop();
38 }
39 if (_graphBuilder)
40 {
41 if (_sinkFilter)
42 _graphBuilder->RemoveFilter(_sinkFilter);
43 if (_captureFilter)
44 _graphBuilder->RemoveFilter(_captureFilter);
45 if (_dvFilter)
46 _graphBuilder->RemoveFilter(_dvFilter);
47 }
48 RELEASE_AND_CLEAR(_captureFilter); // release the capture device
49 RELEASE_AND_CLEAR(_sinkFilter);
50 RELEASE_AND_CLEAR(_dvFilter);
51
52 RELEASE_AND_CLEAR(_mediaControl);
53 RELEASE_AND_CLEAR(_inputSendPin);
54 RELEASE_AND_CLEAR(_outputCapturePin);
55
56 RELEASE_AND_CLEAR(_inputDvPin);
57 RELEASE_AND_CLEAR(_outputDvPin);
58
59 RELEASE_AND_CLEAR(_graphBuilder);
60}
61
62WebRtc_Word32 VideoCaptureDS::Init(const WebRtc_Word32 id,
63 const char* deviceUniqueIdUTF8)
64{
65 const WebRtc_Word32 nameLength =
66 (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8);
67 if (nameLength > kVideoCaptureUniqueNameLength)
68 return -1;
69
70 // Store the device name
71 _deviceUniqueId = new (std::nothrow) char[nameLength + 1];
72 memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
73
74 if (_dsInfo.Init() != 0)
75 return -1;
76
77 _captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8);
78 if (!_captureFilter)
79 {
80 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
81 "Failed to create capture filter.");
82 return -1;
83 }
84
85 // Get the interface for DirectShow's GraphBuilder
86 HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
87 CLSCTX_INPROC_SERVER, IID_IGraphBuilder,
88 (void **) &_graphBuilder);
89 if (FAILED(hr))
90 {
91 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
92 "Failed to create graph builder.");
93 return -1;
94 }
95
96 hr = _graphBuilder->QueryInterface(IID_IMediaControl,
97 (void **) &_mediaControl);
98 if (FAILED(hr))
99 {
100 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
101 "Failed to create media control builder.");
102 return -1;
103 }
104 hr = _graphBuilder->AddFilter(_captureFilter, CAPTURE_FILTER_NAME);
105 if (FAILED(hr))
106 {
107 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
108 "Failed to add the capture device to the graph.");
109 return -1;
110 }
111
112 _outputCapturePin = GetOutputPin(_captureFilter, PIN_CATEGORY_CAPTURE);
113
114 // Create the sink filte used for receiving Captured frames.
115 _sinkFilter = new CaptureSinkFilter(SINK_FILTER_NAME, NULL, &hr,
116 *this, _id);
117 if (hr != S_OK)
118 {
119 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
120 "Failed to create send filter");
121 return -1;
122 }
123 _sinkFilter->AddRef();
124
125 hr = _graphBuilder->AddFilter(_sinkFilter, SINK_FILTER_NAME);
126 if (FAILED(hr))
127 {
128 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
129 "Failed to add the send filter to the graph.");
130 return -1;
131 }
132 _inputSendPin = GetInputPin(_sinkFilter);
133
134 // Temporary connect here.
135 // This is done so that no one else can use the capture device.
136 if (SetCameraOutput(_requestedCapability) != 0)
137 {
138 return -1;
139 }
140 hr = _mediaControl->Pause();
141 if (FAILED(hr))
142 {
143 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
144 "Failed to Pause the Capture device. Is it already occupied? %d.",
145 hr);
146 return -1;
147 }
148 WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, _id,
149 "Capture device '%s' initialized.", deviceUniqueIdUTF8);
150 return 0;
151}
152
153WebRtc_Word32 VideoCaptureDS::StartCapture(
154 const VideoCaptureCapability& capability)
155{
156 CriticalSectionScoped cs(&_apiCs);
157
158 if (capability != _requestedCapability)
159 {
160 DisconnectGraph();
161
162 if (SetCameraOutput(capability) != 0)
163 {
164 return -1;
165 }
166 }
167 HRESULT hr = _mediaControl->Run();
168 if (FAILED(hr))
169 {
170 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
171 "Failed to start the Capture device.");
172 return -1;
173 }
174 return 0;
175}
176
177WebRtc_Word32 VideoCaptureDS::StopCapture()
178{
179 CriticalSectionScoped cs(&_apiCs);
180
181 HRESULT hr = _mediaControl->Pause();
182 if (FAILED(hr))
183 {
184 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
185 "Failed to stop the capture graph. %d", hr);
186 return -1;
187 }
188 return 0;
189}
190bool VideoCaptureDS::CaptureStarted()
191{
192 OAFilterState state = 0;
193 HRESULT hr = _mediaControl->GetState(1000, &state);
194 if (hr != S_OK && hr != VFW_S_CANT_CUE)
195 {
196 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
197 "Failed to get the CaptureStarted status");
198 }
199 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
200 "CaptureStarted %d", state);
201 return state == State_Running;
202
203}
204WebRtc_Word32 VideoCaptureDS::CaptureSettings(
205 VideoCaptureCapability& settings)
206{
207 settings = _requestedCapability;
208 return 0;
209}
210
211WebRtc_Word32 VideoCaptureDS::SetCameraOutput(
212 const VideoCaptureCapability& requestedCapability)
213{
214
215 // Get the best matching capability
216 VideoCaptureCapability capability;
217 WebRtc_Word32 capabilityIndex;
218
219 // Store the new requested size
220 _requestedCapability = requestedCapability;
221 // Match the requested capability with the supported.
222 if ((capabilityIndex = _dsInfo.GetBestMatchedCapability(_deviceUniqueId,
223 _requestedCapability,
224 capability)) < 0)
225 {
226 return -1;
227 }
228 //Reduce the frame rate if possible.
229 if (capability.maxFPS > requestedCapability.maxFPS)
230 {
231 capability.maxFPS = requestedCapability.maxFPS;
232 } else if (capability.maxFPS <= 0)
233 {
234 capability.maxFPS = 30;
235 }
236 // Store the new expected capture delay
237 _captureDelay = capability.expectedCaptureDelay;
238
239 // Convert it to the windows capability index since they are not nexessary
240 // the same
241 VideoCaptureCapabilityWindows windowsCapability;
242 if (_dsInfo.GetWindowsCapability(capabilityIndex, windowsCapability) != 0)
243 {
244 return -1;
245 }
246
247 IAMStreamConfig* streamConfig = NULL;
248 AM_MEDIA_TYPE *pmt = NULL;
249 VIDEO_STREAM_CONFIG_CAPS caps;
250
251 HRESULT hr = _outputCapturePin->QueryInterface(IID_IAMStreamConfig,
252 (void**) &streamConfig);
253 if (hr)
254 {
255 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
256 "Can't get the Capture format settings.");
257 return -1;
258 }
259
260 //Get the windows capability from the capture device
261 bool isDVCamera = false;
262 hr = streamConfig->GetStreamCaps(
263 windowsCapability.directShowCapabilityIndex,
264 &pmt, reinterpret_cast<BYTE*> (&caps));
265 if (!FAILED(hr))
266 {
267 if (pmt->formattype == FORMAT_VideoInfo2)
268 {
269 VIDEOINFOHEADER2* h =
270 reinterpret_cast<VIDEOINFOHEADER2*> (pmt->pbFormat);
271 if (capability.maxFPS > 0
272 && windowsCapability.supportFrameRateControl)
273 {
274 h->AvgTimePerFrame = REFERENCE_TIME(10000000.0
275 / capability.maxFPS);
276 }
277 }
278 else
279 {
280 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>
281 (pmt->pbFormat);
282 if (capability.maxFPS > 0
283 && windowsCapability.supportFrameRateControl)
284 {
285 h->AvgTimePerFrame = REFERENCE_TIME(10000000.0
286 / capability.maxFPS);
287 }
288
289 }
290
291 // Set the sink filter to request this capability
292 _sinkFilter->SetMatchingMediaType(capability);
293 //Order the capture device to use this capability
294 hr += streamConfig->SetFormat(pmt);
295
296 //Check if this is a DV camera and we need to add MS DV Filter
297 if (pmt->subtype == MEDIASUBTYPE_dvsl
298 || pmt->subtype == MEDIASUBTYPE_dvsd
299 || pmt->subtype == MEDIASUBTYPE_dvhd)
300 isDVCamera = true; // This is a DV camera. Use MS DV filter
301 }
302 RELEASE_AND_CLEAR(streamConfig);
303
304 if (FAILED(hr))
305 {
306 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
307 "Failed to set capture device output format");
308 return -1;
309 }
310
311 if (isDVCamera)
312 {
313 hr = ConnectDVCamera();
314 }
315 else
316 {
317 hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputSendPin,
318 NULL);
319 }
320 if (hr != S_OK)
321 {
322 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
323 "Failed to connect the Capture graph %d", hr);
324 return -1;
325 }
326 return 0;
327}
328
329WebRtc_Word32 VideoCaptureDS::DisconnectGraph()
330{
331 HRESULT hr = _mediaControl->Stop();
332 hr += _graphBuilder->Disconnect(_outputCapturePin);
333 hr += _graphBuilder->Disconnect(_inputSendPin);
334
335 //if the DV camera filter exist
336 if (_dvFilter)
337 {
338 _graphBuilder->Disconnect(_inputDvPin);
339 _graphBuilder->Disconnect(_outputDvPin);
340 }
341 if (hr != S_OK)
342 {
343 WEBRTC_TRACE( webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
344 "Failed to Stop the Capture device for reconfiguration %d",
345 hr);
346 return -1;
347 }
348 return 0;
349}
350HRESULT VideoCaptureDS::ConnectDVCamera()
351{
352 HRESULT hr = S_OK;
353
354 if (!_dvFilter)
355 {
356 hr = CoCreateInstance(CLSID_DVVideoCodec, NULL, CLSCTX_INPROC,
357 IID_IBaseFilter, (void **) &_dvFilter);
358 if (hr != S_OK)
359 {
360 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
361 "Failed to create the dv decoder: %x", hr);
362 return hr;
363 }
364 hr = _graphBuilder->AddFilter(_dvFilter, L"VideoDecoderDV");
365 if (hr != S_OK)
366 {
367 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
368 "Failed to add the dv decoder to the graph: %x", hr);
369 return hr;
370 }
371 _inputDvPin = GetInputPin(_dvFilter);
372 if (_inputDvPin == NULL)
373 {
374 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
375 "Failed to get input pin from DV decoder");
376 return -1;
377 }
378 _outputDvPin = GetOutputPin(_dvFilter);
379 if (_outputDvPin == NULL)
380 {
381 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
382 "Failed to get output pin from DV decoder");
383 return -1;
384 }
385 }
386 hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputDvPin, NULL);
387 if (hr != S_OK)
388 {
389 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
390 "Failed to connect capture device to the dv devoder: %x",
391 hr);
392 return hr;
393 }
394
395 hr = _graphBuilder->ConnectDirect(_outputDvPin, _inputSendPin, NULL);
396 if (hr != S_OK)
397 {
398 if (hr == 0x80070004)
399 {
400 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
401 "Failed to connect the capture device, busy");
402 }
403 else
404 {
405 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
406 "Failed to connect capture device to the send graph: 0x%x",
407 hr);
408 }
409 return hr;
410 }
411 return hr;
412}
413} // namespace videocapturemodule
414} //namespace webrtc