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/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