• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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