Ring Daemon 16.0.0
Loading...
Searching...
No Matches
video_device.h
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#pragma once
18
19#include "media/media_device.h"
20#include "video_base.h"
21#include "rational.h"
22
24#include "string_utils.h"
25#include "logger.h"
26
27#include <fmt/core.h>
28
29#include <cmath>
30#include <map>
31#include <memory>
32#include <string>
33#include <vector>
34#include <algorithm>
35#include <sstream>
36
37namespace jami {
38namespace video {
39
40using VideoSize = std::pair<unsigned, unsigned>;
42static constexpr const char DEVICE_DESKTOP[] = "desktop";
43
44class VideoDeviceImpl;
45
47{
48public:
49 VideoDevice(const std::string& path,
50 const std::vector<std::map<std::string, std::string>>& devInfo);
52
53 /*
54 * The device name, e.g. "Integrated Camera",
55 * actually used as the identifier.
56 */
57 std::string name {};
58
59 const std::string& getDeviceId() const { return id_; }
60
61 /*
62 * Get the 3 level deep tree of possible settings for the device.
63 * The levels are channels, sizes, and rates.
64 *
65 * The result map for the "Integrated Camera" looks like this:
66 *
67 * {'Camera 1': {'1280x720': ['10'],
68 * '320x240': ['30', '15'],
69 * '352x288': ['30', '15'],
70 * '424x240': ['30', '15'],
71 * '640x360': ['30', '15'],
72 * '640x480': ['30', '15'],
73 * '800x448': ['15'],
74 * '960x540': ['10']}}
75 */
77 {
79
80 for (const auto& chan : getChannelList())
81 for (const auto& size : getSizeList(chan)) {
82 std::string sz = fmt::format("{}x{}", size.first, size.second);
83 auto rates = getRateList(chan, size);
84 std::vector<std::string> rates_str {rates.size()};
85 std::transform(rates.begin(), rates.end(), rates_str.begin(), [](const FrameRate& r) {
86 return jami::to_string(r.real());
87 });
88 cap[chan][sz] = std::move(rates_str);
89 }
90
91 return cap;
92 }
93
94 /* Default setting is found by using following rules:
95 * - frame height <= 640 pixels
96 * - frame rate >= 10 fps
97 */
99 {
100 auto settings = getSettings();
101 auto channels = getChannelList();
102 if (channels.empty())
103 return {};
104 settings.channel = getChannelList().front();
105
106 VideoSize max_size {0, 0};
108
109 auto sizes = getSizeList(settings.channel);
110 for (auto& s : sizes) {
111 if (s.second > 640)
112 continue;
113 auto rates = getRateList(settings.channel, s);
114 if (rates.empty())
115 continue;
116 auto max_rate = *std::max_element(rates.begin(), rates.end());
117 if (max_rate < 10)
118 continue;
119 if (s.second > max_size.second
120 || (s.second == max_size.second && s.first > max_size.first)) {
121 max_size = s;
123 }
124 }
125 if (max_size.second > 0) {
126 settings.video_size = fmt::format("{}x{}", max_size.first, max_size.second);
127 settings.framerate = jami::to_string(max_size_rate.real());
128 JAMI_WARN("Default video settings: %s, %s FPS",
129 settings.video_size.c_str(),
130 settings.framerate.c_str());
131 }
132
133 return settings;
134 }
135
136 /*
137 * Get the settings for the device.
138 */
140 {
141 auto params = getDeviceParams();
143 settings.name = name.empty() ? params.name : name;
144 settings.unique_id = params.unique_id;
145 settings.input = params.input;
146 settings.channel = params.channel_name;
147 settings.video_size = sizeToString(params.width, params.height);
148 settings.framerate = jami::to_string(params.framerate.real());
149 return settings;
150 }
151
152 /*
153 * Setup the device with the preferences listed in the "settings" map.
154 * The expected map should be similar to the result of getSettings().
155 *
156 * If a key is missing, a valid default value is choosen. Thus, calling
157 * this function with an empty map will reset the device to default.
158 */
160 {
162 params.name = settings.name;
163 params.input = settings.input;
164 params.unique_id = settings.unique_id;
165 params.channel_name = settings.channel;
166 auto size = sizeFromString(settings.channel, settings.video_size);
167 params.width = size.first;
168 params.height = size.second;
169 params.framerate = rateFromString(settings.channel, size, settings.framerate);
170 setDeviceParams(params);
171 }
172
173 void setOrientation(int orientation) { orientation_ = orientation; }
174
179 std::vector<std::string> getChannelList() const;
180
181private:
182 std::vector<VideoSize> getSizeList(const std::string& channel) const;
183 std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
184
185 VideoSize sizeFromString(const std::string& channel, const std::string& size) const
186 {
187 auto size_list = getSizeList(channel);
188 for (const auto& s : size_list) {
189 if (sizeToString(s.first, s.second) == size)
190 return s;
191 }
192 return {0, 0};
193 }
194
195 std::string sizeToString(unsigned w, unsigned h) const
196 {
197 return fmt::format("{}x{}", w, h);
198 }
199
200 FrameRate rateFromString(const std::string& channel,
201 VideoSize size,
202 const std::string& rate) const
203 {
204 FrameRate closest {0};
205 double rate_val = 0;
206 try {
207 rate_val = rate.empty() ? 0 : std::stod(rate);
208 } catch (...) {
209 JAMI_WARN("Unable to read framerate \"%s\"", rate.c_str());
210 }
211 // fallback to framerate closest to 30 FPS
212 if (rate_val == 0)
213 rate_val = 30;
214 double closest_dist = std::numeric_limits<double>::max();
215 auto rate_list = getRateList(channel, size);
216 for (const auto& r : rate_list) {
217 double dist = std::fabs(r.real() - rate_val);
218 if (dist < closest_dist) {
219 closest = r;
221 }
222 }
223 return closest;
224 }
225
226 void setDeviceParams(const DeviceParams&);
227
228 /*
229 * The device node, e.g. "046d082dF41A2B3F".
230 */
231 std::string id_ {};
232
233 int orientation_ {0};
234
235 /*
236 * Device specific implementation.
237 * On Linux, V4L2 stuffs go there.
238 *
239 * Note: since a VideoDevice is copyable,
240 * deviceImpl_ cannot be an unique_ptr.
241 */
242 std::shared_ptr<VideoDeviceImpl> deviceImpl_;
243};
244
245} // namespace video
246} // namespace jami
VideoSettings getSettings() const
const std::string & getDeviceId() const
std::vector< std::string > getChannelList() const
VideoSettings getDefaultSettings() const
libjami::VideoCapabilities getCapabilities() const
void applySettings(VideoSettings settings)
DeviceParams getDeviceParams() const
Returns the parameters needed for actual use of the device.
void setOrientation(int orientation)
#define JAMI_WARN(...)
Definition logger.h:217
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)
std::map< std::string, std::map< std::string, std::vector< std::string > > > VideoCapabilities
DeviceParams Parameters used by MediaDecoder and MediaEncoder to open a LibAV device/stream.