Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 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 { 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, 70 : const std::shared_ptr<MediaFrame>& v) override; 71 : void attached(Observable<std::shared_ptr<MediaFrame>>* ob) override; 72 : void detached(Observable<std::shared_ptr<MediaFrame>>* ob) override; 73 : 74 : /** 75 : * Set all inputs at once 76 : * @param inputs New inputs 77 : * @note previous inputs will be stopped 78 : */ 79 : void switchInputs(const std::vector<std::string>& inputs); 80 : /** 81 : * Stop all inputs 82 : */ 83 : void stopInputs(); 84 : 85 : void setActiveStream(const std::string& id); 86 1 : void resetActiveStream() 87 : { 88 1 : activeStream_ = {}; 89 1 : updateLayout(); 90 1 : } 91 : 92 9752 : bool verifyActive(const std::string& id) { return activeStream_ == id; } 93 : 94 0 : void setVideoLayout(Layout newLayout) 95 : { 96 0 : currentLayout_ = newLayout; 97 0 : if (currentLayout_ == Layout::GRID) 98 0 : resetActiveStream(); 99 0 : layoutUpdated_ += 1; 100 0 : } 101 : 102 : Layout getVideoLayout() const { return currentLayout_; } 103 : 104 38 : void setOnSourcesUpdated(OnSourcesUpdatedCb&& cb) { onSourcesUpdated_ = std::move(cb); } 105 : 106 : MediaStream getStream(const std::string& name) const; 107 : 108 69 : std::shared_ptr<VideoFrameActiveWriter> getVideoLocal() const 109 : { 110 69 : if (!localInputs_.empty()) 111 64 : return *localInputs_.begin(); 112 5 : return {}; 113 : } 114 : 115 : void updateLayout(); 116 : 117 134 : std::shared_ptr<SinkClient>& getSink() { return sink_; } 118 : 119 22 : void addAudioOnlySource(const std::string& callId, const std::string& streamId) 120 : { 121 22 : std::unique_lock lk(audioOnlySourcesMtx_); 122 22 : audioOnlySources_.insert({callId, streamId}); 123 22 : lk.unlock(); 124 22 : updateLayout(); 125 22 : } 126 : 127 103 : void removeAudioOnlySource(const std::string& callId, const std::string& streamId) 128 : { 129 103 : std::unique_lock lk(audioOnlySourcesMtx_); 130 103 : if (audioOnlySources_.erase({callId, streamId})) { 131 16 : lk.unlock(); 132 16 : updateLayout(); 133 : } 134 103 : } 135 : 136 : void attachVideo(Observable<std::shared_ptr<MediaFrame>>* frame, 137 : const std::string& callId, 138 : const std::string& streamId); 139 : void detachVideo(Observable<std::shared_ptr<MediaFrame>>* frame); 140 : 141 8832 : StreamInfo streamInfo(Observable<std::shared_ptr<MediaFrame>>* frame) const 142 : { 143 8832 : std::lock_guard lk(videoToStreamInfoMtx_); 144 8832 : auto it = videoToStreamInfo_.find(frame); 145 8832 : if (it == videoToStreamInfo_.end()) 146 5 : return {}; 147 8827 : return it->second; 148 8832 : } 149 : 150 : private: 151 : NON_COPYABLE(VideoMixer); 152 : struct VideoMixerSource; 153 : 154 : bool render_frame(VideoFrame& output, 155 : const std::shared_ptr<VideoFrame>& input, 156 : std::unique_ptr<VideoMixerSource>& source); 157 : 158 : void calc_position(std::unique_ptr<VideoMixerSource>& source, 159 : const std::shared_ptr<VideoFrame>& input, 160 : int index); 161 : 162 : void startSink(); 163 : void stopSink(); 164 : 165 : void process(); 166 : 167 : const std::string id_; 168 : int width_ = 0; 169 : int height_ = 0; 170 : AVPixelFormat format_ = AV_PIX_FMT_YUV422P; 171 : std::shared_mutex rwMutex_; 172 : 173 : std::shared_ptr<SinkClient> sink_; 174 : 175 : std::chrono::time_point<std::chrono::steady_clock> nextProcess_; 176 : std::mutex localInputsMtx_; 177 : std::vector<std::shared_ptr<VideoFrameActiveWriter>> localInputs_ {}; 178 : void stopInput(const std::shared_ptr<VideoFrameActiveWriter>& input); 179 : 180 : VideoScaler scaler_; 181 : 182 : ThreadLoop loop_; // as to be last member 183 : 184 : Layout currentLayout_ {Layout::GRID}; 185 : std::list<std::unique_ptr<VideoMixerSource>> sources_; 186 : 187 : // We need to convert call to frame 188 : mutable std::mutex videoToStreamInfoMtx_ {}; 189 : std::map<Observable<std::shared_ptr<MediaFrame>>*, StreamInfo> videoToStreamInfo_ {}; 190 : 191 : std::mutex audioOnlySourcesMtx_; 192 : std::set<std::pair<std::string, std::string>> audioOnlySources_; 193 : std::string activeStream_ {}; 194 : 195 : std::atomic_int layoutUpdated_ {0}; 196 : OnSourcesUpdatedCb onSourcesUpdated_ {}; 197 : 198 : int64_t startTime_; 199 : int64_t lastTimestamp_; 200 : }; 201 : 202 : } // namespace video 203 : } // namespace jami