Line data Source code
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 <cstring>
22 : #include <map>
23 : #include <sstream>
24 : #include <stdexcept>
25 : #include <string>
26 : #include <vector>
27 :
28 : extern "C" {
29 : #include <linux/videodev2.h>
30 : #if !defined(VIDIOC_ENUM_FRAMESIZES) || !defined(VIDIOC_ENUM_FRAMEINTERVALS)
31 : #error You need at least Linux 2.6.19
32 : #endif
33 :
34 : #include <fcntl.h>
35 : #include <unistd.h>
36 : #include <sys/ioctl.h>
37 : }
38 :
39 : #include "logger.h"
40 : #include "../video_device.h"
41 : #include "string_utils.h"
42 :
43 : #define ZEROVAR(x) std::memset(&(x), 0, sizeof(x))
44 :
45 : namespace jami {
46 : namespace video {
47 :
48 : namespace {
49 : // Owns a file descriptor so that the device node is released even when probing
50 : // it throws, which happens for every node that is not a capture device.
51 : class ScopedFd
52 : {
53 : public:
54 0 : explicit ScopedFd(int fd)
55 0 : : fd_(fd)
56 0 : {}
57 0 : ~ScopedFd()
58 : {
59 0 : if (fd_ != -1)
60 0 : ::close(fd_);
61 0 : }
62 : ScopedFd(const ScopedFd&) = delete;
63 : ScopedFd& operator=(const ScopedFd&) = delete;
64 :
65 0 : int get() const { return fd_; }
66 :
67 : private:
68 : int fd_;
69 : };
70 : } // namespace
71 :
72 : class VideoV4l2Rate
73 : {
74 : public:
75 33 : VideoV4l2Rate(unsigned rate_numerator = 0, unsigned rate_denominator = 0, unsigned format = 0)
76 33 : : frame_rate(rate_denominator, rate_numerator)
77 : {
78 33 : if (format)
79 0 : pixel_formats.push_back(format);
80 33 : }
81 :
82 : FrameRate frame_rate;
83 : std::vector<unsigned> pixel_formats;
84 : std::string libAvPixelformat(bool passthrough = false) const;
85 : };
86 :
87 : class VideoV4l2Size
88 : {
89 : public:
90 33 : VideoV4l2Size(const unsigned width, const unsigned height)
91 33 : : width(width)
92 33 : , height(height)
93 33 : , rates_()
94 33 : {}
95 :
96 : /**
97 : * @throw std::runtime_error
98 : */
99 : void readFrameRates(int fd, unsigned int pixel_format);
100 :
101 : std::vector<FrameRate> getRateList() const;
102 : VideoV4l2Rate getRate(const FrameRate& rate) const;
103 :
104 : unsigned width;
105 : unsigned height;
106 :
107 : private:
108 : void addRate(VideoV4l2Rate proposed_rate);
109 : std::vector<VideoV4l2Rate> rates_;
110 : };
111 :
112 : bool
113 0 : operator==(VideoV4l2Size& a, VideoV4l2Size& b)
114 : {
115 0 : return a.height == b.height && a.width == b.width;
116 : }
117 :
118 : class VideoV4l2Channel
119 : {
120 : public:
121 : VideoV4l2Channel(unsigned idx, const char* s);
122 :
123 : /**
124 : * @throw std::runtime_error
125 : */
126 : void readFormats(int fd);
127 :
128 : /**
129 : * @throw std::runtime_error
130 : */
131 : unsigned int readSizes(int fd, unsigned int pixel_format);
132 :
133 : std::vector<VideoSize> getSizeList() const;
134 : const VideoV4l2Size& getSize(VideoSize name) const;
135 :
136 : unsigned idx;
137 : std::string name;
138 :
139 : private:
140 : void putCIFFirst();
141 : std::vector<VideoV4l2Size> sizes_;
142 : };
143 :
144 : class VideoDeviceImpl
145 : {
146 : public:
147 : /**
148 : * @throw std::runtime_error
149 : */
150 : VideoDeviceImpl(const std::string& id, const std::string& path);
151 :
152 : std::string unique_id;
153 : std::string path;
154 : std::string name;
155 :
156 : std::vector<std::string> getChannelList() const;
157 : std::vector<VideoSize> getSizeList(const std::string& channel) const;
158 : std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
159 :
160 : DeviceParams getDeviceParams() const;
161 : void setDeviceParams(const DeviceParams&);
162 :
163 : private:
164 : std::vector<VideoV4l2Channel> channels_;
165 : const VideoV4l2Channel& getChannel(const std::string& name) const;
166 :
167 : /* Preferences */
168 : VideoV4l2Channel channel_;
169 : VideoV4l2Size size_;
170 : VideoV4l2Rate rate_;
171 : bool passthrough_ = false;
172 : };
173 :
174 : static const unsigned pixelformats_supported[] = {
175 : /* pixel format depth description */
176 :
177 : /* preferred formats, they can be fed directly to the video encoder */
178 : V4L2_PIX_FMT_YUV420, /* 12 YUV 4:2:0 */
179 : V4L2_PIX_FMT_YUV422P, /* 16 YVU422 planar */
180 : V4L2_PIX_FMT_YUV444, /* 16 xxxxyyyy uuuuvvvv */
181 :
182 : /* Luminance+Chrominance formats */
183 : V4L2_PIX_FMT_YVU410, /* 9 YVU 4:1:0 */
184 : V4L2_PIX_FMT_YVU420, /* 12 YVU 4:2:0 */
185 : V4L2_PIX_FMT_YUYV, /* 16 YUV 4:2:2 */
186 : V4L2_PIX_FMT_YYUV, /* 16 YUV 4:2:2 */
187 : V4L2_PIX_FMT_YVYU, /* 16 YVU 4:2:2 */
188 : V4L2_PIX_FMT_UYVY, /* 16 YUV 4:2:2 */
189 : V4L2_PIX_FMT_VYUY, /* 16 YUV 4:2:2 */
190 : V4L2_PIX_FMT_YUV411P, /* 16 YVU411 planar */
191 : V4L2_PIX_FMT_Y41P, /* 12 YUV 4:1:1 */
192 : V4L2_PIX_FMT_YUV555, /* 16 YUV-5-5-5 */
193 : V4L2_PIX_FMT_YUV565, /* 16 YUV-5-6-5 */
194 : V4L2_PIX_FMT_YUV32, /* 32 YUV-8-8-8-8 */
195 : V4L2_PIX_FMT_YUV410, /* 9 YUV 4:1:0 */
196 : V4L2_PIX_FMT_HI240, /* 8 8-bit color */
197 : V4L2_PIX_FMT_HM12, /* 8 YUV 4:2:0 16x16 macroblocks */
198 :
199 : /* two planes -- one Y, one Cr + Cb interleaved */
200 : V4L2_PIX_FMT_NV12, /* 12 Y/CbCr 4:2:0 */
201 : V4L2_PIX_FMT_NV21, /* 12 Y/CrCb 4:2:0 */
202 : V4L2_PIX_FMT_NV16, /* 16 Y/CbCr 4:2:2 */
203 : V4L2_PIX_FMT_NV61, /* 16 Y/CrCb 4:2:2 */
204 :
205 : /* Compressed formats */
206 : V4L2_PIX_FMT_MJPEG,
207 : V4L2_PIX_FMT_JPEG,
208 : V4L2_PIX_FMT_DV,
209 : V4L2_PIX_FMT_MPEG,
210 : V4L2_PIX_FMT_H264,
211 : V4L2_PIX_FMT_H264_NO_SC,
212 : V4L2_PIX_FMT_H264_MVC,
213 : V4L2_PIX_FMT_H263,
214 : V4L2_PIX_FMT_MPEG1,
215 : V4L2_PIX_FMT_MPEG2,
216 : V4L2_PIX_FMT_MPEG4,
217 : V4L2_PIX_FMT_XVID,
218 : V4L2_PIX_FMT_VC1_ANNEX_G,
219 : V4L2_PIX_FMT_VC1_ANNEX_L,
220 : V4L2_PIX_FMT_VP8,
221 :
222 : #if 0
223 : /* RGB formats */
224 : V4L2_PIX_FMT_RGB332, /* 8 RGB-3-3-2 */
225 : V4L2_PIX_FMT_RGB444, /* 16 xxxxrrrr ggggbbbb */
226 : V4L2_PIX_FMT_RGB555, /* 16 RGB-5-5-5 */
227 : V4L2_PIX_FMT_RGB565, /* 16 RGB-5-6-5 */
228 : V4L2_PIX_FMT_RGB555X, /* 16 RGB-5-5-5 BE */
229 : V4L2_PIX_FMT_RGB565X, /* 16 RGB-5-6-5 BE */
230 : V4L2_PIX_FMT_BGR666, /* 18 BGR-6-6-6 */
231 : V4L2_PIX_FMT_BGR24, /* 24 BGR-8-8-8 */
232 : V4L2_PIX_FMT_RGB24, /* 24 RGB-8-8-8 */
233 : V4L2_PIX_FMT_BGR32, /* 32 BGR-8-8-8-8 */
234 : V4L2_PIX_FMT_RGB32, /* 32 RGB-8-8-8-8 */
235 :
236 : /* Grey formats */
237 : V4L2_PIX_FMT_GREY, /* 8 Greyscale */
238 : V4L2_PIX_FMT_Y4, /* 4 Greyscale */
239 : V4L2_PIX_FMT_Y6, /* 6 Greyscale */
240 : V4L2_PIX_FMT_Y10, /* 10 Greyscale */
241 : V4L2_PIX_FMT_Y16, /* 16 Greyscale */
242 :
243 : /* Palette formats */
244 : V4L2_PIX_FMT_PAL8, /* 8 8-bit palette */
245 : #endif
246 : };
247 :
248 : /* Returns a score for the given pixelformat
249 : *
250 : * Lowest score is the best, the first entries in the array are the formats
251 : * supported as an input for the video encoders.
252 : *
253 : * See pixelformats_supported array for the support list.
254 : */
255 : static unsigned int
256 0 : pixelformat_score(unsigned pixelformat)
257 : {
258 0 : unsigned int formats_count = std::size(pixelformats_supported);
259 0 : for (unsigned int i = 0; i < formats_count; ++i) {
260 0 : if (pixelformats_supported[i] == pixelformat)
261 0 : return i;
262 : }
263 0 : return UINT_MAX - 1;
264 : }
265 :
266 : using std::vector;
267 : using std::string;
268 :
269 : vector<FrameRate>
270 0 : VideoV4l2Size::getRateList() const
271 : {
272 0 : vector<FrameRate> rates;
273 0 : rates.reserve(rates_.size());
274 0 : for (const auto& r : rates_)
275 0 : rates.emplace_back(r.frame_rate);
276 0 : return rates;
277 0 : }
278 :
279 : void
280 0 : VideoV4l2Size::readFrameRates(int fd, unsigned int pixel_format)
281 : {
282 0 : VideoV4l2Rate fallback_rate {1, 25, pixel_format};
283 :
284 : v4l2_frmivalenum frmival;
285 0 : ZEROVAR(frmival);
286 0 : frmival.pixel_format = pixel_format;
287 0 : frmival.width = width;
288 0 : frmival.height = height;
289 :
290 0 : if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival)) {
291 0 : addRate(fallback_rate);
292 0 : JAMI_ERROR("Unable to query frame interval for size");
293 0 : return;
294 : }
295 :
296 0 : if (frmival.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
297 0 : addRate(fallback_rate);
298 0 : JAMI_ERROR("Continuous and stepwise Frame Intervals are not supported");
299 0 : return;
300 : }
301 :
302 : do {
303 0 : addRate({frmival.discrete.numerator, frmival.discrete.denominator, pixel_format});
304 0 : ++frmival.index;
305 0 : } while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival));
306 0 : }
307 :
308 : VideoV4l2Rate
309 0 : VideoV4l2Size::getRate(const FrameRate& rate) const
310 : {
311 0 : for (const auto& item : rates_) {
312 0 : if (std::fabs((item.frame_rate - rate).real()) < 0.0001)
313 0 : return item;
314 : }
315 0 : return rates_.back();
316 : }
317 :
318 : void
319 0 : VideoV4l2Size::addRate(VideoV4l2Rate new_rate)
320 : {
321 0 : for (auto& item : rates_) {
322 0 : if (item.frame_rate == new_rate.frame_rate) {
323 0 : for (auto fmt : new_rate.pixel_formats) {
324 0 : if (std::find(item.pixel_formats.begin(), item.pixel_formats.end(), fmt) == item.pixel_formats.end())
325 0 : item.pixel_formats.push_back(fmt);
326 : }
327 0 : return;
328 : }
329 : }
330 :
331 0 : rates_.push_back(new_rate);
332 : }
333 :
334 33 : VideoV4l2Channel::VideoV4l2Channel(unsigned idx, const char* s)
335 33 : : idx(idx)
336 66 : , name(s)
337 33 : , sizes_()
338 33 : {}
339 :
340 : std::vector<VideoSize>
341 0 : VideoV4l2Channel::getSizeList() const
342 : {
343 0 : vector<VideoSize> v;
344 0 : v.reserve(sizes_.size());
345 0 : for (const auto& item : sizes_)
346 0 : v.emplace_back(item.width, item.height);
347 :
348 0 : return v;
349 0 : }
350 :
351 : unsigned int
352 0 : VideoV4l2Channel::readSizes(int fd, unsigned int pixelformat)
353 : {
354 : v4l2_frmsizeenum frmsize;
355 0 : ZEROVAR(frmsize);
356 :
357 0 : frmsize.index = 0;
358 0 : frmsize.pixel_format = pixelformat;
359 :
360 0 : if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) < 0) {
361 : v4l2_format fmt;
362 0 : ZEROVAR(fmt);
363 :
364 0 : fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
365 0 : if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0)
366 0 : throw std::runtime_error("Unable to get format");
367 :
368 0 : VideoV4l2Size size(fmt.fmt.pix.width, fmt.fmt.pix.height);
369 0 : size.readFrameRates(fd, fmt.fmt.pix.pixelformat);
370 0 : sizes_.push_back(size);
371 :
372 0 : return fmt.fmt.pix.pixelformat;
373 0 : }
374 :
375 0 : if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
376 : // We do not take care of V4L2_FRMSIZE_TYPE_CONTINUOUS or V4L2_FRMSIZE_TYPE_STEPWISE
377 0 : JAMI_ERROR("Continuous Frame sizes not supported");
378 0 : return pixelformat;
379 : }
380 :
381 : // Real work starts here: attach framerates to sizes and update pixelformat information
382 : do {
383 0 : bool size_exists = false;
384 0 : VideoV4l2Size size(frmsize.discrete.width, frmsize.discrete.height);
385 :
386 0 : for (auto& item : sizes_) {
387 0 : if (item == size) {
388 0 : size_exists = true;
389 : // If a size already exist we add frame rates since there may be some
390 : // frame rates available in one format that are not availabe in another.
391 0 : item.readFrameRates(fd, frmsize.pixel_format);
392 : }
393 : }
394 :
395 0 : if (!size_exists) {
396 0 : size.readFrameRates(fd, frmsize.pixel_format);
397 0 : sizes_.push_back(size);
398 : }
399 :
400 0 : ++frmsize.index;
401 0 : } while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize));
402 :
403 0 : return pixelformat;
404 : }
405 :
406 : // Put CIF resolution (352x288) first in the list since it is more prevalent in
407 : // VoIP
408 : void
409 0 : VideoV4l2Channel::putCIFFirst()
410 : {
411 0 : const auto iter = std::find_if(sizes_.begin(), sizes_.end(), [](const VideoV4l2Size& size) {
412 0 : return size.width == 352 and size.height == 258;
413 : });
414 :
415 0 : if (iter != sizes_.end() and iter != sizes_.begin())
416 0 : std::swap(*iter, *sizes_.begin());
417 0 : }
418 :
419 : void
420 0 : VideoV4l2Channel::readFormats(int fd)
421 : {
422 0 : if (ioctl(fd, VIDIOC_S_INPUT, &idx))
423 0 : throw std::runtime_error("VIDIOC_S_INPUT failed");
424 :
425 : v4l2_fmtdesc fmt;
426 0 : ZEROVAR(fmt);
427 0 : unsigned fmt_index = 0;
428 :
429 0 : fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
430 0 : while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmt)) {
431 0 : if (fmt_index != fmt.index)
432 0 : break;
433 0 : readSizes(fd, fmt.pixelformat);
434 0 : fmt.index = ++fmt_index;
435 : }
436 :
437 0 : if (fmt_index == 0)
438 0 : throw std::runtime_error("Unable to enumerate formats");
439 :
440 0 : putCIFFirst();
441 0 : }
442 :
443 : const VideoV4l2Size&
444 0 : VideoV4l2Channel::getSize(VideoSize s) const
445 : {
446 0 : for (const auto& item : sizes_) {
447 0 : if (item.width == s.first && item.height == s.second)
448 0 : return item;
449 : }
450 :
451 0 : assert(not sizes_.empty());
452 0 : return sizes_.front();
453 : }
454 :
455 33 : VideoDeviceImpl::VideoDeviceImpl(const string& id, const std::string& path)
456 33 : : unique_id(id)
457 33 : , path(path)
458 33 : , name()
459 33 : , channels_()
460 33 : , channel_(-1, "")
461 33 : , size_(-1, -1)
462 33 : , rate_(-1, 1, 0)
463 : {
464 33 : if (id == DEVICE_DESKTOP) {
465 33 : name = DEVICE_DESKTOP;
466 33 : rate_.frame_rate = 30;
467 33 : return;
468 : }
469 0 : ScopedFd fd(open(path.c_str(), O_RDWR));
470 0 : if (fd.get() == -1)
471 0 : throw std::runtime_error("Unable to open device");
472 :
473 : v4l2_capability cap;
474 0 : if (ioctl(fd.get(), VIDIOC_QUERYCAP, &cap))
475 0 : throw std::runtime_error("Unable to query capabilities");
476 :
477 0 : if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
478 0 : throw std::runtime_error("Not a capture device");
479 :
480 0 : if (cap.capabilities & V4L2_CAP_TOUCH)
481 0 : throw std::runtime_error("Touch device, ignoring it");
482 :
483 0 : name = string(reinterpret_cast<const char*>(cap.card));
484 :
485 : v4l2_input input;
486 0 : ZEROVAR(input);
487 : unsigned idx;
488 0 : input.index = idx = 0;
489 0 : while (!ioctl(fd.get(), VIDIOC_ENUMINPUT, &input)) {
490 0 : if (idx != input.index)
491 0 : break;
492 :
493 0 : if (input.type & V4L2_INPUT_TYPE_CAMERA) {
494 0 : VideoV4l2Channel channel(idx, (const char*) input.name);
495 0 : channel.readFormats(fd.get());
496 0 : if (not channel.getSizeList().empty())
497 0 : channels_.push_back(channel);
498 0 : }
499 :
500 0 : input.index = ++idx;
501 : }
502 0 : }
503 :
504 : string
505 0 : VideoV4l2Rate::libAvPixelformat(bool passthrough) const
506 : {
507 0 : unsigned best_fmt = 0;
508 0 : unsigned best_score = UINT_MAX;
509 :
510 0 : for (const auto& fmt : pixel_formats) {
511 0 : if (passthrough) {
512 : // Favor H264 > HEVC > MJPEG if passthrough
513 0 : if (fmt == V4L2_PIX_FMT_H264 || fmt == V4L2_PIX_FMT_H264_NO_SC || fmt == V4L2_PIX_FMT_H264_MVC)
514 0 : return "h264";
515 0 : if (fmt == V4L2_PIX_FMT_H263)
516 0 : return "h263";
517 0 : if (fmt == V4L2_PIX_FMT_VP8)
518 0 : return "vp8";
519 : }
520 0 : auto score = pixelformat_score(fmt);
521 0 : if (score < best_score) {
522 0 : best_score = score;
523 0 : best_fmt = fmt;
524 : }
525 : }
526 :
527 : /* If passthrough was requested but no compressed format found, fallback to best found format */
528 :
529 0 : switch (best_fmt) {
530 : // Set codec name for those pixelformats.
531 : // Those names can be found in libavcodec/codec_desc.c
532 0 : case V4L2_PIX_FMT_MJPEG:
533 0 : return "mjpeg";
534 0 : case V4L2_PIX_FMT_DV:
535 0 : return "dvvideo";
536 0 : case V4L2_PIX_FMT_MPEG:
537 : case V4L2_PIX_FMT_MPEG1:
538 0 : return "mpeg1video";
539 0 : case V4L2_PIX_FMT_H264:
540 : case V4L2_PIX_FMT_H264_NO_SC:
541 : case V4L2_PIX_FMT_H264_MVC:
542 0 : return "h264";
543 0 : case V4L2_PIX_FMT_H263:
544 0 : return "h263";
545 0 : case V4L2_PIX_FMT_MPEG2:
546 0 : return "mpeg2video";
547 0 : case V4L2_PIX_FMT_MPEG4:
548 0 : return "mpeg4";
549 0 : case V4L2_PIX_FMT_VC1_ANNEX_G:
550 : case V4L2_PIX_FMT_VC1_ANNEX_L:
551 0 : return "vc1";
552 0 : case V4L2_PIX_FMT_VP8:
553 0 : return "vp8";
554 0 : default: // Most pixel formats do not need any codec
555 0 : return "";
556 : }
557 : }
558 :
559 : vector<string>
560 99 : VideoDeviceImpl::getChannelList() const
561 : {
562 99 : if (unique_id == DEVICE_DESKTOP)
563 297 : return {"default"};
564 0 : vector<string> v;
565 0 : v.reserve(channels_.size());
566 0 : for (const auto& itr : channels_)
567 0 : v.push_back(itr.name);
568 :
569 0 : return v;
570 0 : }
571 :
572 : vector<VideoSize>
573 96 : VideoDeviceImpl::getSizeList(const string& channel) const
574 : {
575 96 : if (unique_id == DEVICE_DESKTOP) {
576 288 : return {VideoSize(0, 0)};
577 : }
578 0 : return getChannel(channel).getSizeList();
579 : }
580 :
581 : vector<FrameRate>
582 96 : VideoDeviceImpl::getRateList(const string& channel, VideoSize size) const
583 : {
584 96 : if (unique_id == DEVICE_DESKTOP) {
585 : return {FrameRate(1),
586 : FrameRate(5),
587 : FrameRate(10),
588 : FrameRate(15),
589 : FrameRate(20),
590 : FrameRate(25),
591 : FrameRate(30),
592 : FrameRate(60),
593 : FrameRate(120),
594 288 : FrameRate(144)};
595 : }
596 0 : return getChannel(channel).getSize(size).getRateList();
597 : }
598 :
599 : const VideoV4l2Channel&
600 0 : VideoDeviceImpl::getChannel(const string& name) const
601 : {
602 0 : for (const auto& item : channels_)
603 0 : if (item.name == name)
604 0 : return item;
605 :
606 0 : assert(not channels_.empty());
607 0 : return channels_.front();
608 : }
609 :
610 : DeviceParams
611 191 : VideoDeviceImpl::getDeviceParams() const
612 : {
613 191 : DeviceParams params;
614 191 : params.name = name;
615 191 : params.unique_id = unique_id;
616 191 : params.input = path;
617 191 : if (unique_id == DEVICE_DESKTOP) {
618 191 : const auto* env = std::getenv("WAYLAND_DISPLAY");
619 191 : if (!env || strlen(env) == 0) {
620 191 : params.format = "x11grab";
621 : } else {
622 0 : params.format = "lavfi";
623 0 : params.input = "pipewiregrab";
624 : }
625 191 : params.framerate = rate_.frame_rate;
626 191 : return params;
627 : }
628 0 : params.format = "video4linux2";
629 0 : params.channel_name = channel_.name;
630 0 : params.channel = channel_.idx;
631 0 : params.width = size_.width;
632 0 : params.height = size_.height;
633 0 : params.framerate = rate_.frame_rate;
634 0 : params.pixel_format = rate_.libAvPixelformat(passthrough_);
635 0 : params.passthrough = passthrough_;
636 :
637 : // If passthrough was requested but we got a raw format, disable passthrough
638 0 : if (passthrough_ && params.pixel_format != "h264" && params.pixel_format != "vp8" && params.pixel_format != "h263"
639 0 : && params.pixel_format != "mjpeg" && params.pixel_format != "hevc") {
640 0 : params.passthrough = false;
641 : }
642 :
643 0 : return params;
644 0 : }
645 :
646 : void
647 63 : VideoDeviceImpl::setDeviceParams(const DeviceParams& params)
648 : {
649 63 : if (unique_id == DEVICE_DESKTOP) {
650 63 : rate_.frame_rate = params.framerate;
651 63 : return;
652 : }
653 : // Set preferences or fallback to defaults.
654 0 : channel_ = getChannel(params.channel_name);
655 0 : size_ = channel_.getSize({params.width, params.height});
656 : try {
657 0 : rate_ = size_.getRate(params.framerate);
658 0 : } catch (...) {
659 0 : rate_ = {};
660 0 : }
661 0 : passthrough_ = params.passthrough;
662 : }
663 :
664 33 : VideoDevice::VideoDevice(const std::string& id, const std::vector<std::map<std::string, std::string>>& devInfo)
665 33 : : id_(id)
666 : {
667 33 : deviceImpl_ = std::make_shared<VideoDeviceImpl>(id, devInfo.empty() ? id : devInfo.at(0).at("devPath"));
668 33 : name = deviceImpl_->name;
669 33 : }
670 :
671 : DeviceParams
672 191 : VideoDevice::getDeviceParams() const
673 : {
674 191 : auto params = deviceImpl_->getDeviceParams();
675 191 : params.orientation = orientation_;
676 191 : return params;
677 : }
678 :
679 : void
680 63 : VideoDevice::setDeviceParams(const DeviceParams& params)
681 : {
682 63 : return deviceImpl_->setDeviceParams(params);
683 : }
684 :
685 : std::vector<std::string>
686 99 : VideoDevice::getChannelList() const
687 : {
688 99 : return deviceImpl_->getChannelList();
689 : }
690 :
691 : std::vector<VideoSize>
692 96 : VideoDevice::getSizeList(const std::string& channel) const
693 : {
694 96 : return deviceImpl_->getSizeList(channel);
695 : }
696 :
697 : std::vector<FrameRate>
698 96 : VideoDevice::getRateList(const std::string& channel, VideoSize size) const
699 : {
700 96 : return deviceImpl_->getRateList(channel, size);
701 : }
702 :
703 66 : VideoDevice::~VideoDevice() {}
704 :
705 : } // namespace video
706 : } // namespace jami
|