diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp index e7f16077a17a69728627b05ac2cc8df99a6d069e..58ad403a86280a5efb3c75dbd33df35f42cccafd 100644 --- a/src/media/audio/audio_rtp_session.cpp +++ b/src/media/audio/audio_rtp_session.cpp @@ -196,23 +196,25 @@ AudioRtpSession::setMuted(bool isMuted) void AudioRtpSession::initRecorder(std::shared_ptr<MediaRecorder>& rec) { - if (receiveThread_) { - receiveThread_->attach(rec.get()); - rec->addStream(receiveThread_->getInfo()); - } - if (auto input = ring::getAudioInput(callID_)) { - input->attach(rec.get()); - rec->addStream(input->getInfo()); - } + if (receiveThread_) + receiveThread_->attach(rec->addStream(receiveThread_->getInfo())); + if (auto input = ring::getAudioInput(callID_)) + input->attach(rec->addStream(input->getInfo())); } void AudioRtpSession::deinitRecorder(std::shared_ptr<MediaRecorder>& rec) { - if (receiveThread_) - receiveThread_->detach(rec.get()); - if (auto input = ring::getAudioInput(callID_)) - input->detach(rec.get()); + if (receiveThread_) { + if (auto ob = rec->getStream(receiveThread_->getInfo().name)) { + receiveThread_->detach(ob); + } + } + if (auto input = ring::getAudioInput(callID_)) { + if (auto ob = rec->getStream(input->getInfo().name)) { + input->detach(ob); + } + } } } // namespace ring diff --git a/src/media/localrecorder.cpp b/src/media/localrecorder.cpp index 8eaed6c993829aa3ab989c544cda7e7a9b589f0b..4a995e9c16e470e841babafd6d841fd85d2af259 100644 --- a/src/media/localrecorder.cpp +++ b/src/media/localrecorder.cpp @@ -73,16 +73,14 @@ LocalRecorder::startRecording() audioInput_ = ring::getAudioInput(path_); audioInput_->setFormat(AudioFormat::STEREO()); - recorder_->addStream(audioInput_->getInfo()); - audioInput_->attach(recorder_.get()); + audioInput_->attach(recorder_->addStream(audioInput_->getInfo())); #ifdef RING_VIDEO // video recording if (!isAudioOnly_) { videoInput_ = std::static_pointer_cast<video::VideoInput>(ring::getVideoCamera()); if (videoInput_) { - recorder_->addStream(videoInput_->getInfo()); - videoInput_->attach(recorder_.get()); + videoInput_->attach(recorder_->addStream(videoInput_->getInfo())); } else { RING_ERR() << "Unable to record video (no video input)"; return false; @@ -98,9 +96,11 @@ LocalRecorder::stopRecording() { Recordable::stopRecording(); Manager::instance().getRingBufferPool().unBindHalfDuplexOut(path_, RingBufferPool::DEFAULT_ID); - audioInput_->detach(recorder_.get()); + if (auto ob = recorder_->getStream(audioInput_->getInfo().name)) + audioInput_->detach(ob); if (videoInput_) - videoInput_->detach(recorder_.get()); + if (auto ob = recorder_->getStream(videoInput_->getInfo().name)) + videoInput_->detach(ob); audioInput_.reset(); videoInput_.reset(); } diff --git a/src/media/media_recorder.cpp b/src/media/media_recorder.cpp index baf35522ece8469c2dd8df3b3ea1b66be3833bba..1b8005fdd9c47f3394fc21dff97916ec85959497 100644 --- a/src/media/media_recorder.cpp +++ b/src/media/media_recorder.cpp @@ -19,9 +19,6 @@ */ #include "libav_deps.h" // MUST BE INCLUDED FIRST -#include "audio/audio_input.h" -#include "audio/audio_receive_thread.h" -#include "audio/audio_sender.h" #include "client/ring_signal.h" #include "fileutils.h" #include "logger.h" @@ -29,8 +26,6 @@ #include "media_recorder.h" #include "system_codec_container.h" #include "thread_pool.h" -#include "video/video_input.h" -#include "video/video_receive_thread.h" #include <algorithm> #include <iomanip> @@ -119,45 +114,50 @@ MediaRecorder::stopRecording() } } -int +Observer<std::shared_ptr<MediaFrame>>* MediaRecorder::addStream(const MediaStream& ms) { if (audioOnly_ && ms.isVideo) { RING_ERR() << "Trying to add video stream to audio only recording"; - return -1; + return nullptr; } - if (streams_.insert(std::make_pair(ms.name, ms)).second) { + auto ptr = std::make_unique<StreamObserver>(ms, [this, ms](const std::shared_ptr<MediaFrame>& frame) { + onFrame(ms.name, frame); + }); + auto p = streams_.insert(std::make_pair(ms.name, std::move(ptr))); + if (p.second) { RING_DBG() << "Recorder input #" << streams_.size() << ": " << ms; if (ms.isVideo) hasVideo_ = true; else hasAudio_ = true; - return 0; + return p.first->second.get(); } else { - RING_ERR() << "Could not add stream '" << ms.name << "' to record"; - return -1; + RING_WARN() << "Recorder already has '" << ms.name << "' as input"; + return p.first->second.get(); } } +Observer<std::shared_ptr<MediaFrame>>* +MediaRecorder::getStream(const std::string& name) const +{ + const auto it = streams_.find(name); + if (it != streams_.cend()) + return it->second.get(); + return nullptr; +} + void -MediaRecorder::update(Observable<std::shared_ptr<MediaFrame>>* ob, const std::shared_ptr<MediaFrame>& m) +MediaRecorder::onFrame(const std::string& name, const std::shared_ptr<MediaFrame>& frame) { if (!isRecording_) return; - std::string name; - if (dynamic_cast<AudioReceiveThread*>(ob)) - name = "a:remote"; - else if (dynamic_cast<AudioInput*>(ob)) - name = "a:local"; - else if (dynamic_cast<video::VideoReceiveThread*>(ob)) - name = "v:remote"; - else if (dynamic_cast<video::VideoInput*>(ob)) - name = "v:local"; + // copy frame to not mess with the original frame's pts (does not actually copy frame data) MediaFrame clone; - clone.copyFrom(*m); - clone.pointer()->pts -= streams_[name].firstTimestamp; + clone.copyFrom(*frame); + clone.pointer()->pts -= streams_[name]->info.firstTimestamp; if (clone.pointer()->width > 0 && clone.pointer()->height > 0) videoFilter_->feedInput(clone.pointer(), name); else @@ -262,19 +262,19 @@ MediaRecorder::setupVideoOutput() { MediaStream encoderStream, peer, local; auto it = std::find_if(streams_.begin(), streams_.end(), [](const auto& pair){ - return pair.second.isVideo && pair.second.name.find("remote") != std::string::npos; + return pair.second->info.isVideo && pair.second->info.name.find("remote") != std::string::npos; }); if (it != streams_.end()) { - peer = it->second; + peer = it->second->info; } it = std::find_if(streams_.begin(), streams_.end(), [](const auto& pair){ - return pair.second.isVideo && pair.second.name.find("local") != std::string::npos; + return pair.second->info.isVideo && pair.second->info.name.find("local") != std::string::npos; }); if (it != streams_.end()) { - local = it->second; + local = it->second->info; } // vp8 supports only yuv420p @@ -348,19 +348,19 @@ MediaRecorder::setupAudioOutput() { MediaStream encoderStream, peer, local; auto it = std::find_if(streams_.begin(), streams_.end(), [](const auto& pair){ - return !pair.second.isVideo && pair.second.name.find("remote") != std::string::npos; + return !pair.second->info.isVideo && pair.second->info.name.find("remote") != std::string::npos; }); if (it != streams_.end()) { - peer = it->second; + peer = it->second->info; } it = std::find_if(streams_.begin(), streams_.end(), [](const auto& pair){ - return !pair.second.isVideo && pair.second.name.find("local") != std::string::npos; + return !pair.second->info.isVideo && pair.second->info.name.find("local") != std::string::npos; }); if (it != streams_.end()) { - local = it->second; + local = it->second->info; } // resample to common audio format, so any player can play the file diff --git a/src/media/media_recorder.h b/src/media/media_recorder.h index 0bcb632f0ab1ea7918f570761ec62a785f12de80..b169a8279344b6090d9dff23d2b492d7946014e5 100644 --- a/src/media/media_recorder.h +++ b/src/media/media_recorder.h @@ -27,9 +27,6 @@ #include "media_stream.h" #include "noncopyable.h" #include "observer.h" -#ifdef RING_VIDEO -#include "video/video_base.h" -#endif #include <map> #include <memory> @@ -41,8 +38,7 @@ namespace ring { -class MediaRecorder : public Observer<std::shared_ptr<MediaFrame>> - , public std::enable_shared_from_this<MediaRecorder> +class MediaRecorder : public std::enable_shared_from_this<MediaRecorder> { public: MediaRecorder(); @@ -84,8 +80,14 @@ public: void setMetadata(const std::string& title, const std::string& desc); /** + * Adds a stream to the recorder. Caller must then attach this to the media source. */ - int addStream(const MediaStream& ms); + Observer<std::shared_ptr<MediaFrame>>* addStream(const MediaStream& ms); + + /** + * Gets the stream observer so the caller can detach it from the media source. + */ + Observer<std::shared_ptr<MediaFrame>>* getStream(const std::string& name) const; /** * Starts the record. Streams must have been added using Observable::attach and @@ -98,14 +100,29 @@ public: */ void stopRecording(); - /** - * Updates the recorder with an audio or video frame. - */ - void update(Observable<std::shared_ptr<MediaFrame>>* ob, const std::shared_ptr<MediaFrame>& a) override; - private: NON_COPYABLE(MediaRecorder); + struct StreamObserver : public Observer<std::shared_ptr<MediaFrame>> { + const MediaStream info; + + StreamObserver(const MediaStream& ms, std::function<void(const std::shared_ptr<MediaFrame>&)> func) + : info(ms), cb_(func) + {}; + + ~StreamObserver() {}; + + void update(Observable<std::shared_ptr<MediaFrame>>* /*ob*/, const std::shared_ptr<MediaFrame>& m) override + { + cb_(m); + } + + private: + std::function<void(const std::shared_ptr<MediaFrame>&)> cb_; + }; + + void onFrame(const std::string& name, const std::shared_ptr<MediaFrame>& frame); + void flush(); void reset(); @@ -117,7 +134,7 @@ private: std::mutex mutex_; // protect against concurrent file writes - std::map<std::string, const MediaStream> streams_; + std::map<std::string, std::unique_ptr<StreamObserver>> streams_; std::string path_; std::tm startTime_; diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index 9cf78560558a2f8e85c74edbd2a3f9723c81f863..332ceb71fbbfaf7123bfcb7fc22492fd3a86206d 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -568,22 +568,30 @@ void VideoRtpSession::initRecorder(std::shared_ptr<MediaRecorder>& rec) { if (receiveThread_) { - receiveThread_->attach(rec.get()); - rec->addStream(receiveThread_->getInfo()); + if (auto ob = rec->addStream(receiveThread_->getInfo())) { + receiveThread_->attach(ob); + } } - if (auto vidInput = std::static_pointer_cast<VideoInput>(videoLocal_)) { - vidInput->attach(rec.get()); - rec->addStream(vidInput->getInfo()); + if (auto input = std::static_pointer_cast<VideoInput>(videoLocal_)) { + if (auto ob = rec->addStream(input->getInfo())) { + input->attach(ob); + } } } void VideoRtpSession::deinitRecorder(std::shared_ptr<MediaRecorder>& rec) { - if (receiveThread_) - receiveThread_->detach(rec.get()); - if (auto vidInput = std::static_pointer_cast<VideoInput>(videoLocal_)) - vidInput->detach(rec.get()); + if (receiveThread_) { + if (auto ob = rec->getStream(receiveThread_->getInfo().name)) { + receiveThread_->detach(ob); + } + } + if (auto input = std::static_pointer_cast<VideoInput>(videoLocal_)) { + if (auto ob = rec->getStream(input->getInfo().name)) { + input->detach(ob); + } + } } }} // namespace ring::video