LCOV - code coverage report
Current view: top level - src/media/video - video_device.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 70.2 % 94 66
Test Date: 2026-06-13 09:18:46 Functions: 50.0 % 14 7

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

Generated by: LCOV version 2.0-1