LCOV - code coverage report
Current view: top level - src/media/video - video_mixer.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 83.3 % 36 30
Test Date: 2026-06-13 09:18:46 Functions: 88.9 % 9 8

            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 "noncopyable.h"
      20              : #include "video_base.h"
      21              : #include "video_scaler.h"
      22              : #include "threadloop.h"
      23              : #include "media_stream.h"
      24              : 
      25              : #include <list>
      26              : #include <chrono>
      27              : #include <memory>
      28              : #include <shared_mutex>
      29              : 
      30              : namespace jami {
      31              : namespace video {
      32              : 
      33              : class SinkClient;
      34              : 
      35              : struct StreamInfo
      36              : {
      37              :     std::string callId;
      38              :     std::string streamId;
      39              : };
      40              : 
      41              : struct SourceInfo
      42              : {
      43              :     Observable<std::shared_ptr<MediaFrame>>* source;
      44              :     int x;
      45              :     int y;
      46              :     int w;
      47              :     int h;
      48              :     bool hasVideo;
      49              :     std::string callId;
      50              :     std::string streamId;
      51              : };
      52              : using OnSourcesUpdatedCb = std::function<void(std::vector<SourceInfo>&&)>;
      53              : 
      54              : enum class Layout : uint8_t { GRID, ONE_BIG_WITH_SMALL, ONE_BIG };
      55              : 
      56              : class VideoMixer : public VideoGenerator, public VideoFramePassiveReader
      57              : {
      58              : public:
      59              :     VideoMixer(const std::string& id, const std::string& localInput = {}, bool attachHost = false);
      60              :     ~VideoMixer();
      61              : 
      62              :     void setParameters(int width, int height, AVPixelFormat format = AV_PIX_FMT_YUV422P);
      63              : 
      64              :     int getWidth() const override;
      65              :     int getHeight() const override;
      66              :     AVPixelFormat getPixelFormat() const override;
      67              : 
      68              :     // as VideoFramePassiveReader
      69              :     void update(Observable<std::shared_ptr<MediaFrame>>* ob, const std::shared_ptr<MediaFrame>& v) override;
      70              :     void attached(Observable<std::shared_ptr<MediaFrame>>* ob) override;
      71              :     void detached(Observable<std::shared_ptr<MediaFrame>>* ob) override;
      72              : 
      73              :     /**
      74              :      * Set all inputs at once
      75              :      * @param inputs        New inputs
      76              :      * @note previous inputs will be stopped
      77              :      */
      78              :     void switchInputs(const std::vector<std::string>& inputs);
      79              :     /**
      80              :      * Stop all inputs
      81              :      */
      82              :     void stopInputs();
      83              : 
      84              :     void setActiveStream(const std::string& id);
      85            1 :     void resetActiveStream()
      86              :     {
      87            1 :         activeStream_ = {};
      88            1 :         updateLayout();
      89            1 :     }
      90              : 
      91        10203 :     bool verifyActive(const std::string& id) { return activeStream_ == id; }
      92              : 
      93            0 :     void setVideoLayout(Layout newLayout)
      94              :     {
      95            0 :         currentLayout_ = newLayout;
      96            0 :         if (currentLayout_ == Layout::GRID)
      97            0 :             resetActiveStream();
      98            0 :         layoutUpdated_ += 1;
      99            0 :     }
     100              : 
     101              :     Layout getVideoLayout() const { return currentLayout_; }
     102              : 
     103           37 :     void setOnSourcesUpdated(OnSourcesUpdatedCb&& cb) { onSourcesUpdated_ = std::move(cb); }
     104              : 
     105              :     MediaStream getStream(const std::string& name) const;
     106              : 
     107           68 :     std::shared_ptr<VideoFrameActiveWriter> getVideoLocal() const
     108              :     {
     109           68 :         if (!localInputs_.empty())
     110           51 :             return *localInputs_.begin();
     111           17 :         return {};
     112              :     }
     113              : 
     114              :     void updateLayout();
     115              : 
     116          193 :     std::shared_ptr<SinkClient>& getSink() { return sink_; }
     117              : 
     118           23 :     void addAudioOnlySource(const std::string& callId, const std::string& streamId)
     119              :     {
     120           23 :         std::unique_lock lk(audioOnlySourcesMtx_);
     121           23 :         audioOnlySources_.insert({callId, streamId});
     122           23 :         lk.unlock();
     123           23 :         updateLayout();
     124           23 :     }
     125              : 
     126          102 :     void removeAudioOnlySource(const std::string& callId, const std::string& streamId)
     127              :     {
     128          102 :         std::unique_lock lk(audioOnlySourcesMtx_);
     129          102 :         if (audioOnlySources_.erase({callId, streamId})) {
     130           14 :             lk.unlock();
     131           14 :             updateLayout();
     132              :         }
     133          102 :     }
     134              : 
     135              :     void attachVideo(Observable<std::shared_ptr<MediaFrame>>* frame,
     136              :                      const std::string& callId,
     137              :                      const std::string& streamId);
     138              :     void detachVideo(Observable<std::shared_ptr<MediaFrame>>* frame);
     139              : 
     140         9068 :     StreamInfo streamInfo(Observable<std::shared_ptr<MediaFrame>>* frame) const
     141              :     {
     142         9068 :         std::lock_guard lk(videoToStreamInfoMtx_);
     143         9068 :         auto it = videoToStreamInfo_.find(frame);
     144         9068 :         if (it == videoToStreamInfo_.end())
     145           11 :             return {};
     146         9057 :         return it->second;
     147         9061 :     }
     148              : 
     149              : private:
     150              :     NON_COPYABLE(VideoMixer);
     151              :     struct VideoMixerSource;
     152              : 
     153              :     bool render_frame(VideoFrame& output,
     154              :                       const std::shared_ptr<VideoFrame>& input,
     155              :                       std::unique_ptr<VideoMixerSource>& source);
     156              : 
     157              :     void calc_position(std::unique_ptr<VideoMixerSource>& source, const std::shared_ptr<VideoFrame>& input, int index);
     158              : 
     159              :     void startSink();
     160              :     void stopSink();
     161              : 
     162              :     void process();
     163              : 
     164              :     const std::string id_;
     165              :     int width_ = 0;
     166              :     int height_ = 0;
     167              :     AVPixelFormat format_ = AV_PIX_FMT_YUV422P;
     168              :     std::shared_mutex rwMutex_;
     169              : 
     170              :     std::shared_ptr<SinkClient> sink_;
     171              : 
     172              :     std::chrono::time_point<std::chrono::steady_clock> nextProcess_;
     173              :     std::mutex localInputsMtx_;
     174              :     std::vector<std::shared_ptr<VideoFrameActiveWriter>> localInputs_ {};
     175              :     void stopInput(const std::shared_ptr<VideoFrameActiveWriter>& input);
     176              : 
     177              :     VideoScaler scaler_;
     178              : 
     179              :     ThreadLoop loop_; // as to be last member
     180              : 
     181              :     Layout currentLayout_ {Layout::GRID};
     182              :     std::list<std::unique_ptr<VideoMixerSource>> sources_;
     183              : 
     184              :     // We need to convert call to frame
     185              :     mutable std::mutex videoToStreamInfoMtx_ {};
     186              :     std::map<Observable<std::shared_ptr<MediaFrame>>*, StreamInfo> videoToStreamInfo_ {};
     187              : 
     188              :     std::mutex audioOnlySourcesMtx_;
     189              :     std::set<std::pair<std::string, std::string>> audioOnlySources_;
     190              :     std::string activeStream_ {};
     191              : 
     192              :     std::atomic_int layoutUpdated_ {0};
     193              :     OnSourcesUpdatedCb onSourcesUpdated_ {};
     194              : 
     195              :     int64_t startTime_;
     196              :     int64_t lastTimestamp_;
     197              : };
     198              : 
     199              : } // namespace video
     200              : } // namespace jami
        

Generated by: LCOV version 2.0-1