LCOV - code coverage report
Current view: top level - src/client - videomanager.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 39.0 % 449 175
Test Date: 2026-11-09 10:41:14 Functions: 34.7 % 75 26

            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 "videomanager_interface.h"
      19              : #include "videomanager.h"
      20              : #include "localrecorder.h"
      21              : #include "localrecordermanager.h"
      22              : #include "libav_utils.h"
      23              : #include "video/video_input.h"
      24              : #include "video/video_device_monitor.h"
      25              : #include "logger.h"
      26              : #include "manager.h"
      27              : #ifdef ENABLE_VIDEO
      28              : #endif
      29              : #include "audio/ringbufferpool.h"
      30              : #include "jami/media_const.h"
      31              : #include "libav_utils.h"
      32              : 
      33              : #include <functional>
      34              : #include <memory>
      35              : #include <string>
      36              : #include <vector>
      37              : #include <new> // std::bad_alloc
      38              : #include <algorithm>
      39              : #include <cstdlib>
      40              : #include <cstring> // std::memset
      41              : 
      42              : extern "C" {
      43              : #include <libavutil/display.h>
      44              : }
      45              : 
      46              : namespace libjami {
      47              : 
      48        59117 : MediaFrame::MediaFrame()
      49        59117 :     : frame_ {av_frame_alloc()}
      50              : {
      51        59117 :     if (not frame_)
      52            0 :         throw std::bad_alloc();
      53        59117 : }
      54              : 
      55              : void
      56         8901 : MediaFrame::copyFrom(const MediaFrame& o)
      57              : {
      58         8901 :     reset();
      59         8901 :     if (o.frame_) {
      60         8901 :         av_frame_ref(frame_.get(), o.frame_.get());
      61         8901 :         av_frame_copy_props(frame_.get(), o.frame_.get());
      62              :     }
      63              : 
      64         8901 :     if (o.packet_) {
      65            0 :         packet_.reset(av_packet_alloc());
      66            0 :         av_packet_ref(packet_.get(), o.packet_.get());
      67              :     }
      68         8901 : }
      69              : 
      70              : void
      71         8961 : MediaFrame::reset() noexcept
      72              : {
      73         8961 :     if (frame_)
      74         8961 :         av_frame_unref(frame_.get());
      75         8961 :     packet_.reset();
      76         8961 : }
      77              : 
      78              : void
      79            0 : MediaFrame::setPacket(PacketBuffer&& pkt)
      80              : {
      81            0 :     packet_ = std::move(pkt);
      82            0 : }
      83              : 
      84        19926 : AudioFrame::AudioFrame(const jami::AudioFormat& format, size_t nb_samples)
      85        19926 :     : MediaFrame()
      86              : {
      87        19926 :     setFormat(format);
      88        19926 :     if (nb_samples)
      89            8 :         reserve(nb_samples);
      90        19926 : }
      91              : 
      92              : void
      93        19926 : AudioFrame::setFormat(const jami::AudioFormat& format)
      94              : {
      95        19926 :     auto* d = pointer();
      96        19926 :     av_channel_layout_default(&d->ch_layout, static_cast<int>(format.nb_channels));
      97        19926 :     d->sample_rate = static_cast<int>(format.sample_rate);
      98        19926 :     d->format = format.sampleFormat;
      99        19926 : }
     100              : 
     101              : jami::AudioFormat
     102            0 : AudioFrame::getFormat() const
     103              : {
     104            0 :     return {(unsigned) frame_->sample_rate, (unsigned) frame_->ch_layout.nb_channels, (AVSampleFormat) frame_->format};
     105              : }
     106              : 
     107              : size_t
     108            0 : AudioFrame::getFrameSize() const
     109              : {
     110            0 :     return frame_->nb_samples;
     111              : }
     112              : 
     113              : void
     114            8 : AudioFrame::reserve(size_t nb_samples)
     115              : {
     116            8 :     if (nb_samples != 0) {
     117            8 :         auto* d = pointer();
     118            8 :         d->nb_samples = static_cast<int>(nb_samples);
     119              :         int err;
     120            8 :         if ((err = av_frame_get_buffer(d, 0)) < 0) {
     121            0 :             throw std::bad_alloc();
     122              :         }
     123              :     }
     124            8 : }
     125              : 
     126              : void
     127            1 : AudioFrame::mix(const AudioFrame& frame)
     128              : {
     129            1 :     auto& f = *pointer();
     130            1 :     auto& fIn = *frame.pointer();
     131            1 :     if (f.ch_layout.nb_channels != fIn.ch_layout.nb_channels || f.format != fIn.format
     132            1 :         || f.sample_rate != fIn.sample_rate) {
     133            0 :         throw std::invalid_argument("Unable to mix frames with different formats");
     134              :     }
     135            1 :     if (f.nb_samples == 0) {
     136            0 :         reserve(fIn.nb_samples);
     137            0 :         jami::libav_utils::fillWithSilence(&f);
     138            1 :     } else if (f.nb_samples != fIn.nb_samples) {
     139            0 :         throw std::invalid_argument("Unable to mix frames with different length");
     140              :     }
     141            1 :     AVSampleFormat fmt = (AVSampleFormat) f.format;
     142            1 :     bool isPlanar = av_sample_fmt_is_planar(fmt);
     143            1 :     unsigned samplesPerChannel = isPlanar ? f.nb_samples : f.nb_samples * f.ch_layout.nb_channels;
     144            1 :     unsigned channels = isPlanar ? f.ch_layout.nb_channels : 1;
     145            1 :     if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P) {
     146            2 :         for (unsigned i = 0; i < channels; i++) {
     147            1 :             auto* c = (int16_t*) f.extended_data[i];
     148            1 :             auto* cIn = (int16_t*) fIn.extended_data[i];
     149         1921 :             for (unsigned s = 0; s < samplesPerChannel; s++) {
     150            0 :                 c[s] = static_cast<int16_t>(std::clamp((int32_t) c[s] + (int32_t) cIn[s],
     151         1920 :                                                        (int32_t) std::numeric_limits<int16_t>::min(),
     152         3840 :                                                        (int32_t) std::numeric_limits<int16_t>::max()));
     153              :             }
     154              :         }
     155            1 :     } else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP) {
     156            0 :         for (unsigned i = 0; i < channels; i++) {
     157            0 :             auto* c = (float*) f.extended_data[i];
     158            0 :             auto* cIn = (float*) fIn.extended_data[i];
     159            0 :             for (unsigned s = 0; s < samplesPerChannel; s++) {
     160            0 :                 c[s] += cIn[s];
     161              :             }
     162              :         }
     163            0 :     } else {
     164            0 :         throw std::invalid_argument(std::string("Unsupported format for mixing: ") + av_get_sample_fmt_name(fmt));
     165              :     }
     166            1 : }
     167              : 
     168              : float
     169            0 : AudioFrame::calcRMS() const
     170              : {
     171            0 :     double rms = 0.0;
     172            0 :     auto fmt = static_cast<AVSampleFormat>(frame_->format);
     173            0 :     bool planar = av_sample_fmt_is_planar(fmt);
     174            0 :     int perChannel = planar ? frame_->nb_samples : frame_->nb_samples * frame_->ch_layout.nb_channels;
     175            0 :     int channels = planar ? frame_->ch_layout.nb_channels : 1;
     176            0 :     if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P) {
     177            0 :         for (int c = 0; c < channels; ++c) {
     178            0 :             auto* buf = reinterpret_cast<int16_t*>(frame_->extended_data[c]);
     179            0 :             for (int i = 0; i < perChannel; ++i) {
     180            0 :                 auto sample = static_cast<float>(buf[i]) * 0.000030517578125f;
     181            0 :                 rms += sample * sample;
     182              :             }
     183              :         }
     184            0 :     } else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP) {
     185            0 :         for (int c = 0; c < channels; ++c) {
     186            0 :             auto* buf = reinterpret_cast<float*>(frame_->extended_data[c]);
     187            0 :             for (int i = 0; i < perChannel; ++i) {
     188            0 :                 rms += buf[i] * buf[i];
     189              :             }
     190              :         }
     191            0 :     } else {
     192              :         // Should not happen
     193            0 :         JAMI_ERROR("Unsupported format for getting volume level: {}", av_get_sample_fmt_name(fmt));
     194            0 :         return 0.0;
     195              :     }
     196              :     // divide by the number of multi-byte samples
     197            0 :     return static_cast<float>(sqrt(rms / (frame_->nb_samples * frame_->ch_layout.nb_channels)));
     198              : }
     199              : 
     200              : #ifdef ENABLE_VIDEO
     201              : 
     202        54790 : VideoFrame::~VideoFrame()
     203              : {
     204        39019 :     if (releaseBufferCb_)
     205            0 :         releaseBufferCb_(ptr_);
     206        54790 : }
     207              : 
     208              : void
     209         8961 : VideoFrame::reset() noexcept
     210              : {
     211         8961 :     MediaFrame::reset();
     212         8961 :     allocated_ = false;
     213         8961 :     releaseBufferCb_ = {};
     214         8961 : }
     215              : 
     216              : void
     217         8901 : VideoFrame::copyFrom(const VideoFrame& o)
     218              : {
     219         8901 :     MediaFrame::copyFrom(o);
     220         8901 :     ptr_ = o.ptr_;
     221         8901 :     allocated_ = o.allocated_;
     222         8901 : }
     223              : 
     224              : size_t
     225            0 : VideoFrame::size() const noexcept
     226              : {
     227            0 :     return av_image_get_buffer_size((AVPixelFormat) frame_->format, frame_->width, frame_->height, 1);
     228              : }
     229              : 
     230              : int
     231        35605 : VideoFrame::format() const noexcept
     232              : {
     233        35605 :     return frame_->format;
     234              : }
     235              : 
     236              : int
     237        36341 : VideoFrame::width() const noexcept
     238              : {
     239        36341 :     return frame_->width;
     240              : }
     241              : 
     242              : int
     243        50554 : VideoFrame::height() const noexcept
     244              : {
     245        50554 :     return frame_->height;
     246              : }
     247              : 
     248              : void
     249        30051 : VideoFrame::setGeometry(int format, int width, int height) noexcept
     250              : {
     251        30051 :     frame_->format = format;
     252        30051 :     frame_->width = width;
     253        30051 :     frame_->height = height;
     254        30051 : }
     255              : 
     256              : void
     257        29991 : VideoFrame::reserve(int format, int width, int height)
     258              : {
     259        29991 :     auto* libav_frame = frame_.get();
     260              : 
     261        29991 :     if (allocated_) {
     262              :         // nothing to do if same properties
     263            0 :         if (width == libav_frame->width and height == libav_frame->height and format == libav_frame->format)
     264            0 :             av_frame_unref(libav_frame);
     265              :     }
     266              : 
     267        29991 :     setGeometry(format, width, height);
     268        29991 :     if (av_frame_get_buffer(libav_frame, 32))
     269            0 :         throw std::bad_alloc();
     270        29991 :     allocated_ = true;
     271        29991 :     releaseBufferCb_ = {};
     272        29991 : }
     273              : 
     274              : void
     275           60 : VideoFrame::setFromMemory(uint8_t* ptr, int format, int width, int height) noexcept
     276              : {
     277           60 :     reset();
     278           60 :     setGeometry(format, width, height);
     279           60 :     if (not ptr)
     280            0 :         return;
     281           60 :     av_image_fill_arrays(frame_->data,
     282           60 :                          frame_->linesize,
     283              :                          (uint8_t*) ptr,
     284           60 :                          (AVPixelFormat) frame_->format,
     285              :                          width,
     286              :                          height,
     287              :                          1);
     288              : }
     289              : 
     290              : void
     291            0 : VideoFrame::setFromMemory(
     292              :     uint8_t* ptr, int format, int width, int height, const std::function<void(uint8_t*)>& cb) noexcept
     293              : {
     294            0 :     setFromMemory(ptr, format, width, height);
     295            0 :     if (cb) {
     296            0 :         releaseBufferCb_ = cb;
     297            0 :         ptr_ = ptr;
     298              :     }
     299            0 : }
     300              : 
     301              : void
     302            0 : VideoFrame::setReleaseCb(const std::function<void(uint8_t*)>& cb) noexcept
     303              : {
     304            0 :     if (cb) {
     305            0 :         releaseBufferCb_ = cb;
     306              :     }
     307            0 : }
     308              : 
     309              : void
     310            0 : VideoFrame::noise()
     311              : {
     312            0 :     auto* f = frame_.get();
     313            0 :     if (f->data[0] == nullptr)
     314            0 :         return;
     315            0 :     for (std::size_t i = 0; i < size(); ++i) {
     316            0 :         f->data[0][i] = std::rand() & 255;
     317              :     }
     318              : }
     319              : 
     320              : int
     321        23849 : VideoFrame::getOrientation() const
     322              : {
     323        23849 :     int32_t* matrix {nullptr};
     324        23849 :     if (auto* p = packet()) {
     325            0 :         matrix = reinterpret_cast<int32_t*>(av_packet_get_side_data(p, AV_PKT_DATA_DISPLAYMATRIX, nullptr));
     326        23849 :     } else if (const auto* p = pointer()) {
     327        23849 :         if (AVFrameSideData* side_data = av_frame_get_side_data(p, AV_FRAME_DATA_DISPLAYMATRIX)) {
     328            0 :             matrix = reinterpret_cast<int32_t*>(side_data->data);
     329              :         }
     330              :     }
     331        23849 :     if (matrix) {
     332            0 :         double angle = av_display_rotation_get(matrix);
     333            0 :         return std::isnan(angle) ? 0 : -(int) angle;
     334              :     }
     335        23849 :     return 0;
     336              : }
     337              : 
     338              : VideoFrame*
     339            0 : getNewFrame(std::string_view id)
     340              : {
     341            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     342            0 :         if (auto input = vm->getVideoInput(id))
     343            0 :             return &input->getNewFrame();
     344            0 :     JAMI_WARNING("getNewFrame: Unable to find input {}", id);
     345            0 :     return nullptr;
     346              : }
     347              : 
     348              : void
     349            0 : publishFrame(std::string_view id)
     350              : {
     351            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     352            0 :         if (auto input = vm->getVideoInput(id))
     353            0 :             input->publishFrame();
     354            0 : }
     355              : 
     356              : void
     357            0 : registerVideoHandlers(const std::map<std::string, std::shared_ptr<CallbackWrapperBase>>& handlers)
     358              : {
     359            0 :     registerSignalHandlers(handlers);
     360            0 : }
     361              : 
     362              : std::vector<std::string>
     363            0 : getDeviceList()
     364              : {
     365            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     366            0 :         return vm->videoDeviceMonitor.getDeviceList();
     367            0 :     return {};
     368              : }
     369              : 
     370              : VideoCapabilities
     371            0 : getCapabilities(const std::string& deviceId)
     372              : {
     373            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     374            0 :         return vm->videoDeviceMonitor.getCapabilities(deviceId);
     375            0 :     return {};
     376              : }
     377              : 
     378              : std::string
     379            0 : getDefaultDevice()
     380              : {
     381            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     382            0 :         return vm->videoDeviceMonitor.getDefaultDevice();
     383            0 :     return {};
     384              : }
     385              : 
     386              : void
     387            0 : setDefaultDevice(const std::string& deviceId)
     388              : {
     389            0 :     JAMI_LOG("Setting default device to {}", deviceId);
     390            0 :     if (auto* vm = jami::Manager::instance().getVideoManager()) {
     391            0 :         if (vm->videoDeviceMonitor.setDefaultDevice(deviceId))
     392            0 :             jami::Manager::instance().saveConfig();
     393              :     }
     394            0 : }
     395              : 
     396              : void
     397            0 : setDeviceOrientation(const std::string& deviceId, int angle)
     398              : {
     399            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     400            0 :         vm->setDeviceOrientation(deviceId, angle);
     401            0 : }
     402              : 
     403              : std::map<std::string, std::string>
     404            0 : getDeviceParams(const std::string& deviceId)
     405              : {
     406            0 :     if (auto* vm = jami::Manager::instance().getVideoManager()) {
     407            0 :         auto params = vm->videoDeviceMonitor.getDeviceParams(deviceId);
     408              :         return {{"format", params.format},
     409            0 :                 {"width", std::to_string(params.width)},
     410            0 :                 {"height", std::to_string(params.height)},
     411            0 :                 {"rate", params.framerate.to_string()}};
     412            0 :     }
     413            0 :     return {};
     414            0 : }
     415              : 
     416              : std::map<std::string, std::string>
     417            0 : getSettings(const std::string& deviceId)
     418              : {
     419            0 :     if (auto* vm = jami::Manager::instance().getVideoManager()) {
     420            0 :         return vm->videoDeviceMonitor.getSettings(deviceId).to_map();
     421              :     }
     422            0 :     return {};
     423              : }
     424              : 
     425              : void
     426            0 : applySettings(const std::string& deviceId, const std::map<std::string, std::string>& settings)
     427              : {
     428            0 :     if (auto* vm = jami::Manager::instance().getVideoManager()) {
     429            0 :         vm->videoDeviceMonitor.applySettings(deviceId, settings);
     430            0 :         jami::Manager::instance().saveConfig();
     431              :     }
     432            0 : }
     433              : 
     434              : std::string
     435            0 : openVideoInput(const std::string& path)
     436              : {
     437            0 :     if (auto* vm = jami::Manager::instance().getVideoManager()) {
     438            0 :         auto id = path.empty() ? vm->videoDeviceMonitor.getMRLForDefaultDevice() : path;
     439            0 :         auto& input = vm->clientVideoInputs[id];
     440            0 :         if (not input) {
     441            0 :             input = jami::getVideoInput(id);
     442              :         }
     443            0 :         return id;
     444            0 :     }
     445            0 :     return {};
     446              : }
     447              : 
     448              : bool
     449            0 : closeVideoInput(const std::string& id)
     450              : {
     451            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     452            0 :         return vm->clientVideoInputs.erase(id) > 0;
     453            0 :     return false;
     454              : }
     455              : #endif
     456              : 
     457              : void
     458            0 : startAudioDevice()
     459              : {
     460            0 :     if (auto newPreview = jami::getAudioInput(jami::RingBufferPool::DEFAULT_ID)) {
     461            0 :         if (auto* vm = jami::Manager::instance().getVideoManager())
     462            0 :             vm->audioPreview = newPreview;
     463            0 :         newPreview->switchInput("");
     464            0 :     }
     465            0 : }
     466              : 
     467              : void
     468            0 : stopAudioDevice()
     469              : {
     470            0 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     471            0 :         vm->audioPreview.reset();
     472            0 : }
     473              : 
     474              : std::string
     475            0 : startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath)
     476              : {
     477            0 :     auto rec = std::make_unique<jami::LocalRecorder>(videoInputId);
     478            0 :     rec->setPath(filepath);
     479              : 
     480              :     // retrieve final path (containing file extension)
     481            0 :     auto path = rec->getPath();
     482              : 
     483            0 :     auto& recordManager = jami::LocalRecorderManager::instance();
     484              : 
     485              :     try {
     486            0 :         recordManager.insertRecorder(path, std::move(rec));
     487            0 :     } catch (const std::invalid_argument&) {
     488            0 :         return "";
     489            0 :     }
     490              : 
     491            0 :     auto ret = recordManager.getRecorderByPath(path)->start();
     492            0 :     if (!ret) {
     493            0 :         recordManager.removeRecorderByPath(filepath);
     494            0 :         return "";
     495              :     }
     496              : 
     497            0 :     return path;
     498            0 : }
     499              : 
     500              : void
     501            0 : stopLocalRecorder(const std::string& filepath)
     502              : {
     503            0 :     jami::LocalRecorder* rec = jami::LocalRecorderManager::instance().getRecorderByPath(filepath);
     504            0 :     if (!rec) {
     505            0 :         JAMI_WARNING("Unable to stop non existing local recorder.");
     506            0 :         return;
     507              :     }
     508              : 
     509            0 :     rec->stopRecording();
     510            0 :     jami::LocalRecorderManager::instance().removeRecorderByPath(filepath);
     511              : }
     512              : 
     513              : bool
     514            0 : registerSinkTarget(const std::string& sinkId, SinkTarget target)
     515              : {
     516              : #ifdef ENABLE_VIDEO
     517            0 :     if (auto sink = jami::Manager::instance().getSinkClient(sinkId)) {
     518            0 :         sink->registerTarget(std::move(target));
     519            0 :         return true;
     520              :     } else
     521            0 :         JAMI_WARNING("No sink found for id '{}'", sinkId);
     522              : #endif
     523            0 :     return false;
     524              : }
     525              : 
     526              : #ifdef ENABLE_SHM
     527              : void
     528            0 : startShmSink(const std::string& sinkId, bool value)
     529              : {
     530              : #ifdef ENABLE_VIDEO
     531            0 :     if (auto sink = jami::Manager::instance().getSinkClient(sinkId))
     532            0 :         sink->enableShm(value);
     533              :     else
     534            0 :         JAMI_WARNING("No sink found for id '{}'", sinkId);
     535              : #endif
     536            0 : }
     537              : #endif
     538              : 
     539              : std::map<std::string, std::string>
     540            0 : getRenderer(const std::string& callId)
     541              : {
     542              : #ifdef ENABLE_VIDEO
     543            0 :     if (auto sink = jami::Manager::instance().getSinkClient(callId))
     544              :         return {
     545              :             {libjami::Media::Details::CALL_ID, callId},
     546            0 :             {libjami::Media::Details::SHM_PATH, sink->openedName()},
     547            0 :             {libjami::Media::Details::WIDTH, std::to_string(sink->getWidth())},
     548            0 :             {libjami::Media::Details::HEIGHT, std::to_string(sink->getHeight())},
     549            0 :         };
     550              :     else
     551              : #endif
     552              :         return {
     553              :             {libjami::Media::Details::CALL_ID, callId},
     554              :             {libjami::Media::Details::SHM_PATH, ""},
     555              :             {libjami::Media::Details::WIDTH, "0"},
     556              :             {libjami::Media::Details::HEIGHT, "0"},
     557            0 :         };
     558            0 : }
     559              : 
     560              : std::string
     561            0 : createMediaPlayer(const std::string& path)
     562              : {
     563            0 :     return jami::createMediaPlayer(path);
     564              : }
     565              : 
     566              : bool
     567            0 : closeMediaPlayer(const std::string& id)
     568              : {
     569            0 :     return jami::closeMediaPlayer(id);
     570              : }
     571              : 
     572              : bool
     573            0 : pausePlayer(const std::string& id, const bool& pause)
     574              : {
     575            0 :     return jami::pausePlayer(id, pause);
     576              : }
     577              : 
     578              : bool
     579            0 : mutePlayerAudio(const std::string& id, const bool& mute)
     580              : {
     581            0 :     return jami::mutePlayerAudio(id, mute);
     582              : }
     583              : 
     584              : bool
     585            0 : playerSeekToTime(const std::string& id, const int& time)
     586              : {
     587            0 :     return jami::playerSeekToTime(id, time);
     588              : }
     589              : 
     590              : int64_t
     591            0 : getPlayerPosition(const std::string& id)
     592              : {
     593            0 :     return jami::getPlayerPosition(id);
     594              : }
     595              : 
     596              : int64_t
     597            0 : getPlayerDuration(const std::string& id)
     598              : {
     599            0 :     return jami::getPlayerDuration(id);
     600              : }
     601              : 
     602              : void
     603            0 : setAutoRestart(const std::string& id, const bool& restart)
     604              : {
     605            0 :     jami::setAutoRestart(id, restart);
     606            0 : }
     607              : 
     608              : MediaPreview
     609            0 : getMediaPreview(const std::string& path)
     610              : {
     611            0 :     return jami::getMediaPreview(path);
     612              : }
     613              : 
     614              : bool
     615            0 : getDecodingAccelerated()
     616              : {
     617              : #ifdef ENABLE_HWACCEL
     618            0 :     return jami::Manager::instance().videoPreferences.getDecodingAccelerated();
     619              : #else
     620              :     return false;
     621              : #endif
     622              : }
     623              : 
     624              : void
     625            0 : setDecodingAccelerated(bool state)
     626              : {
     627              : #ifdef ENABLE_HWACCEL
     628            0 :     JAMI_LOG("{} hardware acceleration", (state ? "Enabling" : "Disabling"));
     629            0 :     if (jami::Manager::instance().videoPreferences.setDecodingAccelerated(state))
     630            0 :         jami::Manager::instance().saveConfig();
     631              : #endif
     632            0 : }
     633              : 
     634              : bool
     635            0 : getEncodingAccelerated()
     636              : {
     637              : #ifdef ENABLE_HWACCEL
     638            0 :     return jami::Manager::instance().videoPreferences.getEncodingAccelerated();
     639              : #else
     640              :     return false;
     641              : #endif
     642              : }
     643              : 
     644              : void
     645            0 : setEncodingAccelerated(bool state)
     646              : {
     647              : #ifdef ENABLE_HWACCEL
     648            0 :     JAMI_LOG("{} hardware acceleration", (state ? "Enabling" : "Disabling"));
     649            0 :     if (jami::Manager::instance().videoPreferences.setEncodingAccelerated(state))
     650            0 :         jami::Manager::instance().saveConfig();
     651              :     else
     652            0 :         return;
     653              : #endif
     654            0 :     for (const auto& acc : jami::Manager::instance().getAllAccounts()) {
     655            0 :         if (state)
     656            0 :             acc->setCodecActive(AV_CODEC_ID_HEVC);
     657              :         else
     658            0 :             acc->setCodecInactive(AV_CODEC_ID_HEVC);
     659              :         // Update and sort codecs
     660            0 :         acc->setActiveCodecs(acc->getActiveCodecs());
     661            0 :         jami::Manager::instance().saveConfig(acc);
     662            0 :     }
     663              : }
     664              : 
     665              : #if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS)
     666              : void
     667              : addVideoDevice(const std::string& node, const std::vector<std::map<std::string, std::string>>& devInfo)
     668              : {
     669              :     if (auto videoManager = jami::Manager::instance().getVideoManager()) {
     670              :         videoManager->videoDeviceMonitor.addDevice(node, devInfo);
     671              :     }
     672              : }
     673              : 
     674              : void
     675              : removeVideoDevice(const std::string& node)
     676              : {
     677              :     if (auto videoManager = jami::Manager::instance().getVideoManager()) {
     678              :         videoManager->videoDeviceMonitor.removeDevice(node);
     679              :     }
     680              : }
     681              : #endif
     682              : 
     683              : } // namespace libjami
     684              : 
     685              : namespace jami {
     686              : 
     687              : #ifdef ENABLE_VIDEO
     688              : // How long getVideoInput() waits for a VideoInput that is being destroyed to
     689              : // release its capture device before giving up and creating a new one anyway.
     690              : static constexpr auto VIDEO_INPUT_RELEASE_TIMEOUT = std::chrono::seconds(10);
     691              : 
     692              : video::VideoDeviceMonitor*
     693         3580 : getVideoDeviceMonitor()
     694              : {
     695         3580 :     if (auto* vm = jami::Manager::instance().getVideoManager())
     696         3574 :         return &vm->videoDeviceMonitor;
     697            6 :     return {};
     698              : }
     699              : 
     700              : std::shared_ptr<video::VideoInput>
     701          173 : getVideoInput(const std::string& resource, video::VideoInputMode inputMode, const std::string& sink)
     702              : {
     703          173 :     auto sinkId = sink.empty() ? resource : sink;
     704          173 :     auto* vmgr = Manager::instance().getVideoManager();
     705          173 :     if (!vmgr)
     706            0 :         return {};
     707          173 :     std::unique_lock<std::mutex> lk(vmgr->videoMutex);
     708              : 
     709              :     // A registered entry whose weak pointer has expired means that a VideoInput
     710              :     // for this sink is still being destroyed: its capture thread may be stuck in
     711              :     // a blocking driver call and it therefore still holds the device open.
     712              :     // Creating a new one right away would open the capture device a second time,
     713              :     // which wedges some webcams badly enough that they drop off the USB bus.
     714              :     // Wait for the teardown to complete instead. The deadline is computed once so
     715              :     // that notifications for other sinks, which share this condition variable,
     716              :     // don't restart the timeout.
     717          173 :     std::shared_ptr<video::VideoInput> current;
     718          173 :     const auto deadline = std::chrono::steady_clock::now() + VIDEO_INPUT_RELEASE_TIMEOUT;
     719          173 :     const auto released = vmgr->videoInputsCv.wait_until(lk, deadline, [&] {
     720          173 :         current.reset();
     721          173 :         auto it = vmgr->videoInputs.find(sinkId);
     722          173 :         if (it == vmgr->videoInputs.end())
     723           75 :             return true;
     724           98 :         current = it->second.input.lock();
     725           98 :         return current != nullptr;
     726              :     });
     727          173 :     if (current)
     728           98 :         return current;
     729           75 :     if (!released)
     730            0 :         JAMI_WARNING("Timed out waiting for the previous input on {} to be released", sinkId);
     731              : 
     732              :     // Unregister the input only once it is really destroyed, so that a concurrent
     733              :     // getVideoInput() waits for the capture device to be released. The generation
     734              :     // guards against a deleter that ran late erasing a newer entry: matching it
     735              :     // proves the registered entry is still the one this instance created.
     736           75 :     const auto generation = ++vmgr->nextVideoInputGeneration;
     737           75 :     auto unregister = [sinkId, generation](video::VideoInput* input) {
     738           75 :         delete input;
     739           75 :         auto* vmgr = Manager::instance().getVideoManager();
     740           75 :         if (!vmgr)
     741            0 :             return;
     742              :         {
     743           75 :             std::lock_guard<std::mutex> lk(vmgr->videoMutex);
     744           75 :             auto it = vmgr->videoInputs.find(sinkId);
     745           75 :             if (it != vmgr->videoInputs.end() && it->second.generation == generation)
     746           75 :                 vmgr->videoInputs.erase(it);
     747           75 :         }
     748           75 :         vmgr->videoInputsCv.notify_all();
     749           75 :     };
     750           75 :     auto* raw = new video::VideoInput(inputMode, resource, sinkId);
     751           75 :     auto input = std::shared_ptr<video::VideoInput>(raw, std::move(unregister));
     752           75 :     vmgr->videoInputs[sinkId] = {input, generation};
     753           75 :     return input;
     754          248 : }
     755              : 
     756              : void
     757            0 : VideoManager::setDeviceOrientation(const std::string& deviceId, int angle)
     758              : {
     759            0 :     videoDeviceMonitor.setDeviceOrientation(deviceId, angle);
     760            0 : }
     761              : #endif
     762              : 
     763              : std::shared_ptr<AudioInput>
     764          348 : getAudioInput(const std::string& device)
     765              : {
     766          348 :     auto* vmgr = Manager::instance().getVideoManager();
     767          348 :     if (!vmgr)
     768            0 :         return {};
     769          348 :     std::lock_guard<std::mutex> lk(vmgr->audioMutex);
     770              : 
     771              :     // erase expired audio inputs
     772         1456 :     for (auto it = vmgr->audioInputs.cbegin(); it != vmgr->audioInputs.cend();) {
     773         1108 :         if (it->second.expired())
     774          272 :             it = vmgr->audioInputs.erase(it);
     775              :         else
     776          836 :             ++it;
     777              :     }
     778              : 
     779          348 :     auto it = vmgr->audioInputs.find(device);
     780          348 :     if (it != vmgr->audioInputs.end()) {
     781           45 :         if (auto input = it->second.lock()) {
     782           45 :             return input;
     783           45 :         }
     784              :     }
     785              : 
     786          303 :     auto input = std::make_shared<AudioInput>(device);
     787          303 :     vmgr->audioInputs[device] = input;
     788          303 :     return input;
     789          348 : }
     790              : 
     791              : bool
     792            0 : VideoManager::hasRunningPlayers()
     793              : {
     794            0 :     if (auto* vmgr = Manager::instance().getVideoManager()) {
     795            0 :         std::scoped_lock<std::mutex> lk(vmgr->mediaPlayersMutex);
     796            0 :         return !vmgr->mediaPlayers.empty();
     797            0 :     }
     798            0 :     return false;
     799              : }
     800              : 
     801              : std::shared_ptr<MediaPlayer>
     802           11 : getMediaPlayer(const std::string& id)
     803              : {
     804           11 :     if (auto* vmgr = Manager::instance().getVideoManager()) {
     805           11 :         std::scoped_lock<std::mutex> lk(vmgr->mediaPlayersMutex);
     806           11 :         auto it = vmgr->mediaPlayers.find(id);
     807           11 :         if (it != vmgr->mediaPlayers.end()) {
     808           11 :             return it->second;
     809              :         }
     810           11 :     }
     811            0 :     return {};
     812              : }
     813              : 
     814              : std::string
     815           14 : createMediaPlayer(const std::string& path)
     816              : {
     817           14 :     if (auto* vmgr = Manager::instance().getVideoManager()) {
     818           14 :         std::scoped_lock<std::mutex> lk(vmgr->mediaPlayersMutex);
     819           14 :         auto& player = vmgr->mediaPlayers[path];
     820           14 :         if (!player) {
     821           11 :             player = std::make_shared<MediaPlayer>(path);
     822              :         }
     823           14 :         return path;
     824           14 :     }
     825            0 :     return {};
     826              : }
     827              : 
     828              : bool
     829            0 : pausePlayer(const std::string& id, bool pause)
     830              : {
     831            0 :     if (auto player = getMediaPlayer(id)) {
     832            0 :         player->pause(pause);
     833            0 :         return true;
     834            0 :     }
     835            0 :     return false;
     836              : }
     837              : 
     838              : bool
     839          601 : closeMediaPlayer(const std::string& id)
     840              : {
     841          601 :     if (auto* vm = Manager::instance().getVideoManager()) {
     842          601 :         std::scoped_lock<std::mutex> lk(vm->mediaPlayersMutex);
     843          601 :         return vm->mediaPlayers.erase(id) > 0;
     844          601 :     }
     845            0 :     return false;
     846              : }
     847              : 
     848              : bool
     849            0 : mutePlayerAudio(const std::string& id, bool mute)
     850              : {
     851            0 :     if (auto player = getMediaPlayer(id)) {
     852            0 :         player->muteAudio(mute);
     853            0 :         return true;
     854            0 :     }
     855            0 :     return false;
     856              : }
     857              : 
     858              : bool
     859            0 : playerSeekToTime(const std::string& id, int time)
     860              : {
     861            0 :     if (auto player = getMediaPlayer(id))
     862            0 :         return player->seekToTime(time);
     863            0 :     return false;
     864              : }
     865              : 
     866              : int64_t
     867            0 : getPlayerPosition(const std::string& id)
     868              : {
     869            0 :     if (auto player = getMediaPlayer(id))
     870            0 :         return player->getPlayerPosition();
     871            0 :     return -1;
     872              : }
     873              : 
     874              : int64_t
     875            0 : getPlayerDuration(const std::string& id)
     876              : {
     877            0 :     if (auto player = getMediaPlayer(id))
     878            0 :         return player->getPlayerDuration();
     879            0 :     return -1;
     880              : }
     881              : 
     882              : void
     883            0 : setAutoRestart(const std::string& id, bool restart)
     884              : {
     885            0 :     if (auto player = getMediaPlayer(id))
     886            0 :         player->setAutoRestart(restart);
     887            0 : }
     888              : 
     889              : } // namespace jami
        

Generated by: LCOV version 2.0-1