LCOV - code coverage report
Current view: top level - src/media/video - video_device.h (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 66 94 70.2 %
Date: 2024-04-25 08:05:53 Functions: 7 10 70.0 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  Author: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
       5             :  *  Author: Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com>
       6             :  *
       7             :  *  This program is free software; you can redistribute it and/or modify
       8             :  *  it under the terms of the GNU General Public License as published by
       9             :  *  the Free Software Foundation; either version 3 of the License, or
      10             :  *  (at your option) any later version.
      11             :  *
      12             :  *  This program is distributed in the hope that it will be useful,
      13             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  *  GNU General Public License for more details.
      16             :  *
      17             :  *  You should have received a copy of the GNU General Public License
      18             :  *  along with this program; if not, write to the Free Software
      19             :  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
      20             :  */
      21             : 
      22             : #pragma once
      23             : 
      24             : #include "media/media_device.h"
      25             : #include "video_base.h"
      26             : #include "rational.h"
      27             : 
      28             : #include "videomanager_interface.h"
      29             : #include "string_utils.h"
      30             : #include "logger.h"
      31             : 
      32             : #include <fmt/core.h>
      33             : 
      34             : #include <cmath>
      35             : #include <map>
      36             : #include <memory>
      37             : #include <string>
      38             : #include <vector>
      39             : #include <algorithm>
      40             : #include <sstream>
      41             : 
      42             : namespace jami {
      43             : namespace video {
      44             : 
      45             : using VideoSize = std::pair<unsigned, unsigned>;
      46             : using FrameRate = rational<double>;
      47             : static constexpr const char DEVICE_DESKTOP[] = "desktop";
      48             : 
      49             : class VideoDeviceImpl;
      50             : 
      51             : class VideoDevice
      52             : {
      53             : public:
      54             :     VideoDevice(const std::string& path,
      55             :                 const std::vector<std::map<std::string, std::string>>& devInfo);
      56             :     ~VideoDevice();
      57             : 
      58             :     /*
      59             :      * The device name, e.g. "Integrated Camera",
      60             :      * actually used as the identifier.
      61             :      */
      62             :     std::string name {};
      63             : 
      64        2486 :     const std::string& getDeviceId() const { return id_; }
      65             : 
      66             :     /*
      67             :      * Get the 3 level deep tree of possible settings for the device.
      68             :      * The levels are channels, sizes, and rates.
      69             :      *
      70             :      * The result map for the "Integrated Camera" looks like this:
      71             :      *
      72             :      *   {'Camera 1': {'1280x720': ['10'],
      73             :      *                 '320x240': ['30', '15'],
      74             :      *                 '352x288': ['30', '15'],
      75             :      *                 '424x240': ['30', '15'],
      76             :      *                 '640x360': ['30', '15'],
      77             :      *                 '640x480': ['30', '15'],
      78             :      *                 '800x448': ['15'],
      79             :      *                 '960x540': ['10']}}
      80             :      */
      81           0 :     libjami::VideoCapabilities getCapabilities() const
      82             :     {
      83           0 :         libjami::VideoCapabilities cap;
      84             : 
      85           0 :         for (const auto& chan : getChannelList())
      86           0 :             for (const auto& size : getSizeList(chan)) {
      87           0 :                 std::string sz = fmt::format("{}x{}", size.first, size.second);
      88           0 :                 auto rates = getRateList(chan, size);
      89           0 :                 std::vector<std::string> rates_str {rates.size()};
      90           0 :                 std::transform(rates.begin(), rates.end(), rates_str.begin(), [](const FrameRate& r) {
      91           0 :                     return jami::to_string(r.real());
      92             :                 });
      93           0 :                 cap[chan][sz] = std::move(rates_str);
      94           0 :             }
      95             : 
      96           0 :         return cap;
      97           0 :     }
      98             : 
      99             :     /* Default setting is found by using following rules:
     100             :      * - frame height <= 640 pixels
     101             :      * - frame rate >= 10 fps
     102             :      */
     103          38 :     VideoSettings getDefaultSettings() const
     104             :     {
     105          38 :         auto settings = getSettings();
     106          38 :         auto channels = getChannelList();
     107          38 :         if (channels.empty())
     108           0 :             return {};
     109          38 :         settings.channel = getChannelList().front();
     110             : 
     111          38 :         VideoSize max_size {0, 0};
     112          38 :         FrameRate max_size_rate {0};
     113             : 
     114          38 :         auto sizes = getSizeList(settings.channel);
     115          76 :         for (auto& s : sizes) {
     116          38 :             if (s.second > 640)
     117           0 :                 continue;
     118          38 :             auto rates = getRateList(settings.channel, s);
     119          38 :             if (rates.empty())
     120           0 :                 continue;
     121          38 :             auto max_rate = *std::max_element(rates.begin(), rates.end());
     122          38 :             if (max_rate < 10)
     123           0 :                 continue;
     124          38 :             if (s.second > max_size.second
     125          38 :                 || (s.second == max_size.second && s.first > max_size.first)) {
     126           0 :                 max_size = s;
     127           0 :                 max_size_rate = max_rate;
     128             :             }
     129          38 :         }
     130          38 :         if (max_size.second > 0) {
     131           0 :             settings.video_size = fmt::format("{}x{}", max_size.first, max_size.second);
     132           0 :             settings.framerate = jami::to_string(max_size_rate.real());
     133           0 :             JAMI_WARN("Default video settings: %s, %s FPS",
     134             :                       settings.video_size.c_str(),
     135             :                       settings.framerate.c_str());
     136             :         }
     137             : 
     138          38 :         return settings;
     139          38 :     }
     140             : 
     141             :     /*
     142             :      * Get the settings for the device.
     143             :      */
     144          76 :     VideoSettings getSettings() const
     145             :     {
     146          76 :         auto params = getDeviceParams();
     147          76 :         VideoSettings settings;
     148          76 :         settings.name = name.empty() ? params.name : name;
     149          76 :         settings.unique_id = params.unique_id;
     150          76 :         settings.input = params.input;
     151          76 :         settings.channel = params.channel_name;
     152          76 :         settings.video_size = sizeToString(params.width, params.height);
     153          76 :         settings.framerate = jami::to_string(params.framerate.real());
     154         152 :         return settings;
     155          76 :     }
     156             : 
     157             :     /*
     158             :      * Setup the device with the preferences listed in the "settings" map.
     159             :      * The expected map should be similar to the result of getSettings().
     160             :      *
     161             :      * If a key is missing, a valid default value is choosen. Thus, calling
     162             :      * this function with an empty map will reset the device to default.
     163             :      */
     164          68 :     void applySettings(VideoSettings settings)
     165             :     {
     166          68 :         DeviceParams params {};
     167          68 :         params.name = settings.name;
     168          68 :         params.input = settings.input;
     169          68 :         params.unique_id = settings.unique_id;
     170          68 :         params.channel_name = settings.channel;
     171          68 :         auto size = sizeFromString(settings.channel, settings.video_size);
     172          68 :         params.width = size.first;
     173          68 :         params.height = size.second;
     174          68 :         params.framerate = rateFromString(settings.channel, size, settings.framerate);
     175          68 :         setDeviceParams(params);
     176          68 :     }
     177             : 
     178           0 :     void setOrientation(int orientation) { orientation_ = orientation; }
     179             : 
     180             :     /**
     181             :      * Returns the parameters needed for actual use of the device
     182             :      */
     183             :     DeviceParams getDeviceParams() const;
     184             :     std::vector<std::string> getChannelList() const;
     185             : 
     186             : private:
     187             :     std::vector<VideoSize> getSizeList(const std::string& channel) const;
     188             :     std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
     189             : 
     190          68 :     VideoSize sizeFromString(const std::string& channel, const std::string& size) const
     191             :     {
     192          68 :         auto size_list = getSizeList(channel);
     193          68 :         for (const auto& s : size_list) {
     194          68 :             if (sizeToString(s.first, s.second) == size)
     195          68 :                 return s;
     196             :         }
     197           0 :         return {0, 0};
     198          68 :     }
     199             : 
     200         144 :     std::string sizeToString(unsigned w, unsigned h) const
     201             :     {
     202         144 :         return fmt::format("{}x{}", w, h);
     203             :     }
     204             : 
     205          68 :     FrameRate rateFromString(const std::string& channel,
     206             :                              VideoSize size,
     207             :                              const std::string& rate) const
     208             :     {
     209          68 :         FrameRate closest {0};
     210          68 :         double rate_val = 0;
     211             :         try {
     212          68 :             rate_val = rate.empty() ? 0 : jami::stod(rate);
     213           0 :         } catch (...) {
     214           0 :             JAMI_WARN("Can't read framerate \"%s\"", rate.c_str());
     215           0 :         }
     216             :         // fallback to framerate closest to 30 FPS
     217          68 :         if (rate_val == 0)
     218           0 :             rate_val = 30;
     219          68 :         double closest_dist = std::numeric_limits<double>::max();
     220          68 :         auto rate_list = getRateList(channel, size);
     221         680 :         for (const auto& r : rate_list) {
     222         612 :             double dist = std::fabs(r.real() - rate_val);
     223         612 :             if (dist < closest_dist) {
     224         408 :                 closest = r;
     225         408 :                 closest_dist = dist;
     226             :             }
     227             :         }
     228          68 :         return closest;
     229          68 :     }
     230             : 
     231             :     void setDeviceParams(const DeviceParams&);
     232             : 
     233             :     /*
     234             :      * The device node, e.g. "046d082dF41A2B3F".
     235             :      */
     236             :     std::string id_ {};
     237             : 
     238             :     int orientation_ {0};
     239             : 
     240             :     /*
     241             :      * Device specific implementation.
     242             :      * On Linux, V4L2 stuffs go there.
     243             :      *
     244             :      * Note: since a VideoDevice is copyable,
     245             :      * deviceImpl_ cannot be an unique_ptr.
     246             :      */
     247             :     std::shared_ptr<VideoDeviceImpl> deviceImpl_;
     248             : };
     249             : 
     250             : } // namespace video
     251             : } // namespace jami

Generated by: LCOV version 1.14