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