Line data Source code
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 :
18 : #include "videomanager_interface.h"
19 : #include "videomanager.h"
20 : #include "localrecorder.h"
21 : #include "localrecordermanager.h"
22 : #include "libav_utils.h"
23 : #include "video/video_input.h"
24 : #include "video/video_device_monitor.h"
25 : #include "account.h"
26 : #include "logger.h"
27 : #include "manager.h"
28 : #include "system_codec_container.h"
29 : #ifdef ENABLE_VIDEO
30 : #include "video/sinkclient.h"
31 : #endif
32 : #include "client/ring_signal.h"
33 : #include "audio/ringbufferpool.h"
34 : #include "jami/media_const.h"
35 : #include "libav_utils.h"
36 : #include "call_const.h"
37 : #include "system_codec_container.h"
38 :
39 : #include <functional>
40 : #include <memory>
41 : #include <string>
42 : #include <vector>
43 : #include <new> // std::bad_alloc
44 : #include <algorithm>
45 : #include <cstdlib>
46 : #include <cstring> // std::memset
47 : #include <ciso646> // fix windows compiler bug
48 :
49 : extern "C" {
50 : #include <libavutil/display.h>
51 : }
52 :
53 : namespace libjami {
54 :
55 34330 : MediaFrame::MediaFrame()
56 34330 : : frame_ {av_frame_alloc()}
57 : {
58 34319 : if (not frame_)
59 0 : throw std::bad_alloc();
60 34321 : }
61 :
62 : void
63 5752 : MediaFrame::copyFrom(const MediaFrame& o)
64 : {
65 5752 : reset();
66 5752 : if (o.frame_) {
67 5752 : av_frame_ref(frame_.get(), o.frame_.get());
68 5752 : av_frame_copy_props(frame_.get(), o.frame_.get());
69 : }
70 :
71 5752 : if (o.packet_) {
72 0 : packet_.reset(av_packet_alloc());
73 0 : av_packet_ref(packet_.get(), o.packet_.get());
74 : }
75 5752 : }
76 :
77 : void
78 5802 : MediaFrame::reset() noexcept
79 : {
80 5802 : if (frame_)
81 5802 : av_frame_unref(frame_.get());
82 5802 : packet_.reset();
83 5802 : }
84 :
85 : void
86 0 : MediaFrame::setPacket(PacketBuffer&& pkt)
87 : {
88 0 : packet_ = std::move(pkt);
89 0 : }
90 :
91 9941 : AudioFrame::AudioFrame(const jami::AudioFormat& format, size_t nb_samples)
92 9941 : : MediaFrame()
93 : {
94 9941 : setFormat(format);
95 9941 : if (nb_samples)
96 8 : reserve(nb_samples);
97 9941 : }
98 :
99 : void
100 9941 : AudioFrame::setFormat(const jami::AudioFormat& format)
101 : {
102 9941 : auto d = pointer();
103 9941 : av_channel_layout_default(&d->ch_layout, format.nb_channels);
104 9941 : d->sample_rate = format.sample_rate;
105 9941 : d->format = format.sampleFormat;
106 9941 : }
107 :
108 : jami::AudioFormat
109 0 : AudioFrame::getFormat() const
110 : {
111 0 : return {(unsigned) frame_->sample_rate, (unsigned) frame_->ch_layout.nb_channels, (AVSampleFormat) frame_->format};
112 : }
113 :
114 : size_t
115 0 : AudioFrame::getFrameSize() const
116 : {
117 0 : return frame_->nb_samples;
118 : }
119 :
120 : void
121 8 : AudioFrame::reserve(size_t nb_samples)
122 : {
123 8 : if (nb_samples != 0) {
124 8 : auto d = pointer();
125 8 : d->nb_samples = nb_samples;
126 : int err;
127 8 : if ((err = av_frame_get_buffer(d, 0)) < 0) {
128 0 : throw std::bad_alloc();
129 : }
130 : }
131 8 : }
132 :
133 : void
134 1 : AudioFrame::mix(const AudioFrame& frame)
135 : {
136 1 : auto& f = *pointer();
137 1 : auto& fIn = *frame.pointer();
138 1 : if (f.ch_layout.nb_channels != fIn.ch_layout.nb_channels || f.format != fIn.format
139 1 : || f.sample_rate != fIn.sample_rate) {
140 0 : throw std::invalid_argument("Unable to mix frames with different formats");
141 : }
142 1 : if (f.nb_samples == 0) {
143 0 : reserve(fIn.nb_samples);
144 0 : jami::libav_utils::fillWithSilence(&f);
145 1 : } else if (f.nb_samples != fIn.nb_samples) {
146 0 : throw std::invalid_argument("Unable to mix frames with different length");
147 : }
148 1 : AVSampleFormat fmt = (AVSampleFormat) f.format;
149 1 : bool isPlanar = av_sample_fmt_is_planar(fmt);
150 1 : unsigned samplesPerChannel = isPlanar ? f.nb_samples : f.nb_samples * f.ch_layout.nb_channels;
151 1 : unsigned channels = isPlanar ? f.ch_layout.nb_channels : 1;
152 1 : if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P) {
153 2 : for (unsigned i = 0; i < channels; i++) {
154 1 : auto c = (int16_t*) f.extended_data[i];
155 1 : auto cIn = (int16_t*) fIn.extended_data[i];
156 1921 : for (unsigned s = 0; s < samplesPerChannel; s++) {
157 3840 : c[s] = std::clamp((int32_t) c[s] + (int32_t) cIn[s],
158 1920 : (int32_t) std::numeric_limits<int16_t>::min(),
159 3840 : (int32_t) std::numeric_limits<int16_t>::max());
160 : }
161 : }
162 1 : } else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP) {
163 0 : for (unsigned i = 0; i < channels; i++) {
164 0 : auto c = (float*) f.extended_data[i];
165 0 : auto cIn = (float*) fIn.extended_data[i];
166 0 : for (unsigned s = 0; s < samplesPerChannel; s++) {
167 0 : c[s] += cIn[s];
168 : }
169 : }
170 0 : } else {
171 0 : throw std::invalid_argument(std::string("Unsupported format for mixing: ") + av_get_sample_fmt_name(fmt));
172 : }
173 1 : }
174 :
175 : float
176 0 : AudioFrame::calcRMS() const
177 : {
178 0 : double rms = 0.0;
179 0 : auto fmt = static_cast<AVSampleFormat>(frame_->format);
180 0 : bool planar = av_sample_fmt_is_planar(fmt);
181 0 : int perChannel = planar ? frame_->nb_samples : frame_->nb_samples * frame_->ch_layout.nb_channels;
182 0 : int channels = planar ? frame_->ch_layout.nb_channels : 1;
183 0 : if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P) {
184 0 : for (int c = 0; c < channels; ++c) {
185 0 : auto buf = reinterpret_cast<int16_t*>(frame_->extended_data[c]);
186 0 : for (int i = 0; i < perChannel; ++i) {
187 0 : auto sample = buf[i] * 0.000030517578125f;
188 0 : rms += sample * sample;
189 : }
190 : }
191 0 : } else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP) {
192 0 : for (int c = 0; c < channels; ++c) {
193 0 : auto buf = reinterpret_cast<float*>(frame_->extended_data[c]);
194 0 : for (int i = 0; i < perChannel; ++i) {
195 0 : rms += buf[i] * buf[i];
196 : }
197 : }
198 0 : } else {
199 : // Should not happen
200 0 : JAMI_ERR() << "Unsupported format for getting volume level: " << av_get_sample_fmt_name(fmt);
201 0 : return 0.0;
202 : }
203 : // divide by the number of multi-byte samples
204 0 : return sqrt(rms / (frame_->nb_samples * frame_->ch_layout.nb_channels));
205 : }
206 :
207 : #ifdef ENABLE_VIDEO
208 :
209 33599 : VideoFrame::~VideoFrame()
210 : {
211 24238 : if (releaseBufferCb_)
212 0 : releaseBufferCb_(ptr_);
213 33599 : }
214 :
215 : void
216 5802 : VideoFrame::reset() noexcept
217 : {
218 5802 : MediaFrame::reset();
219 5802 : allocated_ = false;
220 5802 : releaseBufferCb_ = {};
221 5802 : }
222 :
223 : void
224 5752 : VideoFrame::copyFrom(const VideoFrame& o)
225 : {
226 5752 : MediaFrame::copyFrom(o);
227 5752 : ptr_ = o.ptr_;
228 5752 : allocated_ = o.allocated_;
229 5752 : }
230 :
231 : size_t
232 0 : VideoFrame::size() const noexcept
233 : {
234 0 : return av_image_get_buffer_size((AVPixelFormat) frame_->format, frame_->width, frame_->height, 1);
235 : }
236 :
237 : int
238 23009 : VideoFrame::format() const noexcept
239 : {
240 23009 : return frame_->format;
241 : }
242 :
243 : int
244 24315 : VideoFrame::width() const noexcept
245 : {
246 24315 : return frame_->width;
247 : }
248 :
249 : int
250 33311 : VideoFrame::height() const noexcept
251 : {
252 33311 : return frame_->height;
253 : }
254 :
255 : void
256 18414 : VideoFrame::setGeometry(int format, int width, int height) noexcept
257 : {
258 18414 : frame_->format = format;
259 18414 : frame_->width = width;
260 18414 : frame_->height = height;
261 18414 : }
262 :
263 : void
264 18364 : VideoFrame::reserve(int format, int width, int height)
265 : {
266 18364 : auto libav_frame = frame_.get();
267 :
268 18364 : if (allocated_) {
269 : // nothing to do if same properties
270 0 : if (width == libav_frame->width and height == libav_frame->height and format == libav_frame->format)
271 0 : av_frame_unref(libav_frame);
272 : }
273 :
274 18364 : setGeometry(format, width, height);
275 18364 : if (av_frame_get_buffer(libav_frame, 32))
276 0 : throw std::bad_alloc();
277 18364 : allocated_ = true;
278 18364 : releaseBufferCb_ = {};
279 18364 : }
280 :
281 : void
282 50 : VideoFrame::setFromMemory(uint8_t* ptr, int format, int width, int height) noexcept
283 : {
284 50 : reset();
285 50 : setGeometry(format, width, height);
286 50 : if (not ptr)
287 0 : return;
288 50 : av_image_fill_arrays(frame_->data,
289 50 : frame_->linesize,
290 : (uint8_t*) ptr,
291 50 : (AVPixelFormat) frame_->format,
292 : width,
293 : height,
294 : 1);
295 : }
296 :
297 : void
298 0 : VideoFrame::setFromMemory(
299 : uint8_t* ptr, int format, int width, int height, const std::function<void(uint8_t*)>& cb) noexcept
300 : {
301 0 : setFromMemory(ptr, format, width, height);
302 0 : if (cb) {
303 0 : releaseBufferCb_ = cb;
304 0 : ptr_ = ptr;
305 : }
306 0 : }
307 :
308 : void
309 0 : VideoFrame::setReleaseCb(const std::function<void(uint8_t*)>& cb) noexcept
310 : {
311 0 : if (cb) {
312 0 : releaseBufferCb_ = cb;
313 : }
314 0 : }
315 :
316 : void
317 0 : VideoFrame::noise()
318 : {
319 0 : auto f = frame_.get();
320 0 : if (f->data[0] == nullptr)
321 0 : return;
322 0 : for (std::size_t i = 0; i < size(); ++i) {
323 0 : f->data[0][i] = std::rand() & 255;
324 : }
325 : }
326 :
327 : int
328 15401 : VideoFrame::getOrientation() const
329 : {
330 15401 : int32_t* matrix {nullptr};
331 15401 : if (auto p = packet()) {
332 0 : matrix = reinterpret_cast<int32_t*>(av_packet_get_side_data(p, AV_PKT_DATA_DISPLAYMATRIX, nullptr));
333 15401 : } else if (auto p = pointer()) {
334 15401 : if (AVFrameSideData* side_data = av_frame_get_side_data(p, AV_FRAME_DATA_DISPLAYMATRIX)) {
335 0 : matrix = reinterpret_cast<int32_t*>(side_data->data);
336 : }
337 : }
338 15401 : if (matrix) {
339 0 : double angle = av_display_rotation_get(matrix);
340 0 : return std::isnan(angle) ? 0 : -(int) angle;
341 : }
342 15401 : return 0;
343 : }
344 :
345 : VideoFrame*
346 0 : getNewFrame(std::string_view id)
347 : {
348 0 : if (auto vm = jami::Manager::instance().getVideoManager())
349 0 : if (auto input = vm->getVideoInput(id))
350 0 : return &input->getNewFrame();
351 0 : JAMI_WARNING("getNewFrame: Unable to find input {}", id);
352 0 : return nullptr;
353 : }
354 :
355 : void
356 0 : publishFrame(std::string_view id)
357 : {
358 0 : if (auto vm = jami::Manager::instance().getVideoManager())
359 0 : if (auto input = vm->getVideoInput(id))
360 0 : input->publishFrame();
361 0 : }
362 :
363 : void
364 0 : registerVideoHandlers(const std::map<std::string, std::shared_ptr<CallbackWrapperBase>>& handlers)
365 : {
366 0 : registerSignalHandlers(handlers);
367 0 : }
368 :
369 : std::vector<std::string>
370 0 : getDeviceList()
371 : {
372 0 : if (auto vm = jami::Manager::instance().getVideoManager())
373 0 : return vm->videoDeviceMonitor.getDeviceList();
374 0 : return {};
375 : }
376 :
377 : VideoCapabilities
378 0 : getCapabilities(const std::string& deviceId)
379 : {
380 0 : if (auto vm = jami::Manager::instance().getVideoManager())
381 0 : return vm->videoDeviceMonitor.getCapabilities(deviceId);
382 0 : return {};
383 : }
384 :
385 : std::string
386 0 : getDefaultDevice()
387 : {
388 0 : if (auto vm = jami::Manager::instance().getVideoManager())
389 0 : return vm->videoDeviceMonitor.getDefaultDevice();
390 0 : return {};
391 : }
392 :
393 : void
394 0 : setDefaultDevice(const std::string& deviceId)
395 : {
396 0 : JAMI_DBG("Setting default device to %s", deviceId.c_str());
397 0 : if (auto vm = jami::Manager::instance().getVideoManager()) {
398 0 : if (vm->videoDeviceMonitor.setDefaultDevice(deviceId))
399 0 : jami::Manager::instance().saveConfig();
400 : }
401 0 : }
402 :
403 : void
404 0 : setDeviceOrientation(const std::string& deviceId, int angle)
405 : {
406 0 : if (auto vm = jami::Manager::instance().getVideoManager())
407 0 : vm->setDeviceOrientation(deviceId, angle);
408 0 : }
409 :
410 : std::map<std::string, std::string>
411 0 : getDeviceParams(const std::string& deviceId)
412 : {
413 0 : if (auto vm = jami::Manager::instance().getVideoManager()) {
414 0 : auto params = vm->videoDeviceMonitor.getDeviceParams(deviceId);
415 : return {{"format", params.format},
416 0 : {"width", std::to_string(params.width)},
417 0 : {"height", std::to_string(params.height)},
418 0 : {"rate", params.framerate.to_string()}};
419 0 : }
420 0 : return {};
421 : }
422 :
423 : std::map<std::string, std::string>
424 0 : getSettings(const std::string& deviceId)
425 : {
426 0 : if (auto vm = jami::Manager::instance().getVideoManager()) {
427 0 : return vm->videoDeviceMonitor.getSettings(deviceId).to_map();
428 : }
429 0 : return {};
430 : }
431 :
432 : void
433 0 : applySettings(const std::string& deviceId, const std::map<std::string, std::string>& settings)
434 : {
435 0 : if (auto vm = jami::Manager::instance().getVideoManager()) {
436 0 : vm->videoDeviceMonitor.applySettings(deviceId, settings);
437 0 : jami::Manager::instance().saveConfig();
438 : }
439 0 : }
440 :
441 : std::string
442 0 : openVideoInput(const std::string& path)
443 : {
444 0 : if (auto vm = jami::Manager::instance().getVideoManager()) {
445 0 : auto id = path.empty() ? vm->videoDeviceMonitor.getMRLForDefaultDevice() : path;
446 0 : auto& input = vm->clientVideoInputs[id];
447 0 : if (not input) {
448 0 : input = jami::getVideoInput(id);
449 : }
450 0 : return id;
451 0 : }
452 0 : return {};
453 : }
454 :
455 : bool
456 0 : closeVideoInput(const std::string& id)
457 : {
458 0 : if (auto vm = jami::Manager::instance().getVideoManager())
459 0 : return vm->clientVideoInputs.erase(id) > 0;
460 0 : return false;
461 : }
462 : #endif
463 :
464 : void
465 0 : startAudioDevice()
466 : {
467 0 : if (auto newPreview = jami::getAudioInput(jami::RingBufferPool::DEFAULT_ID)) {
468 0 : if (auto vm = jami::Manager::instance().getVideoManager())
469 0 : vm->audioPreview = newPreview;
470 0 : newPreview->switchInput("");
471 0 : }
472 0 : }
473 :
474 : void
475 0 : stopAudioDevice()
476 : {
477 0 : if (auto vm = jami::Manager::instance().getVideoManager())
478 0 : vm->audioPreview.reset();
479 0 : }
480 :
481 : std::string
482 0 : startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath)
483 : {
484 0 : auto rec = std::make_unique<jami::LocalRecorder>(videoInputId);
485 0 : rec->setPath(filepath);
486 :
487 : // retrieve final path (containing file extension)
488 0 : auto path = rec->getPath();
489 :
490 0 : auto& recordManager = jami::LocalRecorderManager::instance();
491 :
492 : try {
493 0 : recordManager.insertRecorder(path, std::move(rec));
494 0 : } catch (const std::invalid_argument&) {
495 0 : return "";
496 0 : }
497 :
498 0 : auto ret = recordManager.getRecorderByPath(path)->start();
499 0 : if (!ret) {
500 0 : recordManager.removeRecorderByPath(filepath);
501 0 : return "";
502 : }
503 :
504 0 : return path;
505 0 : }
506 :
507 : void
508 0 : stopLocalRecorder(const std::string& filepath)
509 : {
510 0 : jami::LocalRecorder* rec = jami::LocalRecorderManager::instance().getRecorderByPath(filepath);
511 0 : if (!rec) {
512 0 : JAMI_WARN("Unable to stop non existing local recorder.");
513 0 : return;
514 : }
515 :
516 0 : rec->stopRecording();
517 0 : jami::LocalRecorderManager::instance().removeRecorderByPath(filepath);
518 : }
519 :
520 : bool
521 0 : registerSinkTarget(const std::string& sinkId, SinkTarget target)
522 : {
523 : #ifdef ENABLE_VIDEO
524 0 : if (auto sink = jami::Manager::instance().getSinkClient(sinkId)) {
525 0 : sink->registerTarget(std::move(target));
526 0 : return true;
527 : } else
528 0 : JAMI_WARN("No sink found for id '%s'", sinkId.c_str());
529 : #endif
530 0 : return false;
531 : }
532 :
533 : #ifdef ENABLE_SHM
534 : void
535 : startShmSink(const std::string& sinkId, bool value)
536 : {
537 : #ifdef ENABLE_VIDEO
538 : if (auto sink = jami::Manager::instance().getSinkClient(sinkId))
539 : sink->enableShm(value);
540 : else
541 : JAMI_WARN("No sink found for id '%s'", sinkId.c_str());
542 : #endif
543 : }
544 : #endif
545 :
546 : std::map<std::string, std::string>
547 0 : getRenderer(const std::string& callId)
548 : {
549 : #ifdef ENABLE_VIDEO
550 0 : if (auto sink = jami::Manager::instance().getSinkClient(callId))
551 : return {
552 : {libjami::Media::Details::CALL_ID, callId},
553 0 : {libjami::Media::Details::SHM_PATH, sink->openedName()},
554 0 : {libjami::Media::Details::WIDTH, std::to_string(sink->getWidth())},
555 0 : {libjami::Media::Details::HEIGHT, std::to_string(sink->getHeight())},
556 0 : };
557 : else
558 : #endif
559 : return {
560 : {libjami::Media::Details::CALL_ID, callId},
561 : {libjami::Media::Details::SHM_PATH, ""},
562 : {libjami::Media::Details::WIDTH, "0"},
563 : {libjami::Media::Details::HEIGHT, "0"},
564 0 : };
565 : }
566 :
567 : std::string
568 0 : createMediaPlayer(const std::string& path)
569 : {
570 0 : return jami::createMediaPlayer(path);
571 : }
572 :
573 : bool
574 0 : closeMediaPlayer(const std::string& id)
575 : {
576 0 : return jami::closeMediaPlayer(id);
577 : }
578 :
579 : bool
580 0 : pausePlayer(const std::string& id, const bool& pause)
581 : {
582 0 : return jami::pausePlayer(id, pause);
583 : }
584 :
585 : bool
586 0 : mutePlayerAudio(const std::string& id, const bool& mute)
587 : {
588 0 : return jami::mutePlayerAudio(id, mute);
589 : }
590 :
591 : bool
592 0 : playerSeekToTime(const std::string& id, const int& time)
593 : {
594 0 : return jami::playerSeekToTime(id, time);
595 : }
596 :
597 : int64_t
598 0 : getPlayerPosition(const std::string& id)
599 : {
600 0 : return jami::getPlayerPosition(id);
601 : }
602 :
603 : int64_t
604 0 : getPlayerDuration(const std::string& id)
605 : {
606 0 : return jami::getPlayerDuration(id);
607 : }
608 :
609 : void
610 0 : setAutoRestart(const std::string& id, const bool& restart)
611 : {
612 0 : jami::setAutoRestart(id, restart);
613 0 : }
614 :
615 : bool
616 0 : getDecodingAccelerated()
617 : {
618 : #ifdef ENABLE_HWACCEL
619 0 : return jami::Manager::instance().videoPreferences.getDecodingAccelerated();
620 : #else
621 : return false;
622 : #endif
623 : }
624 :
625 : void
626 0 : setDecodingAccelerated(bool state)
627 : {
628 : #ifdef ENABLE_HWACCEL
629 0 : JAMI_DBG("%s hardware acceleration", (state ? "Enabling" : "Disabling"));
630 0 : if (jami::Manager::instance().videoPreferences.setDecodingAccelerated(state))
631 0 : jami::Manager::instance().saveConfig();
632 : #endif
633 0 : }
634 :
635 : bool
636 0 : getEncodingAccelerated()
637 : {
638 : #ifdef ENABLE_HWACCEL
639 0 : return jami::Manager::instance().videoPreferences.getEncodingAccelerated();
640 : #else
641 : return false;
642 : #endif
643 : }
644 :
645 : void
646 0 : setEncodingAccelerated(bool state)
647 : {
648 : #ifdef ENABLE_HWACCEL
649 0 : JAMI_DBG("%s hardware acceleration", (state ? "Enabling" : "Disabling"));
650 0 : if (jami::Manager::instance().videoPreferences.setEncodingAccelerated(state))
651 0 : jami::Manager::instance().saveConfig();
652 : else
653 0 : return;
654 : #endif
655 0 : for (const auto& acc : jami::Manager::instance().getAllAccounts()) {
656 0 : if (state)
657 0 : acc->setCodecActive(AV_CODEC_ID_HEVC);
658 : else
659 0 : acc->setCodecInactive(AV_CODEC_ID_HEVC);
660 : // Update and sort codecs
661 0 : acc->setActiveCodecs(acc->getActiveCodecs());
662 0 : jami::Manager::instance().saveConfig(acc);
663 0 : }
664 : }
665 :
666 : #if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS)
667 : void
668 : addVideoDevice(const std::string& node, const std::vector<std::map<std::string, std::string>>& devInfo)
669 : {
670 : if (auto videoManager = jami::Manager::instance().getVideoManager()) {
671 : videoManager->videoDeviceMonitor.addDevice(node, devInfo);
672 : }
673 : }
674 :
675 : void
676 : removeVideoDevice(const std::string& node)
677 : {
678 : if (auto videoManager = jami::Manager::instance().getVideoManager()) {
679 : videoManager->videoDeviceMonitor.removeDevice(node);
680 : }
681 : }
682 : #endif
683 :
684 : } // namespace libjami
685 :
686 : namespace jami {
687 :
688 : #ifdef ENABLE_VIDEO
689 : video::VideoDeviceMonitor*
690 2037 : getVideoDeviceMonitor()
691 : {
692 2037 : if (auto vm = jami::Manager::instance().getVideoManager())
693 2002 : return &vm->videoDeviceMonitor;
694 35 : return {};
695 : }
696 :
697 : std::shared_ptr<video::VideoInput>
698 144 : getVideoInput(const std::string& resource, video::VideoInputMode inputMode, const std::string& sink)
699 : {
700 144 : auto sinkId = sink.empty() ? resource : sink;
701 144 : auto vmgr = Manager::instance().getVideoManager();
702 144 : if (!vmgr)
703 0 : return {};
704 144 : std::lock_guard<std::mutex> lk(vmgr->videoMutex);
705 144 : auto it = vmgr->videoInputs.find(sinkId);
706 144 : if (it != vmgr->videoInputs.end()) {
707 127 : if (auto input = it->second.lock()) {
708 79 : return input;
709 127 : }
710 : }
711 :
712 65 : auto input = std::make_shared<video::VideoInput>(inputMode, resource, sinkId);
713 65 : vmgr->videoInputs[sinkId] = input;
714 65 : return input;
715 144 : }
716 :
717 : void
718 0 : VideoManager::setDeviceOrientation(const std::string& deviceId, int angle)
719 : {
720 0 : videoDeviceMonitor.setDeviceOrientation(deviceId, angle);
721 0 : }
722 : #endif
723 :
724 : std::shared_ptr<AudioInput>
725 292 : getAudioInput(const std::string& device)
726 : {
727 292 : auto vmgr = Manager::instance().getVideoManager();
728 292 : if (!vmgr)
729 0 : return {};
730 292 : std::lock_guard<std::mutex> lk(vmgr->audioMutex);
731 :
732 : // erase expired audio inputs
733 1205 : for (auto it = vmgr->audioInputs.cbegin(); it != vmgr->audioInputs.cend();) {
734 913 : if (it->second.expired())
735 224 : it = vmgr->audioInputs.erase(it);
736 : else
737 689 : ++it;
738 : }
739 :
740 292 : auto it = vmgr->audioInputs.find(device);
741 292 : if (it != vmgr->audioInputs.end()) {
742 42 : if (auto input = it->second.lock()) {
743 42 : return input;
744 42 : }
745 : }
746 :
747 250 : auto input = std::make_shared<AudioInput>(device);
748 250 : vmgr->audioInputs[device] = input;
749 250 : return input;
750 292 : }
751 :
752 : bool
753 0 : VideoManager::hasRunningPlayers()
754 : {
755 0 : if (auto vmgr = Manager::instance().getVideoManager())
756 0 : return !vmgr->mediaPlayers.empty();
757 0 : return false;
758 : }
759 :
760 : std::shared_ptr<MediaPlayer>
761 6 : getMediaPlayer(const std::string& id)
762 : {
763 6 : if (auto vmgr = Manager::instance().getVideoManager()) {
764 6 : auto it = vmgr->mediaPlayers.find(id);
765 6 : if (it != vmgr->mediaPlayers.end()) {
766 6 : return it->second;
767 : }
768 : }
769 0 : return {};
770 : }
771 :
772 : std::string
773 6 : createMediaPlayer(const std::string& path)
774 : {
775 6 : if (auto vmgr = Manager::instance().getVideoManager()) {
776 6 : auto& player = vmgr->mediaPlayers[path];
777 6 : if (!player) {
778 6 : player = std::make_shared<MediaPlayer>(path);
779 : }
780 6 : return path;
781 : }
782 0 : return {};
783 : }
784 :
785 : bool
786 0 : pausePlayer(const std::string& id, bool pause)
787 : {
788 0 : if (auto player = getMediaPlayer(id)) {
789 0 : player->pause(pause);
790 0 : return true;
791 0 : }
792 0 : return false;
793 : }
794 :
795 : bool
796 535 : closeMediaPlayer(const std::string& id)
797 : {
798 535 : if (auto vm = Manager::instance().getVideoManager())
799 535 : return vm->mediaPlayers.erase(id) > 0;
800 0 : return false;
801 : }
802 :
803 : bool
804 0 : mutePlayerAudio(const std::string& id, bool mute)
805 : {
806 0 : if (auto player = getMediaPlayer(id)) {
807 0 : player->muteAudio(mute);
808 0 : return true;
809 0 : }
810 0 : return false;
811 : }
812 :
813 : bool
814 0 : playerSeekToTime(const std::string& id, int time)
815 : {
816 0 : if (auto player = getMediaPlayer(id))
817 0 : return player->seekToTime(time);
818 0 : return false;
819 : }
820 :
821 : int64_t
822 0 : getPlayerPosition(const std::string& id)
823 : {
824 0 : if (auto player = getMediaPlayer(id))
825 0 : return player->getPlayerPosition();
826 0 : return -1;
827 : }
828 :
829 : int64_t
830 0 : getPlayerDuration(const std::string& id)
831 : {
832 0 : if (auto player = getMediaPlayer(id))
833 0 : return player->getPlayerDuration();
834 0 : return -1;
835 : }
836 :
837 : void
838 0 : setAutoRestart(const std::string& id, bool restart)
839 : {
840 0 : if (auto player = getMediaPlayer(id))
841 0 : player->setAutoRestart(restart);
842 0 : }
843 :
844 : } // namespace jami
|