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