diff --git a/bin/dbus/cx.ring.Ring.VideoManager.xml b/bin/dbus/cx.ring.Ring.VideoManager.xml index 99dddaaffdff82c3687a95be2b8d9c31654527ea..a866cf4ee12a6ec43e2c4b02e5eee3a490540555 100644 --- a/bin/dbus/cx.ring.Ring.VideoManager.xml +++ b/bin/dbus/cx.ring.Ring.VideoManager.xml @@ -49,9 +49,9 @@ </arg> </method> - <method name="startLocalRecorder" tp:name-for-bindings="startLocalRecorder"> - <tp:docstring> Starts a local recorder. Video and/or audio are recorded from preferred devices.</tp:docstring> - <arg type="b" name="audioOnly" direction="in"> + <method name="startLocalMediaRecorder" tp:name-for-bindings="startLocalMediaRecorder"> + <tp:docstring> Starts a local recorder. Video and/or audio.</tp:docstring> + <arg type="s" name="videoInputId" direction="in"> </arg> <arg type="s" name="filepath" direction="in"> <tp:docstring> Base file path for local recording. This is not the final path as the file extension will be added (final path can be obtained as return value of this method).</tp:docstring> @@ -63,17 +63,10 @@ <method name="stopLocalRecorder" tp:name-for-bindings="stopLocalRecorder"> <arg type="s" name="filepath" direction="in"> - <tp:docstring> Identifier for local recorder to be stopped (obtained as return value of startLocalRecorder).</tp:docstring> + <tp:docstring> Identifier for local recorder to be stopped (obtained as return value of startLocalMediaRecorder).</tp:docstring> </arg> </method> - <method name="startCamera" tp:name-for-bindings="startCamera"> - <tp:docstring> Starts the video camera, which renders the active v4l2 device's video to shared memory. Useful for testing/debugging camera settings</tp:docstring> - </method> - - <method name="stopCamera" tp:name-for-bindings="stopCamera"> - </method> - <method name="startAudioDevice" tp:name-for-bindings="startAudioDevice"> <tp:docstring> Starts the audio layer stream, so the audio device can be read.</tp:docstring> </method> @@ -81,8 +74,9 @@ <method name="stopAudioDevice" tp:name-for-bindings="stopAudioDevice"> </method> - <method name="switchInput" tp:name-for-bindings="switchInput"> - <arg type="s" name="resource" direction="in"> + <method name="openVideoInput" tp:name-for-bindings="openVideoInput"> + <tp:docstring>Open a video input by URI. A Sink is made available.</tp:docstring> + <arg type="s" name="inputUri" direction="in"> <tp:docstring> A media resource locator (MRL). Currently, the following are supported: @@ -93,8 +87,18 @@ </ul> </tp:docstring> </arg> - <arg type="b" name="switched" direction="out"> - <tp:docstring>Returns true if the input stream was successfully changed, false otherwise</tp:docstring> + <arg type="s" name="inputId" direction="out"> + <tp:docstring>The ID of the input. A sink with this ID is also made available.</tp:docstring> + </arg> + </method> + + <method name="closeVideoInput" tp:name-for-bindings="closeVideoInput"> + <tp:docstring>Closes a video input by ID</tp:docstring> + <arg type="s" name="inputId" direction="in"> + <tp:docstring>The ID of the input to close, as returned by openVideoInput.</tp:docstring> + </arg> + <arg type="b" name="closed" direction="out"> + <tp:docstring>Returns true if the input was closed</tp:docstring> </arg> </method> diff --git a/bin/dbus/dbusvideomanager.cpp b/bin/dbus/dbusvideomanager.cpp index 2218ad9cadc265398dbf1bd172c48de94d31efce..89c8543499cac6ef4d1d80b09e259d01178dcf5f 100644 --- a/bin/dbus/dbusvideomanager.cpp +++ b/bin/dbus/dbusvideomanager.cpp @@ -61,18 +61,6 @@ DBusVideoManager::getDefaultDevice() -> decltype(DRing::getDefaultDevice()) return DRing::getDefaultDevice(); } -void -DBusVideoManager::startCamera() -{ - DRing::startCamera(); -} - -void -DBusVideoManager::stopCamera() -{ - DRing::stopCamera(); -} - void DBusVideoManager::startAudioDevice() { @@ -85,10 +73,14 @@ DBusVideoManager::stopAudioDevice() DRing::stopAudioDevice(); } -auto -DBusVideoManager::switchInput(const std::string& resource) -> decltype(DRing::switchInput(resource)) -{ - return DRing::switchInput(resource); +std::string +DBusVideoManager::openVideoInput(const std::string& inputUri) { + return DRing::openVideoInput(inputUri); +} + +bool +DBusVideoManager::closeVideoInput(const std::string& inputId) { + return DRing::closeVideoInput(inputId); } auto @@ -128,9 +120,9 @@ DBusVideoManager::getRenderer(const std::string& callId) } std::string -DBusVideoManager::startLocalRecorder(const bool& audioOnly, const std::string& filepath) +DBusVideoManager::startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath) { - return DRing::startLocalRecorder(audioOnly, filepath); + return DRing::startLocalMediaRecorder(videoInputId, filepath); } void diff --git a/bin/dbus/dbusvideomanager.h b/bin/dbus/dbusvideomanager.h index 3de30452fa156170e69fb7049c32aea575207842..78fb20c8b6d72eba37ee96f5034727908cdbe579 100644 --- a/bin/dbus/dbusvideomanager.h +++ b/bin/dbus/dbusvideomanager.h @@ -57,19 +57,18 @@ class DRING_PUBLIC DBusVideoManager : void applySettings(const std::string& deviceId, const std::map<std::string, std::string>& settings); void setDefaultDevice(const std::string& deviceId); std::string getDefaultDevice(); - void startCamera(); - void stopCamera(); void startAudioDevice(); void stopAudioDevice(); - bool switchInput(const std::string& resource); bool getDecodingAccelerated(); void setDecodingAccelerated(const bool& state); bool getEncodingAccelerated(); void setEncodingAccelerated(const bool& state); void setDeviceOrientation(const std::string& deviceId, const int& angle); std::map<std::string, std::string> getRenderer(const std::string& callId); - std::string startLocalRecorder(const bool& audioOnly, const std::string& filepath); + std::string startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath); void stopLocalRecorder(const std::string& filepath); + std::string openVideoInput(const std::string& inputUri); + bool closeVideoInput(const std::string& inputId); }; #endif // __RING_DBUSVIDEOMANAGER_H__ diff --git a/bin/jni/videomanager.i b/bin/jni/videomanager.i index 5f853489cbe50221ada2f6d6f8e822aa2152be9e..71f4ee589c0a9115d9c97c1008f201076fc45bcc 100644 --- a/bin/jni/videomanager.i +++ b/bin/jni/videomanager.i @@ -149,10 +149,14 @@ int AndroidFormatToAVFormat(int androidformat) { } } -JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoPacket(JNIEnv *jenv, jclass jcls, jobject buffer, jint size, jint offset, jboolean keyframe, jlong timestamp, jint rotation) +JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoPacket(JNIEnv *jenv, jclass jcls, jstring inputId, jobject buffer, jint size, jint offset, jboolean keyframe, jlong timestamp, jint rotation) { try { - auto frame = DRing::getNewFrame(); + const char *inputId_pstr = (const char *)jenv->GetStringUTFChars(inputId, 0); + if (!inputId_pstr) + return; + std::string_view input(inputId_pstr); + auto frame = DRing::getNewFrame(input); if (not frame) return; auto packet = std::unique_ptr<AVPacket, void(*)(AVPacket*)>(new AVPacket, [](AVPacket* pkt){ @@ -174,13 +178,13 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoPacket(JN packet->size = size; packet->pts = timestamp; frame->setPacket(std::move(packet)); - DRing::publishFrame(); + DRing::publishFrame(input); } catch (const std::exception& e) { __android_log_print(ANDROID_LOG_ERROR, TAG, "Exception capturing video packet: %s", e.what()); } } -JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoFrame(JNIEnv *jenv, jclass jcls, jobject image, jint rotation) +JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoFrame(JNIEnv *jenv, jclass jcls, jstring inputId, jobject image, jint rotation) { static jclass imageClass = jenv->GetObjectClass(image); static jmethodID imageGetFormat = jenv->GetMethodID(imageClass, "getFormat", "()I"); @@ -189,9 +193,18 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoFrame(JNI static jmethodID imageGetCropRect = jenv->GetMethodID(imageClass, "getCropRect", "()Landroid/graphics/Rect;"); static jmethodID imageGetPlanes = jenv->GetMethodID(imageClass, "getPlanes", "()[Landroid/media/Image$Plane;"); static jmethodID imageClose = jenv->GetMethodID(imageClass, "close", "()V"); + if(!inputId) { + SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string"); + return; + } try { - auto frame = DRing::getNewFrame(); + const char *inputId_pstr = (const char *)jenv->GetStringUTFChars(inputId, 0); + if (!inputId_pstr) + return; + std::string_view input(inputId_pstr); + + auto frame = DRing::getNewFrame(input); if (not frame) { jenv->CallVoidMethod(image, imageClose); return; @@ -277,7 +290,8 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_captureVideoFrame(JNI if (justAttached) gJavaVM->DetachCurrentThread(); }); - DRing::publishFrame(); + DRing::publishFrame(input); + jenv->ReleaseStringUTFChars(inputId, inputId_pstr); } catch (const std::exception& e) { __android_log_print(ANDROID_LOG_ERROR, TAG, "Exception capturing video frame: %s", e.what()); } @@ -352,7 +366,7 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_registerVideoCallback const char *arg1_pstr = (const char *)jenv->GetStringUTFChars(sinkId, 0); if (!arg1_pstr) return; - const std::string sink(arg1_pstr); + std::string sink(arg1_pstr); jenv->ReleaseStringUTFChars(sinkId, arg1_pstr); ANativeWindow* nativeWindow = (ANativeWindow*)((intptr_t) window); @@ -395,19 +409,16 @@ JNIEXPORT void JNICALL Java_net_jami_daemon_JamiServiceJNI_unregisterVideoCallba %native(registerVideoCallback) void registerVideoCallback(jstring, jlong); %native(unregisterVideoCallback) void unregisterVideoCallback(jstring, jlong); -%native(captureVideoFrame) void captureVideoFrame(jobject, jint); -%native(captureVideoPacket) void captureVideoPacket(jobject, jint, jint, jboolean, jlong, jint); +%native(captureVideoFrame) void captureVideoFrame(jstring, jobject, jint); +%native(captureVideoPacket) void captureVideoPacket(jstring, jobject, jint, jint, jboolean, jlong, jint); namespace DRing { void setDefaultDevice(const std::string& name); std::string getDefaultDevice(); -void startCamera(); -void stopCamera(); void startAudioDevice(); void stopAudioDevice(); -bool switchInput(const std::string& resource); std::map<std::string, std::string> getSettings(const std::string& name); void applySettings(const std::string& name, const std::map<std::string, std::string>& settings); @@ -415,12 +426,15 @@ void addVideoDevice(const std::string &node); void removeVideoDevice(const std::string &node); void setDeviceOrientation(const std::string& name, int angle); void registerSinkTarget(const std::string& sinkId, const DRing::SinkTarget& target); -std::string startLocalRecorder(const bool& audioOnly, const std::string& filepath); +std::string startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath); void stopLocalRecorder(const std::string& filepath); bool getDecodingAccelerated(); void setDecodingAccelerated(bool state); bool getEncodingAccelerated(); void setEncodingAccelerated(bool state); + +std::string openVideoInput(const std::string& path); +bool closeVideoInput(const std::string& id); } class VideoCallback { diff --git a/bin/nodejs/videomanager.i b/bin/nodejs/videomanager.i index 7cce141216a565748bda37926175d66bd6c37b46..3d55c272b96da7b5c7952a87651e87b70c4a5153 100644 --- a/bin/nodejs/videomanager.i +++ b/bin/nodejs/videomanager.i @@ -46,11 +46,8 @@ namespace DRing { void setDefaultDevice(const std::string& name); std::string getDefaultDevice(); -void startCamera(); -void stopCamera(); void startAudioDevice(); void stopAudioDevice(); -bool switchInput(const std::string& resource); std::map<std::string, std::string> getSettings(const std::string& name); void applySettings(const std::string& name, const std::map<std::string, std::string>& settings); diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp index 3e230ebb3aa27918708f45c9daf193014dd08532..91980fb791e4176461cb5a518a17605ad4e15880 100644 --- a/src/client/videomanager.cpp +++ b/src/client/videomanager.cpp @@ -350,7 +350,8 @@ VideoFrame::getOrientation() const { int32_t* matrix {nullptr}; if (auto p = packet()) { - matrix = reinterpret_cast<int32_t*>(av_packet_get_side_data(p, AV_PKT_DATA_DISPLAYMATRIX, nullptr)); + matrix = reinterpret_cast<int32_t*>( + av_packet_get_side_data(p, AV_PKT_DATA_DISPLAYMATRIX, nullptr)); } else if (auto p = pointer()) { if (AVFrameSideData* side_data = av_frame_get_side_data(p, AV_FRAME_DATA_DISPLAYMATRIX)) { matrix = reinterpret_cast<int32_t*>(side_data->data); @@ -358,24 +359,26 @@ VideoFrame::getOrientation() const } if (matrix) { double angle = av_display_rotation_get(matrix); - return std::isnan(angle) ? 0 : -(int)angle; + return std::isnan(angle) ? 0 : -(int) angle; } return 0; } VideoFrame* -getNewFrame() +getNewFrame(std::string_view id) { - if (auto input = jami::Manager::instance().getVideoManager().videoInput.lock()) + if (auto input = jami::Manager::instance().getVideoManager().getVideoInput(id)) return &input->getNewFrame(); + JAMI_WARN("getNewFrame: can't find input %.*s", (int) id.size(), id.data()); return nullptr; } void -publishFrame() +publishFrame(std::string_view id) { - if (auto input = jami::Manager::instance().getVideoManager().videoInput.lock()) + if (auto input = jami::Manager::instance().getVideoManager().getVideoInput(id)) return input->publishFrame(); + JAMI_WARN("publishFrame: can't find input %.*s", (int) id.size(), id.data()); } void @@ -446,57 +449,58 @@ applySettings(const std::string& deviceId, const std::map<std::string, std::stri } void -startCamera() +startAudioDevice() { - jami::Manager::instance().getVideoManager().videoPreview = jami::getVideoCamera(); - jami::Manager::instance().getVideoManager().started = switchInput( - jami::Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice()); + auto newPreview = jami::getAudioInput(jami::RingBufferPool::DEFAULT_ID); + jami::Manager::instance().getVideoManager().audioPreview = newPreview; + newPreview->switchInput(""); } void -stopCamera() +stopAudioDevice() { - jami::Manager::instance().getVideoManager().started = false; - jami::Manager::instance().getVideoManager().videoPreview.reset(); + jami::Manager::instance().getVideoManager().audioPreview.reset(); } -void -startAudioDevice() +std::string +openVideoInput(const std::string& path) { - jami::Manager::instance().getVideoManager().audioPreview = jami::getAudioInput( - jami::RingBufferPool::DEFAULT_ID); - jami::Manager::instance().getVideoManager().audioPreview->switchInput(""); + auto& vm = jami::Manager::instance().getVideoManager(); + + auto id = path.empty() ? vm.videoDeviceMonitor.getMRLForDefaultDevice() : path; + auto& input = vm.clientVideoInputs[id]; + if (not input) { + input = jami::getVideoInput(id); + } + return id; } -void -stopAudioDevice() +bool +closeVideoInput(const std::string& id) { - jami::Manager::instance().getVideoManager().audioPreview.reset(); + return jami::Manager::instance().getVideoManager().clientVideoInputs.erase(id) > 0; } std::string -startLocalRecorder(const bool& audioOnly, const std::string& filepath) +startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath) { - if (!audioOnly && !jami::Manager::instance().getVideoManager().started) { - JAMI_ERR("Couldn't start local video recorder (camera is not started)"); - return ""; - } - - auto rec = std::make_unique<jami::LocalRecorder>(audioOnly); + auto rec = std::make_unique<jami::LocalRecorder>(videoInputId); rec->setPath(filepath); // retrieve final path (containing file extension) auto path = rec->getPath(); + auto& recordManager = jami::LocalRecorderManager::instance(); + try { - jami::LocalRecorderManager::instance().insertRecorder(path, std::move(rec)); + recordManager.insertRecorder(path, std::move(rec)); } catch (const std::invalid_argument&) { return ""; } - auto ret = jami::LocalRecorderManager::instance().getRecorderByPath(path)->startRecording(); + auto ret = recordManager.getRecorderByPath(path)->startRecording(); if (!ret) { - jami::LocalRecorderManager::instance().removeRecorderByPath(filepath); + recordManager.removeRecorderByPath(filepath); return ""; } @@ -516,20 +520,6 @@ stopLocalRecorder(const std::string& filepath) jami::LocalRecorderManager::instance().removeRecorderByPath(filepath); } -bool -switchInput(const std::string& resource) -{ - bool ret = true; - if (auto input = jami::Manager::instance().getVideoManager().videoInput.lock()) - ret = input->switchInput(resource).valid(); - else - JAMI_WARN("Video input not initialized"); - - if (auto input = jami::Manager::instance().getVideoManager().audioPreview) - ret &= input->switchInput(resource).valid(); - return ret; -} - void registerSinkTarget(const std::string& sinkId, const SinkTarget& target) { @@ -673,19 +663,6 @@ removeVideoDevice(const std::string& node) namespace jami { -std::shared_ptr<video::VideoFrameActiveWriter> -getVideoCamera() -{ - auto& vmgr = Manager::instance().getVideoManager(); - if (auto input = vmgr.videoInput.lock()) - return input; - - vmgr.started = false; - auto input = std::make_shared<video::VideoInput>(); - vmgr.videoInput = input; - return input; -} - video::VideoDeviceMonitor& getVideoDeviceMonitor() { diff --git a/src/client/videomanager.h b/src/client/videomanager.h index d26e4a18f7c091ae3436ccd0bdc22e60e664c16d..04210fea817a769d5a1cf01a52b4ed92f13a1b18 100644 --- a/src/client/videomanager.h +++ b/src/client/videomanager.h @@ -16,8 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef VIDEOMANAGER_H_ -#define VIDEOMANAGER_H_ +#pragma once #ifdef HAVE_CONFIG_H #include "config.h" @@ -42,37 +41,31 @@ struct VideoManager public: void setDeviceOrientation(const std::string& deviceId, int angle); - /** - * VideoManager acts as a cache of the active VideoInput. - * When this input is needed, you must use getVideoCamera - * to create the instance if not done yet and obtain a shared pointer - * for your own usage. - * VideoManager instance doesn't increment the reference count of - * this video input instance: this instance is destroyed when the last - * external user has released its shared pointer. - */ - std::weak_ptr<video::VideoInput> videoInput; - std::shared_ptr<video::VideoFrameActiveWriter> videoPreview; + // Client-managed video inputs and players + std::map<std::string, std::shared_ptr<video::VideoInput>> clientVideoInputs; + std::map<std::string, std::shared_ptr<MediaPlayer>> mediaPlayers; + // Client-managed audio preview + std::shared_ptr<AudioInput> audioPreview; + + // device monitor video::VideoDeviceMonitor videoDeviceMonitor; - std::atomic_bool started; + /** - * VideoManager also acts as a cache of the active AudioInput(s). - * When one of these is needed, you must use getAudioInput, which will - * create an instance if need be and return a shared_ptr. + * Cache of the active Audio/Video input(s). */ - std::map<std::string, std::weak_ptr<AudioInput>> audioInputs; - std::map<std::string, std::weak_ptr<video::VideoInput>> videoInputs; - std::map<std::string, std::shared_ptr<MediaPlayer>> mediaPlayers; + std::map<std::string, std::weak_ptr<AudioInput>, std::less<>> audioInputs; + std::map<std::string, std::weak_ptr<video::VideoInput>, std::less<>> videoInputs; std::mutex audioMutex; - std::shared_ptr<AudioInput> audioPreview; bool hasRunningPlayers(); + std::shared_ptr<video::VideoInput> getVideoInput(std::string_view id) const { + auto input = videoInputs.find(id); + return input == videoInputs.end() ? nullptr : input->second.lock(); + } }; -std::shared_ptr<video::VideoFrameActiveWriter> getVideoCamera(); video::VideoDeviceMonitor& getVideoDeviceMonitor(); std::shared_ptr<AudioInput> getAudioInput(const std::string& id); -std::shared_ptr<video::VideoInput> getVideoInput( - const std::string& id, video::VideoInputMode inputMode = video::VideoInputMode::Undefined); +std::shared_ptr<video::VideoInput> getVideoInput(const std::string& id, video::VideoInputMode inputMode = video::VideoInputMode::Undefined); std::string createMediaPlayer(const std::string& path); std::shared_ptr<MediaPlayer> getMediaPlayer(const std::string& id); bool pausePlayer(const std::string& id, bool pause); @@ -82,5 +75,3 @@ bool playerSeekToTime(const std::string& id, int time); int64_t getPlayerPosition(const std::string& id); } // namespace jami - -#endif // VIDEOMANAGER_H_ diff --git a/src/conference.cpp b/src/conference.cpp index fa8626f09de6add20e5f8d49c78ef2ba487e16e1..f6245fc91a3dce7498f83f54cf702ea03530b9e5 100644 --- a/src/conference.cpp +++ b/src/conference.cpp @@ -66,7 +66,7 @@ Conference::Conference(bool enableVideo) if (not videoEnabled_) return; - videoMixer_ = std::make_shared<video::VideoMixer>(id_); + videoMixer_ = std::make_shared<video::VideoMixer>(id_, mediaInput_); videoMixer_->setOnSourcesUpdated([this](std::vector<video::SourceInfo>&& infos) { runOnMainThread([w = weak(), infos = std::move(infos)] { auto shared = w.lock(); diff --git a/src/jami/videomanager_interface.h b/src/jami/videomanager_interface.h index ff82e011e07b4c8a563768d04d1eb1a2b61eedd7..337c9d10dd3e1d8409ab26ecf4751f1f688c0eda 100644 --- a/src/jami/videomanager_interface.h +++ b/src/jami/videomanager_interface.h @@ -192,23 +192,24 @@ DRING_PUBLIC void setDefaultDevice(const std::string& deviceId); DRING_PUBLIC void setDeviceOrientation(const std::string& deviceId, int angle); DRING_PUBLIC std::map<std::string, std::string> getDeviceParams(const std::string& deviceId); DRING_PUBLIC std::string getDefaultDevice(); -DRING_PUBLIC void startCamera(); -DRING_PUBLIC void stopCamera(); DRING_PUBLIC void startAudioDevice(); DRING_PUBLIC void stopAudioDevice(); + +DRING_PUBLIC std::string openVideoInput(const std::string& path); +DRING_PUBLIC bool closeVideoInput(const std::string& id); + DRING_PUBLIC std::string createMediaPlayer(const std::string& path); +DRING_PUBLIC std::string closeMediaPlayer(const std::string& id); DRING_PUBLIC bool pausePlayer(const std::string& id, bool pause); -DRING_PUBLIC bool closePlayer(const std::string& id); DRING_PUBLIC bool mutePlayerAudio(const std::string& id, bool mute); DRING_PUBLIC bool playerSeekToTime(const std::string& id, int time); int64_t getPlayerPosition(const std::string& id); -DRING_PUBLIC bool switchInput(const std::string& resource); DRING_PUBLIC void registerSinkTarget(const std::string& sinkId, const SinkTarget& target); DRING_PUBLIC void registerAVSinkTarget(const std::string& sinkId, const AVSinkTarget& target); DRING_PUBLIC std::map<std::string, std::string> getRenderer(const std::string& callId); -DRING_PUBLIC std::string startLocalRecorder(const bool& audioOnly, const std::string& filepath); +DRING_PUBLIC std::string startLocalMediaRecorder(const std::string& videoInputId, const std::string& filepath); DRING_PUBLIC void stopLocalRecorder(const std::string& filepath); #if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) @@ -216,8 +217,8 @@ DRING_PUBLIC void addVideoDevice( const std::string& node, const std::vector<std::map<std::string, std::string>>& devInfo = {}); DRING_PUBLIC void removeVideoDevice(const std::string& node); -DRING_PUBLIC VideoFrame* getNewFrame(); -DRING_PUBLIC void publishFrame(); +DRING_PUBLIC VideoFrame* getNewFrame(std::string_view id); +DRING_PUBLIC void publishFrame(std::string_view id); #endif DRING_PUBLIC bool getDecodingAccelerated(); diff --git a/src/media/localrecorder.cpp b/src/media/localrecorder.cpp index 536ac7c5a08d36120fe1f48b84113e1b4929d833..a3b82c06f0d10f4da296758ca14a2bdc3a50701e 100644 --- a/src/media/localrecorder.cpp +++ b/src/media/localrecorder.cpp @@ -30,10 +30,11 @@ namespace jami { -LocalRecorder::LocalRecorder(const bool& audioOnly) +LocalRecorder::LocalRecorder(const std::string& inputUri) { - isAudioOnly_ = audioOnly; - recorder_->audioOnly(audioOnly); + inputUri_ = inputUri; + isAudioOnly_ = inputUri_.empty(); + recorder_->audioOnly(isAudioOnly_); } LocalRecorder::~LocalRecorder() @@ -76,7 +77,7 @@ LocalRecorder::startRecording() // create read offset in RingBuffer Manager::instance().getRingBufferPool().bindHalfDuplexOut(path_, RingBufferPool::DEFAULT_ID); - audioInput_ = jami::getAudioInput(path_); + audioInput_ = getAudioInput(path_); audioInput_->setFormat(AudioFormat::STEREO()); audioInput_->attach(recorder_->addStream(audioInput_->getInfo())); audioInput_->switchInput(""); @@ -84,7 +85,7 @@ LocalRecorder::startRecording() #ifdef ENABLE_VIDEO // video recording if (!isAudioOnly_) { - videoInput_ = std::static_pointer_cast<video::VideoInput>(jami::getVideoCamera()); + videoInput_ = std::static_pointer_cast<video::VideoInput>(getVideoInput(inputUri_)); if (videoInput_) { videoInput_->attach(recorder_->addStream(videoInput_->getInfo())); } else { diff --git a/src/media/localrecorder.h b/src/media/localrecorder.h index 1aa5370f34b824ba2a1238c1f78c9bc8641b9fdf..f4851a952b8cd1d0de61e8eecee4f1204c67e020 100644 --- a/src/media/localrecorder.h +++ b/src/media/localrecorder.h @@ -40,7 +40,7 @@ namespace jami { class LocalRecorder : public Recordable { public: - LocalRecorder(const bool& audioOnly); + LocalRecorder(const std::string& inputUri); ~LocalRecorder(); /** @@ -57,10 +57,11 @@ public: /** * Stops recording. */ - void stopRecording(); + void stopRecording() override; private: std::string path_; + std::string inputUri_; // media inputs std::shared_ptr<jami::video::VideoInput> videoInput_; diff --git a/src/media/media_recorder.cpp b/src/media/media_recorder.cpp index 2f13a1afabfc739751ecb97b683ee6220afd241f..415865af43342c055075b0bb546c420761d044f6 100644 --- a/src/media/media_recorder.cpp +++ b/src/media/media_recorder.cpp @@ -407,6 +407,10 @@ MediaRecorder::setupVideoOutput() int ret = -1; int streams = peer.isValid() + local.isValid() + mixer.isValid(); switch (streams) { + case 0: { + JAMI_ERR("Trying to record a stream but none is valid"); + break; + } case 1: { MediaStream inputStream; if (peer.isValid()) diff --git a/src/media/recordable.h b/src/media/recordable.h index 5184f3f231a63b1c956b434c18ec8f2f97c83349..63c7432fa4b3fe534bd7bca1de615198cc70d9bb 100644 --- a/src/media/recordable.h +++ b/src/media/recordable.h @@ -55,7 +55,7 @@ public: /** * Stop recording */ - void stopRecording(); + virtual void stopRecording(); /** * Start recording diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp index f4af01384253b20017f342dab8d3c81139bb4809..aad46f712c5db23085838f30cb3f30558dc98149 100644 --- a/src/media/video/video_input.cpp +++ b/src/media/video/video_input.cpp @@ -76,6 +76,7 @@ VideoInput::VideoInput(VideoInputMode inputMode, const std::string& id_) sink_ = Manager::instance().createSinkClient(id_); } #endif + switchInput(id_); } VideoInput::~VideoInput() @@ -576,12 +577,6 @@ VideoInput::switchInput(const std::string& resource) return futureDecOpts_; } -const DeviceParams& -VideoInput::getParams() const -{ - return decOpts_; -} - MediaStream VideoInput::getInfo() const { diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h index d0d3f3207c94432722ec36177cd1305001bd1498..f63a997eb2a9a6fbf14dc5abf8908ba06a75a087 100644 --- a/src/media/video/video_input.h +++ b/src/media/video/video_input.h @@ -65,7 +65,14 @@ public: int getWidth() const; int getHeight() const; AVPixelFormat getPixelFormat() const; - const DeviceParams& getParams() const; + + const DeviceParams& getConfig() const { + return decOpts_; + } + std::shared_future<DeviceParams> getParams() const { + return futureDecOpts_; + } + MediaStream getInfo() const; void setSink(const std::string& sinkId); @@ -80,7 +87,6 @@ public: void setupSink(); void stopSink(); - std::shared_future<DeviceParams> switchInput(const std::string& resource); #if VIDEO_CLIENT_INPUT /* * these functions are used to pass buffer from/to the daemon @@ -98,6 +104,8 @@ public: private: NON_COPYABLE(VideoInput); + std::shared_future<DeviceParams> switchInput(const std::string& resource); + std::string id_; std::string currentResource_; std::atomic<bool> switchPending_ = {false}; diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp index 0540436d50e790615a876b3ab158750357ee1fbb..b8e030e412edc4e4c0773ac54b4fba30c242fc80 100644 --- a/src/media/video/video_mixer.cpp +++ b/src/media/video/video_mixer.cpp @@ -78,14 +78,15 @@ private: static constexpr const auto MIXER_FRAMERATE = 30; static constexpr const auto FRAME_DURATION = std::chrono::duration<double>(1. / MIXER_FRAMERATE); -VideoMixer::VideoMixer(const std::string& id) +VideoMixer::VideoMixer(const std::string& id, const std::string& localInput) : VideoGenerator::VideoGenerator() , id_(id) , sink_(Manager::instance().createSinkClient(id, true)) , loop_([] { return true; }, std::bind(&VideoMixer::process, this), [] {}) { // Local video camera is the main participant - videoLocal_ = getVideoCamera(); + if (not localInput.empty()) + videoLocal_ = getVideoInput(localInput); if (videoLocal_) videoLocal_->attach(this); loop_.start(); @@ -128,8 +129,6 @@ VideoMixer::switchInput(const std::string& input) localInput->stopInput(); } #endif - } else { - videoLocal_ = getVideoCamera(); } if (input.empty()) { @@ -138,12 +137,9 @@ VideoMixer::switchInput(const std::string& input) } // Re-attach videoInput to mixer - if (videoLocal_) { - if (auto localInput = std::dynamic_pointer_cast<VideoInput>(videoLocal_)) { - localInput->switchInput(input); - } + videoLocal_ = getVideoInput(input); + if (videoLocal_) videoLocal_->attach(this); - } } void @@ -159,7 +155,6 @@ VideoMixer::switchSecondaryInput(const std::string& input) } #endif } - videoLocalSecondary_ = getVideoInput(input); if (input.empty()) { JAMI_DBG("[mixer:%s] Input is empty, don't add it in the mixer", id_.c_str()); @@ -167,9 +162,8 @@ VideoMixer::switchSecondaryInput(const std::string& input) } // Re-attach videoInput to mixer + videoLocalSecondary_ = getVideoInput(input); if (videoLocalSecondary_) { - if (auto videoInput = std::dynamic_pointer_cast<VideoInput>(videoLocalSecondary_)) - videoInput->switchInput(input); videoLocalSecondary_->attach(this); } } diff --git a/src/media/video/video_mixer.h b/src/media/video/video_mixer.h index 872674670b6dfddee03585b0c62a0b5832ba7238..bf2d56b60e6ff78685059ba3cb368ced5590ca53 100644 --- a/src/media/video/video_mixer.h +++ b/src/media/video/video_mixer.h @@ -53,7 +53,7 @@ enum class Layout { GRID, ONE_BIG_WITH_SMALL, ONE_BIG }; class VideoMixer : public VideoGenerator, public VideoFramePassiveReader { public: - VideoMixer(const std::string& id); + VideoMixer(const std::string& id, const std::string& localInput = {}); ~VideoMixer(); void setParameters(int width, int height, AVPixelFormat format = AV_PIX_FMT_YUV422P); diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index de414386c260e0f084376f9123e6fad5e658be17..9e6667511b947b90adc7b5b2d59d09170624a6cd 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -112,11 +112,10 @@ VideoRtpSession::startSender() } if (not conference_) { - videoLocal_ = getVideoCamera(); - if (auto input = Manager::instance().getVideoManager().videoInput.lock()) { - std::static_pointer_cast<VideoInput>(videoLocal_) - ->setSuccessfulSetupCb(onSuccessfulSetup_); - auto newParams = input->switchInput(input_); + auto input = getVideoInput(input_); + videoLocal_ = input; + if (input) { + auto newParams = input->getParams(); try { if (newParams.valid() && newParams.wait_for(NEWPARAMS_TIMEOUT) == std::future_status::ready) { @@ -584,7 +583,7 @@ VideoRtpSession::setNewBitrate(unsigned int newBR) #if __ANDROID__ if (auto input_device = std::dynamic_pointer_cast<VideoInput>(videoLocal_)) - emitSignal<DRing::VideoSignal::SetBitrate>(input_device->getParams().name, (int) newBR); + emitSignal<DRing::VideoSignal::SetBitrate>(input_device->getConfig().name, (int) newBR); #endif if (sender_) { diff --git a/tools/jamictrl/jamictrl.py b/tools/jamictrl/jamictrl.py index 78d3db3f92bda685d3181804a78be661785a7cad..0af5605d3c79569c4917f06bf1368af701255f1e 100755 --- a/tools/jamictrl/jamictrl.py +++ b/tools/jamictrl/jamictrl.py @@ -233,9 +233,9 @@ if __name__ == "__main__": import time while True: time.sleep(2) - ctrl.videomanager.startCamera() + id = ctrl.videomanager.openVideoInput("") time.sleep(2) - ctrl.videomanager.stopCamera() + ctrl.videomanager.closeVideoInput(id) if len(sys.argv) == 1 or ctrl.autoAnswer: signal.signal(signal.SIGINT, ctrl.interruptHandler) diff --git a/tools/jamictrl/toggle_video_preview.py b/tools/jamictrl/toggle_video_preview.py index 6e7f96a33a4228a4e882121cec0beefde5e539dd..1829e3f67fbf2c6ae5a98dd60f7cb46d3ca229dc 100755 --- a/tools/jamictrl/toggle_video_preview.py +++ b/tools/jamictrl/toggle_video_preview.py @@ -25,7 +25,6 @@ import sys import os from random import randint - class DRingToggleVideo(): def start(self): bus = dbus.SessionBus() @@ -35,6 +34,6 @@ class DRingToggleVideo(): while True: time.sleep(2) - videoControl.startCamera() + id = videoControl.openVideoInput("") time.sleep(2) - videoControl.stopCamera() + videoControl.closeVideoInput(id)