Home | History | Annotate | Download | only in windows
      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