tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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