Ring Daemon
Loading...
Searching...
No Matches
winvideo/video_device_impl.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 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
119 hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
120 if (hr == S_FALSE) {
122 }
123 pDevEnum->Release();
124 if (FAILED(hr) || pEnum == nullptr) {
125 JAMI_ERR() << "No webcam found";
126 return;
127 }
128
129 // Auto-deletion at exception
130 auto IEnumMonikerDeleter = [](IEnumMoniker* p) {
131 p->Release();
132 };
134
136 while ((pEnumGuard->Next(1, &pMoniker, NULL) == S_OK)) {
138 hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**) &pPropBag);
139 if (FAILED(hr)) {
140 pMoniker->Release();
141 continue;
142 }
143
146
148 if (hr != S_OK) {
149 continue;
150 }
151 hr = pMoniker->GetDisplayName(bind_ctx, NULL, &olestr);
152 if (hr != S_OK) {
153 continue;
154 }
156 if (unique_name.empty()) {
157 continue;
158 }
159
160 // replace ':' with '_' since ffmpeg uses : to delineate between sources */
161 std::replace(unique_name.begin(), unique_name.end(), ':', '_');
162 unique_name = std::string("video=") + unique_name;
163
164 // We want to get the capabilities of a device with the unique_name
165 // that corresponds to what was enumerated by the video device monitor.
166 if (unique_name.find(this->id) == std::string::npos) {
167 continue;
168 }
169
170 this->id = unique_name;
171
172 // get friendly name
173 VARIANT var;
174 VariantInit(&var);
175 hr = pPropBag->Read(L"Description", &var, 0);
176 if (FAILED(hr)) {
177 hr = pPropBag->Read(L"FriendlyName", &var, 0);
178 }
179 if (SUCCEEDED(hr)) {
180 this->name = to_string(var.bstrVal);
181 }
182 pPropBag->Write(L"FriendlyName", &var);
183
184 hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void**) &cInterface->videoInputFilter_);
185 if (SUCCEEDED(hr))
186 hr = cInterface->graph_->AddFilter(cInterface->videoInputFilter_, var.bstrVal);
187 else {
188 fail("Unable to add filter to video device.");
189 }
190 hr = cInterface->captureGraph_->FindInterface(&PIN_CATEGORY_PREVIEW,
192 cInterface->videoInputFilter_,
194 (void**) &cInterface->streamConf_);
195 if (FAILED(hr)) {
196 hr = cInterface->captureGraph_->FindInterface(&PIN_CATEGORY_CAPTURE,
198 cInterface->videoInputFilter_,
200 (void**) &cInterface->streamConf_);
201 if (FAILED(hr)) {
202 fail("Unable to config the stream!");
203 }
204 }
205
207 pPropBag->Release();
208
209 // Device found.
210 break;
211 }
212 pMoniker->Release();
213
214 if (FAILED(hr) || cInterface->streamConf_ == NULL) {
215 fail("Unable to find the video device.");
216 }
217
218 int piCount;
219 int piSize;
220 cInterface->streamConf_->GetNumberOfCapabilities(&piCount, &piSize);
223 std::map<std::pair<jami::video::VideoSize, jami::video::FrameRate>, LONG> bitrateList;
224 for (int i = 0; i < piCount; i++) {
225 cInterface->streamConf_->GetStreamCaps(i, &pmt, (BYTE*) &pSCC);
226 if (pmt->formattype != FORMAT_VideoInfo) {
227 continue;
228 }
229 auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
230 auto size = jami::video::VideoSize(videoInfo->bmiHeader.biWidth, videoInfo->bmiHeader.biHeight);
231 // use 1e7 / MinFrameInterval to get maximum fps
232 auto rate = jami::video::FrameRate(1e7, pSCC.MinFrameInterval);
233 auto bitrate = videoInfo->dwBitRate;
234 // Only add configurations with positive bitrates.
235 if (bitrate == 0)
236 continue;
237 // Avoid adding multiple rates with different bitrates.
238 auto ratesIt = rateList_.find(size);
239 if (ratesIt != rateList_.end()
240 && std::find(ratesIt->second.begin(), ratesIt->second.end(), rate) != ratesIt->second.end()) {
241 // Update bitrate and cap map if the bitrate is greater.
242 auto key = std::make_pair(size, rate);
243 if (bitrate > bitrateList[key]) {
244 bitrateList[key] = bitrate;
245 capMap_[size] = pmt;
246 }
247 continue;
248 }
249 // Add new size, rate, bitrate, and cap map.
250 sizeList_.emplace_back(size);
251 rateList_[size].emplace_back(rate);
252 bitrateList[std::make_pair(size, rate)] = bitrate;
253 capMap_[size] = pmt;
254 }
255}
256
257void
258VideoDeviceImpl::fail(const std::string& error)
259{
260 throw std::runtime_error(error);
261}
262
263DeviceParams
265{
266 DeviceParams params;
267
268 params.name = name;
269 params.unique_id = id;
270 params.input = id;
271 if (id == DEVICE_DESKTOP) {
272 params.format = "dxgigrab";
273 params.framerate = desktopFrameRate_;
274 return params;
275 }
276
277 params.format = "dshow";
278
280 HRESULT hr = cInterface->streamConf_->GetFormat(&pmt);
281 if (SUCCEEDED(hr)) {
282 if (pmt->formattype == FORMAT_VideoInfo) {
283 auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
284 params.width = videoInfo->bmiHeader.biWidth;
285 params.height = videoInfo->bmiHeader.biHeight;
286 params.framerate = {1e7, static_cast<double>(videoInfo->AvgTimePerFrame)};
287 }
288 }
289 return params;
290}
291
292void
293VideoDeviceImpl::setDeviceParams(const DeviceParams& params)
294{
295 if (id == DEVICE_DESKTOP) {
296 desktopFrameRate_ = params.framerate;
297 return;
298 }
299 if (params.width and params.height) {
300 auto pmt = capMap_.at(std::make_pair(params.width, params.height));
301 if (pmt != nullptr) {
302 ((VIDEOINFOHEADER*) pmt->pbFormat)->AvgTimePerFrame = (FrameRate(1e7) / params.framerate).real();
303 if (FAILED(cInterface->streamConf_->SetFormat(pmt))) {
304 JAMI_ERR("Unable to set settings.");
305 }
306 }
307 }
308}
309
310std::vector<VideoSize>
312{
313 return sizeList_;
314}
315
316std::vector<FrameRate>
317VideoDeviceImpl::getRateList(const std::string& channel, VideoSize size) const
318{
319 (void) channel;
320 return rateList_.at(size);
321}
322
323std::vector<VideoSize>
324VideoDeviceImpl::getSizeList(const std::string& channel) const
325{
326 (void) channel;
327 return sizeList_;
328}
329
330std::vector<std::string>
332{
333 return {"default"};
334}
335
336VideoDevice::VideoDevice(const std::string& path, const std::vector<std::map<std::string, std::string>>&)
337 : deviceImpl_(new VideoDeviceImpl(path))
338{
339 id_ = path;
340 name = deviceImpl_->name;
341}
342
343DeviceParams
345{
346 return deviceImpl_->getDeviceParams();
347}
348
349void
350VideoDevice::setDeviceParams(const DeviceParams& params)
351{
352 return deviceImpl_->setDeviceParams(params);
353}
354
355std::vector<std::string>
357{
358 return deviceImpl_->getChannelList();
359}
360
361std::vector<VideoSize>
362VideoDevice::getSizeList(const std::string& channel) const
363{
364 return deviceImpl_->getSizeList(channel);
365}
366
367std::vector<FrameRate>
368VideoDevice::getRateList(const std::string& channel, VideoSize size) const
369{
370 return deviceImpl_->getRateList(channel, size);
371}
372
374
375} // namespace video
376} // 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:230
static constexpr const char DEVICE_DESKTOP[]
std::pair< unsigned, unsigned > VideoSize
rational< double > FrameRate
void emitSignal(Args... args)
Definition jami_signal.h:64
std::string to_string(double value)
DeviceParams Parameters used by MediaDecoder and MediaEncoder to open a LibAV device/stream.