Ring Daemon 16.0.0
Loading...
Searching...
No Matches
winvideo/video_device_impl.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include <algorithm>
19#include <cassert>
20#include <climits>
21#include <map>
22#include <string>
23#include <sstream>
24#include <stdexcept>
25#include <string>
26#include <vector>
27#include <memory>
28
29#include "logger.h"
30#include "../video_device.h"
32
33#include <dshow.h>
34
35namespace jami {
36namespace video {
37
38class VideoDeviceImpl
39{
40public:
44 VideoDeviceImpl(const std::string& id);
45 std::string id;
46 std::string name;
47
48 std::vector<std::string> getChannelList() const;
49 std::vector<VideoSize> getSizeList(const std::string& channel) const;
50 std::vector<VideoSize> getSizeList() const;
51 std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
52
55
56private:
57 std::unique_ptr<CaptureGraphInterfaces> cInterface;
58
59 void setup();
60 std::vector<VideoSize> sizeList_;
61 std::map<VideoSize, std::vector<FrameRate>> rateList_;
62 std::map<VideoSize, AM_MEDIA_TYPE*> capMap_;
63 FrameRate desktopFrameRate_ = {30};
64
65 void fail(const std::string& error);
66};
67
68VideoDeviceImpl::VideoDeviceImpl(const std::string& id)
69 : id(id)
70 , name()
71 , cInterface(new CaptureGraphInterfaces())
72{
73 setup();
74}
75
76void
77VideoDeviceImpl::setup()
78{
79 if (id == DEVICE_DESKTOP) {
81 VideoSize size {0, 0};
82 sizeList_.emplace_back(size);
83 rateList_[size] = {FrameRate(1),
84 FrameRate(5),
85 FrameRate(10),
86 FrameRate(15),
87 FrameRate(20),
88 FrameRate(25),
89 FrameRate(30),
90 FrameRate(60),
91 FrameRate(120),
92 FrameRate(144)};
93 return;
94 }
96 nullptr,
99 (void**) &cInterface->captureGraph_);
100 if (FAILED(hr))
101 return fail("Unable to create the Filter Graph Manager");
102
104 nullptr,
107 (void**) &cInterface->graph_);
108 if (FAILED(hr))
109 return fail("Unable to add the graph builder!");
110
111 hr = cInterface->captureGraph_->SetFiltergraph(cInterface->graph_);
112 if (FAILED(hr))
113 return fail("Unable to set filtergraph.");
114
117 NULL,
120
122 hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
123 if (hr == S_FALSE) {
125 }
126 pDevEnum->Release();
127 if (FAILED(hr) || pEnum == nullptr) {
128 JAMI_ERR() << "No webcam found";
129 return;
130 }
131
132 // Auto-deletion at exception
133 auto IEnumMonikerDeleter = [](IEnumMoniker* p) {
134 p->Release();
135 };
136 std::unique_ptr<IEnumMoniker, decltype(IEnumMonikerDeleter)&> pEnumGuard {pEnum,
138
140 while ((pEnumGuard->Next(1, &pMoniker, NULL) == S_OK)) {
142 hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**) &pPropBag);
143 if (FAILED(hr)) {
144 pMoniker->Release();
145 continue;
146 }
147
150
152 if (hr != S_OK) {
153 continue;
154 }
155 hr = pMoniker->GetDisplayName(bind_ctx, NULL, &olestr);
156 if (hr != S_OK) {
157 continue;
158 }
160 if (unique_name.empty()) {
161 continue;
162 }
163
164 // replace ':' with '_' since ffmpeg uses : to delineate between sources */
165 std::replace(unique_name.begin(), unique_name.end(), ':', '_');
166 unique_name = std::string("video=") + unique_name;
167
168 // We want to get the capabilities of a device with the unique_name
169 // that corresponds to what was enumerated by the video device monitor.
170 if (unique_name.find(this->id) == std::string::npos) {
171 continue;
172 }
173
174 this->id = unique_name;
175
176 // get friendly name
177 VARIANT var;
178 VariantInit(&var);
179 hr = pPropBag->Read(L"Description", &var, 0);
180 if (FAILED(hr)) {
181 hr = pPropBag->Read(L"FriendlyName", &var, 0);
182 }
183 if (SUCCEEDED(hr)) {
184 this->name = to_string(var.bstrVal);
185 }
186 pPropBag->Write(L"FriendlyName", &var);
187
188 hr = pMoniker->BindToObject(nullptr,
189 nullptr,
191 (void**) &cInterface->videoInputFilter_);
192 if (SUCCEEDED(hr))
193 hr = cInterface->graph_->AddFilter(cInterface->videoInputFilter_, var.bstrVal);
194 else {
195 fail("Unable to add filter to video device.");
196 }
197 hr = cInterface->captureGraph_->FindInterface(&PIN_CATEGORY_PREVIEW,
199 cInterface->videoInputFilter_,
201 (void**) &cInterface->streamConf_);
202 if (FAILED(hr)) {
203 hr = cInterface->captureGraph_->FindInterface(&PIN_CATEGORY_CAPTURE,
205 cInterface->videoInputFilter_,
207 (void**) &cInterface->streamConf_);
208 if (FAILED(hr)) {
209 fail("Unable to config the stream!");
210 }
211 }
212
214 pPropBag->Release();
215
216 // Device found.
217 break;
218 }
219 pMoniker->Release();
220
221 if (FAILED(hr) || cInterface->streamConf_ == NULL) {
222 fail("Unable to find the video device.");
223 }
224
225 int piCount;
226 int piSize;
227 cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize);
230 std::map<std::pair<jami::video::VideoSize, jami::video::FrameRate>, LONG> bitrateList;
231 for (int i = 0; i < piCount; i++) {
232 cInterface->streamConf_->GetStreamCaps(i, &pmt, (BYTE*) &pSCC);
233 if (pmt->formattype != FORMAT_VideoInfo) {
234 continue;
235 }
236 auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
237 auto size = jami::video::VideoSize(videoInfo->bmiHeader.biWidth,
238 videoInfo->bmiHeader.biHeight);
239 // use 1e7 / MinFrameInterval to get maximum fps
240 auto rate = jami::video::FrameRate(1e7, pSCC.MinFrameInterval);
241 auto bitrate = videoInfo->dwBitRate;
242 // Only add configurations with positive bitrates.
243 if (bitrate == 0)
244 continue;
245 // Avoid adding multiple rates with different bitrates.
246 auto ratesIt = rateList_.find(size);
247 if (ratesIt != rateList_.end()
248 && std::find(ratesIt->second.begin(), ratesIt->second.end(), rate)
249 != ratesIt->second.end()) {
250 // Update bitrate and cap map if the bitrate is greater.
251 auto key = std::make_pair(size, rate);
252 if (bitrate > bitrateList[key]) {
253 bitrateList[key] = bitrate;
254 capMap_[size] = pmt;
255 }
256 continue;
257 }
258 // Add new size, rate, bitrate, and cap map.
259 sizeList_.emplace_back(size);
260 rateList_[size].emplace_back(rate);
261 bitrateList[std::make_pair(size, rate)] = bitrate;
262 capMap_[size] = pmt;
263 }
264}
265
266void
267VideoDeviceImpl::fail(const std::string& error)
268{
269 throw std::runtime_error(error);
270}
271
272DeviceParams
274{
275 DeviceParams params;
276
277 params.name = name;
278 params.unique_id = id;
279 params.input = id;
280 if (id == DEVICE_DESKTOP) {
281 params.format = "dxgigrab";
282 params.framerate = desktopFrameRate_;
283 return params;
284 }
285
286 params.format = "dshow";
287
289 HRESULT hr = cInterface->streamConf_->GetFormat(&pmt);
290 if (SUCCEEDED(hr)) {
291 if (pmt->formattype == FORMAT_VideoInfo) {
292 auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
293 params.width = videoInfo->bmiHeader.biWidth;
294 params.height = videoInfo->bmiHeader.biHeight;
295 params.framerate = {1e7, static_cast<double>(videoInfo->AvgTimePerFrame)};
296 }
297 }
298 return params;
299}
300
301void
302VideoDeviceImpl::setDeviceParams(const DeviceParams& params)
303{
304 if (id == DEVICE_DESKTOP) {
305 desktopFrameRate_ = params.framerate;
306 return;
307 }
308 if (params.width and params.height) {
309 auto pmt = capMap_.at(std::make_pair(params.width, params.height));
310 if (pmt != nullptr) {
311 ((VIDEOINFOHEADER*) pmt->pbFormat)->AvgTimePerFrame
312 = (FrameRate(1e7) / params.framerate).real();
313 if (FAILED(cInterface->streamConf_->SetFormat(pmt))) {
314 JAMI_ERR("Unable to set settings.");
315 }
316 }
317 }
318}
319
320std::vector<VideoSize>
322{
323 return sizeList_;
324}
325
326std::vector<FrameRate>
327VideoDeviceImpl::getRateList(const std::string& channel, VideoSize size) const
328{
329 (void) channel;
330 return rateList_.at(size);
331}
332
333std::vector<VideoSize>
334VideoDeviceImpl::getSizeList(const std::string& channel) const
335{
336 (void) channel;
337 return sizeList_;
338}
339
340std::vector<std::string>
342{
343 return {"default"};
344}
345
346VideoDevice::VideoDevice(const std::string& path,
347 const std::vector<std::map<std::string, std::string>>&)
348 : deviceImpl_(new VideoDeviceImpl(path))
349{
350 id_ = path;
351 name = deviceImpl_->name;
352}
353
354DeviceParams
356{
357 return deviceImpl_->getDeviceParams();
358}
359
360void
361VideoDevice::setDeviceParams(const DeviceParams& params)
362{
363 return deviceImpl_->setDeviceParams(params);
364}
365
366std::vector<std::string>
368{
369 return deviceImpl_->getChannelList();
370}
371
372std::vector<VideoSize>
373VideoDevice::getSizeList(const std::string& channel) const
374{
375 return deviceImpl_->getSizeList(channel);
376}
377
378std::vector<FrameRate>
379VideoDevice::getRateList(const std::string& channel, VideoSize size) const
380{
381 return deviceImpl_->getRateList(channel, size);
382}
383
385
386} // namespace video
387} // namespace jami
VideoDeviceImpl(const std::string &id)
std::vector< VideoSize > getSizeList() const
std::vector< std::string > getChannelList() const
std::vector< FrameRate > getRateList(const std::string &channel, VideoSize size) const
void setDeviceParams(const DeviceParams &)
DeviceParams getDeviceParams() const
std::vector< FrameRate > getRateList() const
std::vector< VideoSize > getSizeList(const std::string &channel) const
std::vector< std::string > getChannelList() const
VideoDevice(const std::string &path, const std::vector< std::map< std::string, std::string > > &devInfo)
DeviceParams getDeviceParams() const
Returns the parameters needed for actual use of the device.
#define JAMI_ERR(...)
Definition logger.h:218
int64_t size(const std::filesystem::path &path)
static constexpr const char DEVICE_DESKTOP[]
std::pair< unsigned, unsigned > VideoSize
rational< double > FrameRate
void emitSignal(Args... args)
Definition ring_signal.h:64
std::string to_string(double value)
DeviceParams Parameters used by MediaDecoder and MediaEncoder to open a LibAV device/stream.