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