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