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
|