Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 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 34599 : MediaFrame::MediaFrame()
56 34599 : : frame_ {av_frame_alloc()}
57 : {
58 34591 : if (not frame_)
59 0 : throw std::bad_alloc();
60 34592 : }
61 :
62 : void
63 5712 : MediaFrame::copyFrom(const MediaFrame& o)
64 : {
65 5712 : reset();
66 5712 : if (o.frame_) {
67 5712 : av_frame_ref(frame_.get(), o.frame_.get());
68 5712 : av_frame_copy_props(frame_.get(), o.frame_.get());
69 : }
70 :
71 5712 : if (o.packet_) {
72 0 : packet_.reset(av_packet_alloc());
73 0 : av_packet_ref(packet_.get(), o.packet_.get());
74 : }
75 5712 : }
76 :
77 : void
78 5762 : MediaFrame::reset() noexcept
79 : {
80 5762 : if (frame_)
81 5762 : av_frame_unref(frame_.get());
82 5762 : packet_.reset();
83 5762 : }
84 :
85 : void
86 0 : MediaFrame::setPacket(PacketBuffer&& pkt)
87 : {
88 0 : packet_ = std::move(pkt);
89 0 : }
90 :
91 9938 : AudioFrame::AudioFrame(const jami::AudioFormat& format, size_t nb_samples)
92 9938 : : MediaFrame()
93 : {
94 9938 : setFormat(format);
95 9938 : if (nb_samples)
96 4 : reserve(nb_samples);
97 9938 : }
98 :
99 : void
100 9938 : AudioFrame::setFormat(const jami::AudioFormat& format)
101 : {
102 9938 : auto d = pointer();
103 9938 : av_channel_layout_default(&d->ch_layout, format.nb_channels);
104 9938 : d->sample_rate = format.sample_rate;
105 9938 : d->format = format.sampleFormat;
106 9938 : }
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 4 : AudioFrame::reserve(size_t nb_samples)
124 : {
125 4 : if (nb_samples != 0) {
126 4 : auto d = pointer();
127 4 : d->nb_samples = nb_samples;
128 : int err;
129 4 : if ((err = av_frame_get_buffer(d, 0)) < 0) {
130 0 : throw std::bad_alloc();
131 : }
132 : }
133 4 : }
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 33989 : VideoFrame::~VideoFrame()
215 : {
216 24511 : if (releaseBufferCb_)
217 0 : releaseBufferCb_(ptr_);
218 33989 : }
219 :
220 : void
221 5762 : VideoFrame::reset() noexcept
222 : {
223 5762 : MediaFrame::reset();
224 5762 : allocated_ = false;
225 5762 : releaseBufferCb_ = {};
226 5762 : }
227 :
228 : void
229 5712 : VideoFrame::copyFrom(const VideoFrame& o)
230 : {
231 5712 : MediaFrame::copyFrom(o);
232 5712 : ptr_ = o.ptr_;
233 5712 : allocated_ = o.allocated_;
234 5712 : }
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 22849 : VideoFrame::format() const noexcept
247 : {
248 22849 : return frame_->format;
249 : }
250 :
251 : int
252 24213 : VideoFrame::width() const noexcept
253 : {
254 24213 : return frame_->width;
255 : }
256 :
257 : int
258 33437 : VideoFrame::height() const noexcept
259 : {
260 33437 : return frame_->height;
261 : }
262 :
263 : void
264 18759 : VideoFrame::setGeometry(int format, int width, int height) noexcept
265 : {
266 18759 : frame_->format = format;
267 18759 : frame_->width = width;
268 18759 : frame_->height = height;
269 18759 : }
270 :
271 : void
272 18709 : VideoFrame::reserve(int format, int width, int height)
273 : {
274 18709 : auto libav_frame = frame_.get();
275 :
276 18709 : 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 18709 : setGeometry(format, width, height);
284 18709 : if (av_frame_get_buffer(libav_frame, 32))
285 0 : throw std::bad_alloc();
286 18709 : allocated_ = true;
287 18709 : releaseBufferCb_ = {};
288 18709 : }
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 15618 : VideoFrame::getOrientation() const
341 : {
342 15618 : int32_t* matrix {nullptr};
343 15618 : if (auto p = packet()) {
344 : matrix = reinterpret_cast<int32_t*>(
345 0 : av_packet_get_side_data(p, AV_PKT_DATA_DISPLAYMATRIX, nullptr));
346 15618 : } else if (auto p = pointer()) {
347 15618 : if (AVFrameSideData* side_data = av_frame_get_side_data(p, AV_FRAME_DATA_DISPLAYMATRIX)) {
348 0 : matrix = reinterpret_cast<int32_t*>(side_data->data);
349 : }
350 : }
351 15618 : if (matrix) {
352 0 : double angle = av_display_rotation_get(matrix);
353 0 : return std::isnan(angle) ? 0 : -(int) angle;
354 : }
355 15618 : return 0;
356 : }
357 :
358 : VideoFrame*
359 0 : getNewFrame(std::string_view id)
360 : {
361 0 : if (auto input = jami::Manager::instance().getVideoManager().getVideoInput(id))
362 0 : return &input->getNewFrame();
363 0 : JAMI_WARN("getNewFrame: Unable to find input %.*s", (int) id.size(), id.data());
364 0 : return nullptr;
365 : }
366 :
367 : void
368 0 : publishFrame(std::string_view id)
369 : {
370 0 : if (auto input = jami::Manager::instance().getVideoManager().getVideoInput(id))
371 0 : return input->publishFrame();
372 0 : JAMI_WARN("publishFrame: Unable to find input %.*s", (int) id.size(), id.data());
373 : }
374 :
375 : void
376 0 : registerVideoHandlers(const std::map<std::string, std::shared_ptr<CallbackWrapperBase>>& handlers)
377 : {
378 0 : registerSignalHandlers(handlers);
379 0 : }
380 :
381 : std::vector<std::string>
382 0 : getDeviceList()
383 : {
384 0 : return jami::Manager::instance().getVideoManager().videoDeviceMonitor.getDeviceList();
385 : }
386 :
387 : VideoCapabilities
388 0 : getCapabilities(const std::string& deviceId)
389 : {
390 0 : return jami::Manager::instance().getVideoManager().videoDeviceMonitor.getCapabilities(deviceId);
391 : }
392 :
393 : std::string
394 0 : getDefaultDevice()
395 : {
396 0 : return jami::Manager::instance().getVideoManager().videoDeviceMonitor.getDefaultDevice();
397 : }
398 :
399 : void
400 0 : setDefaultDevice(const std::string& deviceId)
401 : {
402 0 : JAMI_DBG("Setting default device to %s", deviceId.c_str());
403 0 : if (jami::Manager::instance().getVideoManager().videoDeviceMonitor.setDefaultDevice(deviceId))
404 0 : jami::Manager::instance().saveConfig();
405 0 : }
406 :
407 : void
408 0 : setDeviceOrientation(const std::string& deviceId, int angle)
409 : {
410 0 : jami::Manager::instance().getVideoManager().setDeviceOrientation(deviceId, angle);
411 0 : }
412 :
413 : std::map<std::string, std::string>
414 0 : getDeviceParams(const std::string& deviceId)
415 : {
416 0 : auto params = jami::Manager::instance().getVideoManager().videoDeviceMonitor.getDeviceParams(
417 0 : deviceId);
418 0 : std::ostringstream rate;
419 0 : rate << params.framerate;
420 : return {{"format", params.format},
421 0 : {"width", std::to_string(params.width)},
422 0 : {"height", std::to_string(params.height)},
423 0 : {"rate", rate.str()}};
424 0 : }
425 :
426 : std::map<std::string, std::string>
427 0 : getSettings(const std::string& deviceId)
428 : {
429 0 : return jami::Manager::instance()
430 0 : .getVideoManager()
431 0 : .videoDeviceMonitor.getSettings(deviceId)
432 0 : .to_map();
433 : }
434 :
435 : void
436 0 : applySettings(const std::string& deviceId, const std::map<std::string, std::string>& settings)
437 : {
438 0 : jami::Manager::instance().getVideoManager().videoDeviceMonitor.applySettings(deviceId, settings);
439 0 : jami::Manager::instance().saveConfig();
440 0 : }
441 :
442 : std::string
443 0 : openVideoInput(const std::string& path)
444 : {
445 0 : auto& vm = jami::Manager::instance().getVideoManager();
446 :
447 0 : auto id = path.empty() ? vm.videoDeviceMonitor.getMRLForDefaultDevice() : path;
448 0 : auto& input = vm.clientVideoInputs[id];
449 0 : if (not input) {
450 0 : input = jami::getVideoInput(id);
451 : }
452 0 : return id;
453 0 : }
454 :
455 : bool
456 0 : closeVideoInput(const std::string& id)
457 : {
458 0 : return jami::Manager::instance().getVideoManager().clientVideoInputs.erase(id) > 0;
459 : }
460 : #endif
461 :
462 : void
463 0 : startAudioDevice()
464 : {
465 0 : auto newPreview = jami::getAudioInput(jami::RingBufferPool::DEFAULT_ID);
466 0 : jami::Manager::instance().getVideoManager().audioPreview = newPreview;
467 0 : newPreview->switchInput("");
468 0 : }
469 :
470 : void
471 0 : stopAudioDevice()
472 : {
473 0 : jami::Manager::instance().getVideoManager().audioPreview.reset();
474 0 : }
475 :
476 : std::string
477 0 : startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath)
478 : {
479 0 : auto rec = std::make_unique<jami::LocalRecorder>(videoInputId);
480 0 : rec->setPath(filepath);
481 :
482 : // retrieve final path (containing file extension)
483 0 : auto path = rec->getPath();
484 :
485 0 : auto& recordManager = jami::LocalRecorderManager::instance();
486 :
487 : try {
488 0 : recordManager.insertRecorder(path, std::move(rec));
489 0 : } catch (const std::invalid_argument&) {
490 0 : return "";
491 0 : }
492 :
493 0 : auto ret = recordManager.getRecorderByPath(path)->startRecording();
494 0 : if (!ret) {
495 0 : recordManager.removeRecorderByPath(filepath);
496 0 : return "";
497 : }
498 :
499 0 : return path;
500 0 : }
501 :
502 : void
503 0 : stopLocalRecorder(const std::string& filepath)
504 : {
505 0 : jami::LocalRecorder* rec = jami::LocalRecorderManager::instance().getRecorderByPath(filepath);
506 0 : if (!rec) {
507 0 : JAMI_WARN("Unable to stop non existing local recorder.");
508 0 : return;
509 : }
510 :
511 0 : rec->stopRecording();
512 0 : jami::LocalRecorderManager::instance().removeRecorderByPath(filepath);
513 : }
514 :
515 : bool
516 0 : registerSinkTarget(const std::string& sinkId, SinkTarget target)
517 : {
518 : #ifdef ENABLE_VIDEO
519 0 : if (auto sink = jami::Manager::instance().getSinkClient(sinkId)) {
520 0 : sink->registerTarget(std::move(target));
521 0 : return true;
522 : } else
523 0 : JAMI_WARN("No sink found for id '%s'", sinkId.c_str());
524 : #endif
525 0 : return false;
526 : }
527 :
528 : #ifdef ENABLE_SHM
529 : void
530 0 : startShmSink(const std::string& sinkId, bool value)
531 : {
532 : #ifdef ENABLE_VIDEO
533 0 : if (auto sink = jami::Manager::instance().getSinkClient(sinkId))
534 0 : sink->enableShm(value);
535 : else
536 0 : JAMI_WARN("No sink found for id '%s'", sinkId.c_str());
537 : #endif
538 0 : }
539 : #endif
540 :
541 : std::map<std::string, std::string>
542 0 : getRenderer(const std::string& callId)
543 : {
544 : #ifdef ENABLE_VIDEO
545 0 : if (auto sink = jami::Manager::instance().getSinkClient(callId))
546 : return {
547 : {libjami::Media::Details::CALL_ID, callId},
548 0 : {libjami::Media::Details::SHM_PATH, sink->openedName()},
549 0 : {libjami::Media::Details::WIDTH, std::to_string(sink->getWidth())},
550 0 : {libjami::Media::Details::HEIGHT, std::to_string(sink->getHeight())},
551 0 : };
552 : else
553 : #endif
554 : return {
555 : {libjami::Media::Details::CALL_ID, callId},
556 : {libjami::Media::Details::SHM_PATH, ""},
557 : {libjami::Media::Details::WIDTH, "0"},
558 : {libjami::Media::Details::HEIGHT, "0"},
559 0 : };
560 : }
561 :
562 : std::string
563 0 : createMediaPlayer(const std::string& path)
564 : {
565 0 : return jami::createMediaPlayer(path);
566 : }
567 :
568 : bool
569 0 : closeMediaPlayer(const std::string& id)
570 : {
571 0 : return jami::closeMediaPlayer(id);
572 : }
573 :
574 : bool
575 0 : pausePlayer(const std::string& id, const bool& pause)
576 : {
577 0 : return jami::pausePlayer(id, pause);
578 : }
579 :
580 : bool
581 0 : mutePlayerAudio(const std::string& id, const bool& mute)
582 : {
583 0 : return jami::mutePlayerAudio(id, mute);
584 : }
585 :
586 : bool
587 0 : playerSeekToTime(const std::string& id, const int& time)
588 : {
589 0 : return jami::playerSeekToTime(id, time);
590 : }
591 :
592 : int64_t
593 0 : getPlayerPosition(const std::string& id)
594 : {
595 0 : return jami::getPlayerPosition(id);
596 : }
597 :
598 : int64_t
599 0 : getPlayerDuration(const std::string& id)
600 : {
601 0 : return jami::getPlayerDuration(id);
602 : }
603 :
604 : void
605 0 : setAutoRestart(const std::string& id, const bool& restart)
606 : {
607 0 : jami::setAutoRestart(id, restart);
608 0 : }
609 :
610 : bool
611 0 : getDecodingAccelerated()
612 : {
613 : #ifdef RING_ACCEL
614 0 : return jami::Manager::instance().videoPreferences.getDecodingAccelerated();
615 : #else
616 : return false;
617 : #endif
618 : }
619 :
620 : void
621 0 : setDecodingAccelerated(bool state)
622 : {
623 : #ifdef RING_ACCEL
624 0 : JAMI_DBG("%s hardware acceleration", (state ? "Enabling" : "Disabling"));
625 0 : if (jami::Manager::instance().videoPreferences.setDecodingAccelerated(state))
626 0 : jami::Manager::instance().saveConfig();
627 : #endif
628 0 : }
629 :
630 : bool
631 0 : getEncodingAccelerated()
632 : {
633 : #ifdef RING_ACCEL
634 0 : return jami::Manager::instance().videoPreferences.getEncodingAccelerated();
635 : #else
636 : return false;
637 : #endif
638 : }
639 :
640 : void
641 0 : setEncodingAccelerated(bool state)
642 : {
643 : #ifdef RING_ACCEL
644 0 : JAMI_DBG("%s hardware acceleration", (state ? "Enabling" : "Disabling"));
645 0 : if (jami::Manager::instance().videoPreferences.setEncodingAccelerated(state))
646 0 : jami::Manager::instance().saveConfig();
647 : else
648 0 : return;
649 : #endif
650 0 : for (const auto& acc : jami::Manager::instance().getAllAccounts()) {
651 0 : if (state)
652 0 : acc->setCodecActive(AV_CODEC_ID_HEVC);
653 : else
654 0 : acc->setCodecInactive(AV_CODEC_ID_HEVC);
655 : // Update and sort codecs
656 0 : acc->setActiveCodecs(acc->getActiveCodecs());
657 0 : jami::Manager::instance().saveConfig(acc);
658 0 : }
659 : }
660 :
661 : #if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS)
662 : void
663 : addVideoDevice(const std::string& node,
664 : const std::vector<std::map<std::string, std::string>>& devInfo)
665 : {
666 : jami::Manager::instance().getVideoManager().videoDeviceMonitor.addDevice(node, devInfo);
667 : }
668 :
669 : void
670 : removeVideoDevice(const std::string& node)
671 : {
672 : jami::Manager::instance().getVideoManager().videoDeviceMonitor.removeDevice(node);
673 : }
674 : #endif
675 :
676 : } // namespace libjami
677 :
678 : namespace jami {
679 :
680 : #ifdef ENABLE_VIDEO
681 : video::VideoDeviceMonitor&
682 2084 : getVideoDeviceMonitor()
683 : {
684 2084 : return Manager::instance().getVideoManager().videoDeviceMonitor;
685 : }
686 :
687 : std::shared_ptr<video::VideoInput>
688 159 : getVideoInput(const std::string& resource, video::VideoInputMode inputMode, const std::string& sink)
689 : {
690 159 : auto sinkId = sink.empty() ? resource : sink;
691 159 : auto& vmgr = Manager::instance().getVideoManager();
692 159 : std::lock_guard lk(vmgr.videoMutex);
693 159 : auto it = vmgr.videoInputs.find(sinkId);
694 159 : if (it != vmgr.videoInputs.end()) {
695 140 : if (auto input = it->second.lock()) {
696 84 : return input;
697 140 : }
698 : }
699 :
700 75 : auto input = std::make_shared<video::VideoInput>(inputMode, resource, sinkId);
701 75 : vmgr.videoInputs[sinkId] = input;
702 75 : return input;
703 159 : }
704 :
705 : void
706 0 : VideoManager::setDeviceOrientation(const std::string& deviceId, int angle)
707 : {
708 0 : videoDeviceMonitor.setDeviceOrientation(deviceId, angle);
709 0 : }
710 : #endif
711 :
712 : std::shared_ptr<AudioInput>
713 286 : getAudioInput(const std::string& device)
714 : {
715 286 : auto& vmgr = Manager::instance().getVideoManager();
716 286 : std::lock_guard lk(vmgr.audioMutex);
717 :
718 : // erase expired audio inputs
719 1041 : for (auto it = vmgr.audioInputs.cbegin(); it != vmgr.audioInputs.cend();) {
720 755 : if (it->second.expired())
721 215 : it = vmgr.audioInputs.erase(it);
722 : else
723 540 : ++it;
724 : }
725 :
726 286 : auto it = vmgr.audioInputs.find(device);
727 286 : if (it != vmgr.audioInputs.end()) {
728 41 : if (auto input = it->second.lock()) {
729 41 : return input;
730 41 : }
731 : }
732 :
733 245 : auto input = std::make_shared<AudioInput>(device);
734 245 : vmgr.audioInputs[device] = input;
735 245 : return input;
736 286 : }
737 :
738 : bool
739 0 : VideoManager::hasRunningPlayers()
740 : {
741 0 : auto& vmgr = Manager::instance().getVideoManager();
742 0 : return !vmgr.mediaPlayers.empty();
743 : }
744 :
745 : std::shared_ptr<MediaPlayer>
746 25 : getMediaPlayer(const std::string& id)
747 : {
748 25 : auto& vmgr = Manager::instance().getVideoManager();
749 25 : auto it = vmgr.mediaPlayers.find(id);
750 25 : if (it != vmgr.mediaPlayers.end()) {
751 14 : return it->second;
752 : }
753 11 : return {};
754 : }
755 :
756 : std::string
757 14 : createMediaPlayer(const std::string& path)
758 : {
759 14 : auto player = getMediaPlayer(path);
760 14 : if (!player) {
761 11 : player = std::make_shared<MediaPlayer>(path);
762 : } else {
763 3 : return path;
764 : }
765 11 : Manager::instance().getVideoManager().mediaPlayers[path] = player;
766 11 : return path;
767 14 : }
768 :
769 : bool
770 0 : pausePlayer(const std::string& id, bool pause)
771 : {
772 0 : if (auto player = getMediaPlayer(id)) {
773 0 : player->pause(pause);
774 0 : return true;
775 0 : }
776 0 : return false;
777 : }
778 :
779 : bool
780 543 : closeMediaPlayer(const std::string& id)
781 : {
782 543 : return Manager::instance().getVideoManager().mediaPlayers.erase(id) > 0;
783 : }
784 :
785 : bool
786 0 : mutePlayerAudio(const std::string& id, bool mute)
787 : {
788 0 : if (auto player = getMediaPlayer(id)) {
789 0 : player->muteAudio(mute);
790 0 : return true;
791 0 : }
792 0 : return false;
793 : }
794 :
795 : bool
796 0 : playerSeekToTime(const std::string& id, int time)
797 : {
798 0 : if (auto player = getMediaPlayer(id))
799 0 : return player->seekToTime(time);
800 0 : return false;
801 : }
802 :
803 : int64_t
804 0 : getPlayerPosition(const std::string& id)
805 : {
806 0 : if (auto player = getMediaPlayer(id))
807 0 : return player->getPlayerPosition();
808 0 : return -1;
809 : }
810 :
811 : int64_t
812 0 : getPlayerDuration(const std::string& id)
813 : {
814 0 : if (auto player = getMediaPlayer(id))
815 0 : return player->getPlayerDuration();
816 0 : return -1;
817 : }
818 :
819 : void
820 0 : setAutoRestart(const std::string& id, bool restart)
821 : {
822 0 : if (auto player = getMediaPlayer(id))
823 0 : player->setAutoRestart(restart);
824 0 : }
825 :
826 : } // namespace jami
|