1 /*
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 "modules/video_capture/windows/video_capture_ds.h"
12
13 #include <dvdmedia.h> // VIDEOINFOHEADER2
14
15 #include "modules/video_capture/video_capture_config.h"
16 #include "modules/video_capture/windows/help_functions_ds.h"
17 #include "modules/video_capture/windows/sink_filter_ds.h"
18 #include "rtc_base/logging.h"
19
20 namespace webrtc {
21 namespace videocapturemodule {
VideoCaptureDS()22 VideoCaptureDS::VideoCaptureDS()
23 : _captureFilter(NULL),
24 _graphBuilder(NULL),
25 _mediaControl(NULL),
26 _inputSendPin(NULL),
27 _outputCapturePin(NULL),
28 _dvFilter(NULL),
29 _inputDvPin(NULL),
30 _outputDvPin(NULL) {}
31
~VideoCaptureDS()32 VideoCaptureDS::~VideoCaptureDS() {
33 if (_mediaControl) {
34 _mediaControl->Stop();
35 }
36 if (_graphBuilder) {
37 if (sink_filter_)
38 _graphBuilder->RemoveFilter(sink_filter_);
39 if (_captureFilter)
40 _graphBuilder->RemoveFilter(_captureFilter);
41 if (_dvFilter)
42 _graphBuilder->RemoveFilter(_dvFilter);
43 }
44 RELEASE_AND_CLEAR(_inputSendPin);
45 RELEASE_AND_CLEAR(_outputCapturePin);
46
47 RELEASE_AND_CLEAR(_captureFilter); // release the capture device
48 RELEASE_AND_CLEAR(_dvFilter);
49
50 RELEASE_AND_CLEAR(_mediaControl);
51
52 RELEASE_AND_CLEAR(_inputDvPin);
53 RELEASE_AND_CLEAR(_outputDvPin);
54
55 RELEASE_AND_CLEAR(_graphBuilder);
56 }
57
Init(const char * deviceUniqueIdUTF8)58 int32_t VideoCaptureDS::Init(const char* deviceUniqueIdUTF8) {
59 const int32_t nameLength = (int32_t)strlen((char*)deviceUniqueIdUTF8);
60 if (nameLength > kVideoCaptureUniqueNameLength)
61 return -1;
62
63 // Store the device name
64 _deviceUniqueId = new (std::nothrow) char[nameLength + 1];
65 memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
66
67 if (_dsInfo.Init() != 0)
68 return -1;
69
70 _captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8);
71 if (!_captureFilter) {
72 RTC_LOG(LS_INFO) << "Failed to create capture filter.";
73 return -1;
74 }
75
76 // Get the interface for DirectShow's GraphBuilder
77 HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
78 IID_IGraphBuilder, (void**)&_graphBuilder);
79 if (FAILED(hr)) {
80 RTC_LOG(LS_INFO) << "Failed to create graph builder.";
81 return -1;
82 }
83
84 hr = _graphBuilder->QueryInterface(IID_IMediaControl, (void**)&_mediaControl);
85 if (FAILED(hr)) {
86 RTC_LOG(LS_INFO) << "Failed to create media control builder.";
87 return -1;
88 }
89 hr = _graphBuilder->AddFilter(_captureFilter, CAPTURE_FILTER_NAME);
90 if (FAILED(hr)) {
91 RTC_LOG(LS_INFO) << "Failed to add the capture device to the graph.";
92 return -1;
93 }
94
95 _outputCapturePin = GetOutputPin(_captureFilter, PIN_CATEGORY_CAPTURE);
96 if (!_outputCapturePin) {
97 RTC_LOG(LS_INFO) << "Failed to get output capture pin";
98 return -1;
99 }
100
101 // Create the sink filte used for receiving Captured frames.
102 sink_filter_ = new ComRefCount<CaptureSinkFilter>(this);
103
104 hr = _graphBuilder->AddFilter(sink_filter_, SINK_FILTER_NAME);
105 if (FAILED(hr)) {
106 RTC_LOG(LS_INFO) << "Failed to add the send filter to the graph.";
107 return -1;
108 }
109
110 _inputSendPin = GetInputPin(sink_filter_);
111 if (!_inputSendPin) {
112 RTC_LOG(LS_INFO) << "Failed to get input send pin";
113 return -1;
114 }
115
116 // Temporary connect here.
117 // This is done so that no one else can use the capture device.
118 if (SetCameraOutput(_requestedCapability) != 0) {
119 return -1;
120 }
121 hr = _mediaControl->Pause();
122 if (FAILED(hr)) {
123 RTC_LOG(LS_INFO)
124 << "Failed to Pause the Capture device. Is it already occupied? " << hr;
125 return -1;
126 }
127 RTC_LOG(LS_INFO) << "Capture device '" << deviceUniqueIdUTF8
128 << "' initialized.";
129 return 0;
130 }
131
StartCapture(const VideoCaptureCapability & capability)132 int32_t VideoCaptureDS::StartCapture(const VideoCaptureCapability& capability) {
133 MutexLock lock(&api_lock_);
134
135 if (capability != _requestedCapability) {
136 DisconnectGraph();
137
138 if (SetCameraOutput(capability) != 0) {
139 return -1;
140 }
141 }
142 HRESULT hr = _mediaControl->Run();
143 if (FAILED(hr)) {
144 RTC_LOG(LS_INFO) << "Failed to start the Capture device.";
145 return -1;
146 }
147 return 0;
148 }
149
StopCapture()150 int32_t VideoCaptureDS::StopCapture() {
151 MutexLock lock(&api_lock_);
152
153 HRESULT hr = _mediaControl->Pause();
154 if (FAILED(hr)) {
155 RTC_LOG(LS_INFO) << "Failed to stop the capture graph. " << hr;
156 return -1;
157 }
158 return 0;
159 }
160
CaptureStarted()161 bool VideoCaptureDS::CaptureStarted() {
162 OAFilterState state = 0;
163 HRESULT hr = _mediaControl->GetState(1000, &state);
164 if (hr != S_OK && hr != VFW_S_CANT_CUE) {
165 RTC_LOG(LS_INFO) << "Failed to get the CaptureStarted status";
166 }
167 RTC_LOG(LS_INFO) << "CaptureStarted " << state;
168 return state == State_Running;
169 }
170
CaptureSettings(VideoCaptureCapability & settings)171 int32_t VideoCaptureDS::CaptureSettings(VideoCaptureCapability& settings) {
172 settings = _requestedCapability;
173 return 0;
174 }
175
SetCameraOutput(const VideoCaptureCapability & requestedCapability)176 int32_t VideoCaptureDS::SetCameraOutput(
177 const VideoCaptureCapability& requestedCapability) {
178 // Get the best matching capability
179 VideoCaptureCapability capability;
180 int32_t capabilityIndex;
181
182 // Store the new requested size
183 _requestedCapability = requestedCapability;
184 // Match the requested capability with the supported.
185 if ((capabilityIndex = _dsInfo.GetBestMatchedCapability(
186 _deviceUniqueId, _requestedCapability, capability)) < 0) {
187 return -1;
188 }
189 // Reduce the frame rate if possible.
190 if (capability.maxFPS > requestedCapability.maxFPS) {
191 capability.maxFPS = requestedCapability.maxFPS;
192 } else if (capability.maxFPS <= 0) {
193 capability.maxFPS = 30;
194 }
195
196 // Convert it to the windows capability index since they are not nexessary
197 // the same
198 VideoCaptureCapabilityWindows windowsCapability;
199 if (_dsInfo.GetWindowsCapability(capabilityIndex, windowsCapability) != 0) {
200 return -1;
201 }
202
203 IAMStreamConfig* streamConfig = NULL;
204 AM_MEDIA_TYPE* pmt = NULL;
205 VIDEO_STREAM_CONFIG_CAPS caps;
206
207 HRESULT hr = _outputCapturePin->QueryInterface(IID_IAMStreamConfig,
208 (void**)&streamConfig);
209 if (hr) {
210 RTC_LOG(LS_INFO) << "Can't get the Capture format settings.";
211 return -1;
212 }
213
214 // Get the windows capability from the capture device
215 bool isDVCamera = false;
216 hr = streamConfig->GetStreamCaps(windowsCapability.directShowCapabilityIndex,
217 &pmt, reinterpret_cast<BYTE*>(&caps));
218 if (hr == S_OK) {
219 if (pmt->formattype == FORMAT_VideoInfo2) {
220 VIDEOINFOHEADER2* h = reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat);
221 if (capability.maxFPS > 0 && windowsCapability.supportFrameRateControl) {
222 h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 / capability.maxFPS);
223 }
224 } else {
225 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
226 if (capability.maxFPS > 0 && windowsCapability.supportFrameRateControl) {
227 h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 / capability.maxFPS);
228 }
229 }
230
231 // Set the sink filter to request this capability
232 sink_filter_->SetRequestedCapability(capability);
233 // Order the capture device to use this capability
234 hr += streamConfig->SetFormat(pmt);
235
236 // Check if this is a DV camera and we need to add MS DV Filter
237 if (pmt->subtype == MEDIASUBTYPE_dvsl ||
238 pmt->subtype == MEDIASUBTYPE_dvsd || pmt->subtype == MEDIASUBTYPE_dvhd)
239 isDVCamera = true; // This is a DV camera. Use MS DV filter
240 }
241 RELEASE_AND_CLEAR(streamConfig);
242
243 if (FAILED(hr)) {
244 RTC_LOG(LS_INFO) << "Failed to set capture device output format";
245 return -1;
246 }
247
248 if (isDVCamera) {
249 hr = ConnectDVCamera();
250 } else {
251 hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputSendPin, NULL);
252 }
253 if (hr != S_OK) {
254 RTC_LOG(LS_INFO) << "Failed to connect the Capture graph " << hr;
255 return -1;
256 }
257 return 0;
258 }
259
DisconnectGraph()260 int32_t VideoCaptureDS::DisconnectGraph() {
261 HRESULT hr = _mediaControl->Stop();
262 hr += _graphBuilder->Disconnect(_outputCapturePin);
263 hr += _graphBuilder->Disconnect(_inputSendPin);
264
265 // if the DV camera filter exist
266 if (_dvFilter) {
267 _graphBuilder->Disconnect(_inputDvPin);
268 _graphBuilder->Disconnect(_outputDvPin);
269 }
270 if (hr != S_OK) {
271 RTC_LOG(LS_ERROR)
272 << "Failed to Stop the Capture device for reconfiguration " << hr;
273 return -1;
274 }
275 return 0;
276 }
277
ConnectDVCamera()278 HRESULT VideoCaptureDS::ConnectDVCamera() {
279 HRESULT hr = S_OK;
280
281 if (!_dvFilter) {
282 hr = CoCreateInstance(CLSID_DVVideoCodec, NULL, CLSCTX_INPROC,
283 IID_IBaseFilter, (void**)&_dvFilter);
284 if (hr != S_OK) {
285 RTC_LOG(LS_INFO) << "Failed to create the dv decoder: " << hr;
286 return hr;
287 }
288 hr = _graphBuilder->AddFilter(_dvFilter, L"VideoDecoderDV");
289 if (hr != S_OK) {
290 RTC_LOG(LS_INFO) << "Failed to add the dv decoder to the graph: " << hr;
291 return hr;
292 }
293 _inputDvPin = GetInputPin(_dvFilter);
294 if (_inputDvPin == NULL) {
295 RTC_LOG(LS_INFO) << "Failed to get input pin from DV decoder";
296 return -1;
297 }
298 _outputDvPin = GetOutputPin(_dvFilter, GUID_NULL);
299 if (_outputDvPin == NULL) {
300 RTC_LOG(LS_INFO) << "Failed to get output pin from DV decoder";
301 return -1;
302 }
303 }
304 hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputDvPin, NULL);
305 if (hr != S_OK) {
306 RTC_LOG(LS_INFO) << "Failed to connect capture device to the dv devoder: "
307 << hr;
308 return hr;
309 }
310
311 hr = _graphBuilder->ConnectDirect(_outputDvPin, _inputSendPin, NULL);
312 if (hr != S_OK) {
313 if (hr == HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES)) {
314 RTC_LOG(LS_INFO) << "Failed to connect the capture device, busy";
315 } else {
316 RTC_LOG(LS_INFO) << "Failed to connect capture device to the send graph: "
317 << hr;
318 }
319 }
320 return hr;
321 }
322 } // namespace videocapturemodule
323 } // namespace webrtc
324