Ring Daemon 16.0.0
Loading...
Searching...
No Matches
video_mixer.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 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
30namespace jami {
31namespace video {
32
33class SinkClient;
34
36{
37 std::string callId;
38 std::string streamId;
39};
40
42{
44 int x;
45 int y;
46 int w;
47 int h;
49 std::string callId;
50 std::string streamId;
51};
52using OnSourcesUpdatedCb = std::function<void(std::vector<SourceInfo>&&)>;
53
55
57{
58public:
59 VideoMixer(const std::string& id, const std::string& localInput = {}, bool attachHost = false);
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
79 void switchInputs(const std::vector<std::string>& inputs);
83 void stopInputs();
84
85 void setActiveStream(const std::string& id);
87 {
88 activeStream_ = {};
90 }
91
92 bool verifyActive(const std::string& id) { return activeStream_ == id; }
93
95 {
96 currentLayout_ = newLayout;
97 if (currentLayout_ == Layout::GRID)
99 layoutUpdated_ += 1;
100 }
101
102 Layout getVideoLayout() const { return currentLayout_; }
103
104 void setOnSourcesUpdated(OnSourcesUpdatedCb&& cb) { onSourcesUpdated_ = std::move(cb); }
105
106 MediaStream getStream(const std::string& name) const;
107
108 std::shared_ptr<VideoFrameActiveWriter> getVideoLocal() const
109 {
110 if (!localInputs_.empty())
111 return *localInputs_.begin();
112 return {};
113 }
114
115 void updateLayout();
116
117 std::shared_ptr<SinkClient>& getSink() { return sink_; }
118
119 void addAudioOnlySource(const std::string& callId, const std::string& streamId)
120 {
121 std::unique_lock lk(audioOnlySourcesMtx_);
122 audioOnlySources_.insert({callId, streamId});
123 lk.unlock();
124 updateLayout();
125 }
126
127 void removeAudioOnlySource(const std::string& callId, const std::string& streamId)
128 {
129 std::unique_lock lk(audioOnlySourcesMtx_);
130 if (audioOnlySources_.erase({callId, streamId})) {
131 lk.unlock();
132 updateLayout();
133 }
134 }
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 StreamInfo streamInfo(Observable<std::shared_ptr<MediaFrame>>* frame) const
142 {
143 std::lock_guard lk(videoToStreamInfoMtx_);
144 auto it = videoToStreamInfo_.find(frame);
145 if (it == videoToStreamInfo_.end())
146 return {};
147 return it->second;
148 }
149
150private:
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;
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
StreamInfo streamInfo(Observable< std::shared_ptr< MediaFrame > > *frame) const
void attached(Observable< std::shared_ptr< MediaFrame > > *ob) override
std::shared_ptr< SinkClient > & getSink()
void addAudioOnlySource(const std::string &callId, const std::string &streamId)
Layout getVideoLayout() const
void update(Observable< std::shared_ptr< MediaFrame > > *ob, const std::shared_ptr< MediaFrame > &v) override
void switchInputs(const std::vector< std::string > &inputs)
Set all inputs at once.
AVPixelFormat getPixelFormat() const override
void detachVideo(Observable< std::shared_ptr< MediaFrame > > *frame)
void setParameters(int width, int height, AVPixelFormat format=AV_PIX_FMT_YUV422P)
void setVideoLayout(Layout newLayout)
Definition video_mixer.h:94
void detached(Observable< std::shared_ptr< MediaFrame > > *ob) override
void setActiveStream(const std::string &id)
void stopInputs()
Stop all inputs.
MediaStream getStream(const std::string &name) const
void setOnSourcesUpdated(OnSourcesUpdatedCb &&cb)
void removeAudioOnlySource(const std::string &callId, const std::string &streamId)
bool verifyActive(const std::string &id)
Definition video_mixer.h:92
int getHeight() const override
int getWidth() const override
std::shared_ptr< VideoFrameActiveWriter > getVideoLocal() const
void attachVideo(Observable< std::shared_ptr< MediaFrame > > *frame, const std::string &callId, const std::string &streamId)
std::function< void(std::vector< SourceInfo > &&)> OnSourcesUpdatedCb
Definition video_mixer.h:52
void emitSignal(Args... args)
Definition ring_signal.h:64
Simple macro to hide class' copy constructor and assignment operator.
#define NON_COPYABLE(ClassName)
Definition noncopyable.h:30
Observable< std::shared_ptr< MediaFrame > > * source
Definition video_mixer.h:43