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 "webrtc/modules/video_capture/windows/video_capture_ds.h"
12 
13 #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"
16 #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
17 #include "webrtc/system_wrappers/include/trace.h"
18 
19 #include <Dvdmedia.h> // VIDEOINFOHEADER2
20 
21 namespace webrtc
22 {
23 namespace videocapturemodule
24 {
VideoCaptureDS(const int32_t id)25 VideoCaptureDS::VideoCaptureDS(const int32_t 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 
~VideoCaptureDS()33 VideoCaptureDS::~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(_inputSendPin);
49     RELEASE_AND_CLEAR(_outputCapturePin);
50 
51     RELEASE_AND_CLEAR(_captureFilter); // release the capture device
52     RELEASE_AND_CLEAR(_sinkFilter);
53     RELEASE_AND_CLEAR(_dvFilter);
54 
55     RELEASE_AND_CLEAR(_mediaControl);
56 
57     RELEASE_AND_CLEAR(_inputDvPin);
58     RELEASE_AND_CLEAR(_outputDvPin);
59 
60     RELEASE_AND_CLEAR(_graphBuilder);
61 }
62 
Init(const int32_t id,const char * deviceUniqueIdUTF8)63 int32_t VideoCaptureDS::Init(const int32_t id, const char* deviceUniqueIdUTF8)
64 {
65     const int32_t nameLength =
66         (int32_t) 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 
StartCapture(const VideoCaptureCapability & capability)153 int32_t 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 
StopCapture()177 int32_t 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 }
CaptureStarted()190 bool 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 }
CaptureSettings(VideoCaptureCapability & settings)204 int32_t VideoCaptureDS::CaptureSettings(
205                                              VideoCaptureCapability& settings)
206 {
207     settings = _requestedCapability;
208     return 0;
209 }
210 
SetCameraOutput(const VideoCaptureCapability & requestedCapability)211 int32_t VideoCaptureDS::SetCameraOutput(
212                              const VideoCaptureCapability& requestedCapability)
213 {
214 
215     // Get the best matching capability
216     VideoCaptureCapability capability;
217     int32_t 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 
DisconnectGraph()329 int32_t 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 }
ConnectDVCamera()350 HRESULT 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, GUID_NULL);
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
415