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 { 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 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 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 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 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 } 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 } 204 int32_t VideoCaptureDS::CaptureSettings( 205 VideoCaptureCapability& settings) 206 { 207 settings = _requestedCapability; 208 return 0; 209 } 210 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 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 } 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