Ring Daemon
Loading...
Searching...
No Matches
video_mixer.h
Go to the documentation of this file.
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
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, 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
78 void switchInputs(const std::vector<std::string>& inputs);
82 void stopInputs();
83
84 void setActiveStream(const std::string& id);
86 {
87 activeStream_ = {};
89 }
90
91 bool verifyActive(const std::string& id) { return activeStream_ == id; }
92
94 {
95 currentLayout_ = newLayout;
96 if (currentLayout_ == Layout::GRID)
98 layoutUpdated_ += 1;
99 }
100
101 Layout getVideoLayout() const { return currentLayout_; }
102
103 void setOnSourcesUpdated(OnSourcesUpdatedCb&& cb) { onSourcesUpdated_ = std::move(cb); }
104
105 MediaStream getStream(const std::string& name) const;
106
107 std::shared_ptr<VideoFrameActiveWriter> getVideoLocal() const
108 {
109 if (!localInputs_.empty())
110 return *localInputs_.begin();
111 return {};
112 }
113
114 void updateLayout();
115
116 std::shared_ptr<SinkClient>& getSink() { return sink_; }
117
118 void addAudioOnlySource(const std::string& callId, const std::string& streamId)
119 {
120 std::unique_lock lk(audioOnlySourcesMtx_);
121 audioOnlySources_.insert({callId, streamId});
122 lk.unlock();
123 updateLayout();
124 }
125
126 void removeAudioOnlySource(const std::string& callId, const std::string& streamId)
127 {
128 std::unique_lock lk(audioOnlySourcesMtx_);
129 if (audioOnlySources_.erase({callId, streamId})) {
130 lk.unlock();
131 updateLayout();
132 }
133 }
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 StreamInfo streamInfo(Observable<std::shared_ptr<MediaFrame>>* frame) const
141 {
142 std::lock_guard lk(videoToStreamInfoMtx_);
143 auto it = videoToStreamInfo_.find(frame);
144 if (it == videoToStreamInfo_.end())
145 return {};
146 return it->second;
147 }
148
149private:
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;
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
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:93
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:91
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 jami_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