diff --git a/src/media/audio/audio_input.cpp b/src/media/audio/audio_input.cpp index 83fa190878e4b01901dc85a703736e9de65ae355..592268544390e17edc16277f3eaa6fa2730bbaec 100644 --- a/src/media/audio/audio_input.cpp +++ b/src/media/audio/audio_input.cpp @@ -47,8 +47,6 @@ AudioInput::AudioInput(const std::string& id) : AudioInput::~AudioInput() { - if (auto rec = recorder_.lock()) - rec->stopRecording(); loop_.join(); } @@ -71,13 +69,6 @@ AudioInput::process() frame->pointer()->pts = sent_samples; sent_samples += frame->pointer()->nb_samples; - { - auto rec = recorder_.lock(); - if (rec && rec->isRecording()) { - rec->recordData(frame->pointer(), ms); - } - } - notify(frame); } @@ -197,11 +188,4 @@ AudioInput::setMuted(bool isMuted) muteState_ = isMuted; } -void -AudioInput::initRecorder(const std::shared_ptr<MediaRecorder>& rec) -{ - rec->incrementExpectedStreams(1); - recorder_ = rec; -} - } // namespace ring diff --git a/src/media/audio/audio_input.h b/src/media/audio/audio_input.h index 43fffa1bbcac45b6b3902e5fe87cab87ee36f9da..efdd1274598392e729793d27354638e402079986 100644 --- a/src/media/audio/audio_input.h +++ b/src/media/audio/audio_input.h @@ -33,7 +33,6 @@ namespace ring { -class MediaRecorder; class Resampler; class AudioInput : public Observable<std::shared_ptr<AudioFrame>> @@ -47,7 +46,6 @@ public: bool isCapturing() const { return loop_.isRunning(); } void setFormat(const AudioFormat& fmt); void setMuted(bool isMuted); - void initRecorder(const std::shared_ptr<MediaRecorder>& rec); private: bool nextFromDevice(AudioFrame& frame); @@ -61,7 +59,6 @@ private: AudioFormat format_; std::unique_ptr<Resampler> resampler_; - std::weak_ptr<MediaRecorder> recorder_; std::string currentResource_; std::atomic_bool switchPending_ {false}; diff --git a/src/media/audio/audio_receive_thread.cpp b/src/media/audio/audio_receive_thread.cpp index f320a8b7c9ba1440287a1a050be05b63e9fc110a..b01d9f73d4f9daa869ab5919aa0220aae7c0b7ff 100644 --- a/src/media/audio/audio_receive_thread.cpp +++ b/src/media/audio/audio_receive_thread.cpp @@ -49,8 +49,6 @@ AudioReceiveThread::AudioReceiveThread(const std::string& id, AudioReceiveThread::~AudioReceiveThread() { - if (auto rec = recorder_.lock()) - rec->stopRecording(); loop_.join(); } @@ -96,11 +94,6 @@ AudioReceiveThread::process() auto decodedFrame = std::make_shared<AudioFrame>(); switch (audioDecoder_->decode(*decodedFrame)) { case MediaDecoder::Status::FrameFinished: - { - auto rec = recorder_.lock(); - if (rec && rec->isRecording()) - rec->recordData(decodedFrame->pointer(), audioDecoder_->getStream("a:remote")); - } audioDecoder_->writeToRingBuffer(*decodedFrame, *ringbuffer_, mainBuffFormat); notify(decodedFrame); @@ -163,11 +156,4 @@ AudioReceiveThread::startLoop() loop_.start(); } -void -AudioReceiveThread::initRecorder(std::shared_ptr<MediaRecorder>& rec) -{ - recorder_ = rec; - rec->incrementExpectedStreams(1); -} - }; // namespace ring diff --git a/src/media/audio/audio_receive_thread.h b/src/media/audio/audio_receive_thread.h index 38a75a6a1d696141b181441dc29390c82fb30089..8ee6b6a93b5bb0b0749dad1a655c4d8b960fd702 100644 --- a/src/media/audio/audio_receive_thread.h +++ b/src/media/audio/audio_receive_thread.h @@ -33,10 +33,9 @@ namespace ring { class MediaDecoder; class MediaIOHandle; -class MediaRecorder; class RingBuffer; -class AudioReceiveThread : Observable<std::shared_ptr<AudioFrame>> +class AudioReceiveThread : public Observable<std::shared_ptr<AudioFrame>> { public: AudioReceiveThread(const std::string &id, @@ -47,8 +46,6 @@ public: void addIOContext(SocketPair &socketPair); void startLoop(); - void initRecorder(std::shared_ptr<MediaRecorder>& rec); - private: NON_COPYABLE(AudioReceiveThread); @@ -60,8 +57,6 @@ private: void openDecoder(); bool decodeFrame(); - std::weak_ptr<MediaRecorder> recorder_; - /*-----------------------------------------------------------------*/ /* These variables should be used in thread (i.e. process()) only! */ /*-----------------------------------------------------------------*/ diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp index 7e326d371a88c564ad6c552e33cd27fd0c08260c..93613fb1a9402f38f87e1337725b36644ec31b7b 100644 --- a/src/media/audio/audio_rtp_session.cpp +++ b/src/media/audio/audio_rtp_session.cpp @@ -197,9 +197,18 @@ void AudioRtpSession::initRecorder(std::shared_ptr<MediaRecorder>& rec) { if (receiveThread_) - receiveThread_->initRecorder(rec); - if (sender_) - sender_->initRecorder(rec); + receiveThread_->attach(rec.get()); + if (auto input = ring::getAudioInput(callID_)) + input->attach(rec.get()); +} + +void +AudioRtpSession::deinitRecorder(std::shared_ptr<MediaRecorder>& rec) +{ + if (receiveThread_) + receiveThread_->detach(rec.get()); + if (auto input = ring::getAudioInput(callID_)) + input->detach(rec.get()); } } // namespace ring diff --git a/src/media/audio/audio_rtp_session.h b/src/media/audio/audio_rtp_session.h index 03f1e6be067cdee4a82b4593642c1cb24ee0e438..eea60715645c256b87d2600d787c71d827a1fb66 100644 --- a/src/media/audio/audio_rtp_session.h +++ b/src/media/audio/audio_rtp_session.h @@ -53,6 +53,7 @@ class AudioRtpSession : public RtpSession { void switchInput(const std::string& resource) { input_ = resource; } void initRecorder(std::shared_ptr<MediaRecorder>& rec) override; + void deinitRecorder(std::shared_ptr<MediaRecorder>& rec) override; private: void startSender(); diff --git a/src/media/audio/audio_sender.cpp b/src/media/audio/audio_sender.cpp index 3d4167ad6493114d2f799e0644bfa9835ddca1a9..04c62f8dba4c8aa757126238db766282f3ca30f7 100644 --- a/src/media/audio/audio_sender.cpp +++ b/src/media/audio/audio_sender.cpp @@ -26,7 +26,6 @@ #include "logger.h" #include "media_encoder.h" #include "media_io_handle.h" -#include "media_recorder.h" #include "media_stream.h" #include "resampler.h" #include "smartools.h" @@ -52,8 +51,6 @@ AudioSender::AudioSender(const std::string& id, AudioSender::~AudioSender() { - if (auto rec = recorder_.lock()) - rec->stopRecording(); audioInput_->detach(this); audioInput_.reset(); audioEncoder_.reset(); @@ -106,12 +103,6 @@ AudioSender::update(Observable<std::shared_ptr<ring::AudioFrame>>* /*obs*/, cons ms.firstTimestamp = frame->pts; sent_samples += frame->nb_samples; - { - auto rec = recorder_.lock(); - if (rec && rec->isRecording()) - rec->recordData(frame, ms); - } - if (audioEncoder_->encodeAudio(*framePtr) < 0) RING_ERR("encoding failed"); } @@ -129,11 +120,4 @@ AudioSender::getLastSeqValue() return audioEncoder_->getLastSeqValue(); } -void -AudioSender::initRecorder(std::shared_ptr<MediaRecorder>& rec) -{ - recorder_ = rec; - rec->incrementExpectedStreams(1); -} - } // namespace ring diff --git a/src/media/audio/audio_sender.h b/src/media/audio/audio_sender.h index b51d9db7f54bae6227049009d15c228fd30974c4..3b2e9736af3bf5dfa0d1884451434e81d6251dcd 100644 --- a/src/media/audio/audio_sender.h +++ b/src/media/audio/audio_sender.h @@ -31,7 +31,6 @@ namespace ring { class AudioInput; class MediaEncoder; class MediaIOHandle; -class MediaRecorder; class Resampler; class AudioSender : public Observer<std::shared_ptr<AudioFrame>> { @@ -51,8 +50,6 @@ public: void update(Observable<std::shared_ptr<ring::AudioFrame>>*, const std::shared_ptr<ring::AudioFrame>&) override; - void initRecorder(std::shared_ptr<MediaRecorder>& rec); - private: NON_COPYABLE(AudioSender); @@ -65,7 +62,6 @@ private: std::unique_ptr<MediaIOHandle> muxContext_; std::unique_ptr<Resampler> resampler_; std::shared_ptr<AudioInput> audioInput_; - std::weak_ptr<MediaRecorder> recorder_; uint64_t sent_samples = 0; diff --git a/src/media/localrecorder.cpp b/src/media/localrecorder.cpp index 1283e1f6a7ccec167ad615e3717a45d7843498c3..2aaebc94f27a3fb1d4d73ce6cd182c4029fbed3e 100644 --- a/src/media/localrecorder.cpp +++ b/src/media/localrecorder.cpp @@ -73,14 +73,14 @@ LocalRecorder::startRecording() audioInput_ = ring::getAudioInput(path_); audioInput_->setFormat(AudioFormat::STEREO()); - audioInput_->initRecorder(recorder_); + audioInput_->attach(recorder_.get()); #ifdef RING_VIDEO // video recording if (!isAudioOnly_) { videoInput_ = std::static_pointer_cast<video::VideoInput>(ring::getVideoCamera()); if (videoInput_) { - videoInput_->initRecorder(recorder_); + videoInput_->attach(recorder_.get()); } else { RING_ERR() << "Unable to record video (no video input)"; return false; @@ -97,8 +97,6 @@ LocalRecorder::stopRecording() Recordable::stopRecording(); Manager::instance().getRingBufferPool().unBindHalfDuplexOut(path_, RingBufferPool::DEFAULT_ID); audioInput_.reset(); - if (videoInput_) - videoInput_->initRecorder(nullptr); // workaround for deiniting recorder videoInput_.reset(); } diff --git a/src/media/media_recorder.cpp b/src/media/media_recorder.cpp index fa6f436cb49cb1809e5e13136ab8fbf68b94b354..589fc74a2ed3c67118c71ab8456c007de0dc095b 100644 --- a/src/media/media_recorder.cpp +++ b/src/media/media_recorder.cpp @@ -19,16 +19,17 @@ */ #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" #include "media_io_handle.h" #include "media_recorder.h" #include "system_codec_container.h" - -extern "C" { -#include <libavutil/frame.h> -} +#include "video/video_input.h" +#include "video/video_receive_thread.h" #include <algorithm> #include <iomanip> @@ -38,8 +39,6 @@ extern "C" { namespace ring { -static constexpr auto FRAME_DEQUEUE_INTERVAL = std::chrono::milliseconds(200); - static std::string replaceAll(const std::string& str, const std::string& from, const std::string& to) { @@ -119,12 +118,6 @@ MediaRecorder::setPath(const std::string& path) RING_DBG() << "Recording will be saved as '" << getPath() << "'"; } -void -MediaRecorder::incrementExpectedStreams(int n) -{ - nbExpectedStreams_ += n; -} - bool MediaRecorder::isRecording() const { @@ -185,6 +178,42 @@ MediaRecorder::stopRecording() resetToDefaults(); } +void +MediaRecorder::update(Observable<std::shared_ptr<AudioFrame>>* ob, const std::shared_ptr<AudioFrame>& a) +{ + MediaStream ms; + if (dynamic_cast<AudioReceiveThread*>(ob)) + ms.name = "a:remote"; + else // if (dynamic_cast<AudioSender*>(ob) || dynamic_cast<AudioInput*>(ob)) + ms.name = "a:local"; + ms.isVideo = false; + ms.update(a->pointer()); + ms.firstTimestamp = a->pointer()->pts; + recordData(a->pointer(), ms); +} + +void MediaRecorder::attached(Observable<std::shared_ptr<AudioFrame>>* /*ob*/) +{ + ++nbExpectedStreams_; +} + +void MediaRecorder::update(Observable<std::shared_ptr<VideoFrame>>* ob, const std::shared_ptr<VideoFrame>& v) +{ + MediaStream ms; + if (auto receiver = dynamic_cast<video::VideoReceiveThread*>(ob)) { + ms = receiver->getStream(); + } else if (auto input = dynamic_cast<video::VideoInput*>(ob)) { + ms = input->getStream(); + } + ms.firstTimestamp = v->pointer()->pts; + recordData(v->pointer(), ms); +} + +void MediaRecorder::attached(Observable<std::shared_ptr<VideoFrame>>* /*ob*/) +{ + ++nbExpectedStreams_; +} + int MediaRecorder::addStream(const MediaStream& ms) { @@ -402,7 +431,7 @@ MediaRecorder::buildVideoFilter(const std::vector<MediaStream>& peers, const Med switch (peers.size()) { case 0: - v << "[" << local.name << "] format=pix_fmts=yuv420p"; + v << "[" << local.name << "] fps=30, format=pix_fmts=yuv420p"; break; case 1: { diff --git a/src/media/media_recorder.h b/src/media/media_recorder.h index e8b3bae30293946b3067cf541257caa2b6713242..829b052450d787e82596fce78524f910879337ea 100644 --- a/src/media/media_recorder.h +++ b/src/media/media_recorder.h @@ -21,11 +21,16 @@ #pragma once #include "config.h" +#include "media_buffer.h" #include "media_encoder.h" #include "media_filter.h" #include "media_stream.h" #include "noncopyable.h" +#include "observer.h" #include "threadloop.h" +#ifdef RING_VIDEO +#include "video/video_base.h" +#endif #include <map> #include <memory> @@ -39,95 +44,102 @@ struct AVFrame; namespace ring { -class MediaRecorder { - public: - MediaRecorder(); - ~MediaRecorder(); - - std::string getPath() const; - - void setPath(const std::string& path); - - void audioOnly(bool audioOnly); - - // replaces %TIMESTAMP with time at start of recording - // default title: "Conversation at %Y-%m-%d %H:%M:%S" - // default description: "Recorded with Jami https://jami.net" - void setMetadata(const std::string& title, const std::string& desc); - - [[deprecated("use setPath to set full recording path")]] - void setRecordingPath(const std::string& dir); - - // adjust nb of streams before recording - // used to know when all streams are set up - void incrementExpectedStreams(int n); - - bool isRecording() const; - - bool toggleRecording(); - - int startRecording(); - - void stopRecording(); - - int recordData(AVFrame* frame, const MediaStream& ms); - - private: - NON_COPYABLE(MediaRecorder); - - int addStream(const MediaStream& ms); - int initRecord(); - MediaStream setupVideoOutput(); - std::string buildVideoFilter(const std::vector<MediaStream>& peers, const MediaStream& local) const; - MediaStream setupAudioOutput(); - std::string buildAudioFilter(const std::vector<MediaStream>& peers, const MediaStream& local) const; - void emptyFilterGraph(); - int sendToEncoder(AVFrame* frame, int streamIdx); - int flush(); - void resetToDefaults(); // clear saved data for next recording - - std::unique_ptr<MediaEncoder> encoder_; - std::unique_ptr<MediaFilter> videoFilter_; - std::unique_ptr<MediaFilter> audioFilter_; - - std::mutex mutex_; // protect against concurrent file writes - - std::map<std::string, const MediaStream> streams_; - - std::tm startTime_; - std::string title_; - std::string description_; - - std::string path_; - - // NOTE do not use dir_ or filename_, use path_ instead - std::string dir_; - std::string filename_; - - unsigned nbExpectedStreams_ = 0; - unsigned nbReceivedVideoStreams_ = 0; - unsigned nbReceivedAudioStreams_ = 0; - int videoIdx_ = -1; - int audioIdx_ = -1; - bool isRecording_ = false; - bool isReady_ = false; - bool audioOnly_ = false; - - struct RecordFrame { - AVFrame* frame; - bool isVideo; - bool fromPeer; - RecordFrame() {} - RecordFrame(AVFrame* f, bool v, bool p) - : frame(f) - , isVideo(v) - , fromPeer(p) - {} - }; - InterruptedThreadLoop loop_; - void process(); - std::mutex qLock_; - std::deque<RecordFrame> frames_; +class MediaRecorder : public Observer<std::shared_ptr<AudioFrame>> +#ifdef RING_VIDEO + , public video::VideoFramePassiveReader +#endif +{ +public: + MediaRecorder(); + ~MediaRecorder(); + + std::string getPath() const; + + void setPath(const std::string& path); + + void audioOnly(bool audioOnly); + + // replaces %TIMESTAMP with time at start of recording + // default title: "Conversation at %Y-%m-%d %H:%M:%S" + // default description: "Recorded with Jami https://jami.net" + void setMetadata(const std::string& title, const std::string& desc); + + [[deprecated("use setPath to set full recording path")]] + void setRecordingPath(const std::string& dir); + + bool isRecording() const; + + bool toggleRecording(); + + int startRecording(); + + void stopRecording(); + + /* Observer methods*/ + void update(Observable<std::shared_ptr<AudioFrame>>* ob, const std::shared_ptr<AudioFrame>& a) override; + void attached(Observable<std::shared_ptr<AudioFrame>>* ob) override; + + void update(Observable<std::shared_ptr<VideoFrame>>* ob, const std::shared_ptr<VideoFrame>& v) override; + void attached(Observable<std::shared_ptr<VideoFrame>>* ob) override; + +private: + NON_COPYABLE(MediaRecorder); + + int recordData(AVFrame* frame, const MediaStream& ms); + + int addStream(const MediaStream& ms); + int initRecord(); + MediaStream setupVideoOutput(); + std::string buildVideoFilter(const std::vector<MediaStream>& peers, const MediaStream& local) const; + MediaStream setupAudioOutput(); + std::string buildAudioFilter(const std::vector<MediaStream>& peers, const MediaStream& local) const; + void emptyFilterGraph(); + int sendToEncoder(AVFrame* frame, int streamIdx); + int flush(); + void resetToDefaults(); // clear saved data for next recording + + std::unique_ptr<MediaEncoder> encoder_; + std::unique_ptr<MediaFilter> videoFilter_; + std::unique_ptr<MediaFilter> audioFilter_; + + std::mutex mutex_; // protect against concurrent file writes + + std::map<std::string, const MediaStream> streams_; + + std::tm startTime_; + std::string title_; + std::string description_; + + std::string path_; + + // NOTE do not use dir_ or filename_, use path_ instead + std::string dir_; + std::string filename_; + + unsigned nbExpectedStreams_ = 0; + unsigned nbReceivedVideoStreams_ = 0; + unsigned nbReceivedAudioStreams_ = 0; + int videoIdx_ = -1; + int audioIdx_ = -1; + bool isRecording_ = false; + bool isReady_ = false; + bool audioOnly_ = false; + + struct RecordFrame { + AVFrame* frame; + bool isVideo; + bool fromPeer; + RecordFrame() {} + RecordFrame(AVFrame* f, bool v, bool p) + : frame(f) + , isVideo(v) + , fromPeer(p) + {} + }; + InterruptedThreadLoop loop_; + void process(); + std::mutex qLock_; + std::deque<RecordFrame> frames_; }; }; // namespace ring diff --git a/src/media/rtp_session.h b/src/media/rtp_session.h index 6c85997efeb83cda525b08e2e359e00fa8fe3a36..f64ac3768e2e300de6c3786e1761eecca7f32bb5 100644 --- a/src/media/rtp_session.h +++ b/src/media/rtp_session.h @@ -56,6 +56,7 @@ public: void setMtu(uint16_t mtu) { mtu_ = mtu; } virtual void initRecorder(std::shared_ptr<MediaRecorder>& rec) = 0; + virtual void deinitRecorder(std::shared_ptr<MediaRecorder>& rec) = 0; protected: std::recursive_mutex mutex_; diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp index d7a6334ddb672f56b3312e5b42f4e64820af6b32..61ace8c1ff298d1ab51d045d6eb629216cecf20a 100644 --- a/src/media/video/video_input.cpp +++ b/src/media/video/video_input.cpp @@ -63,8 +63,6 @@ VideoInput::VideoInput() VideoInput::~VideoInput() { - if (auto rec = recorder_.lock()) - rec->stopRecording(); #if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) /* we need to stop the loop and notify the condition variable * to unblock the process loop */ @@ -232,11 +230,6 @@ bool VideoInput::captureFrame() return static_cast<bool>(decoder_); case MediaDecoder::Status::FrameFinished: - { - auto rec = recorder_.lock(); - if (rec && rec->isRecording()) - rec->recordData(frame.pointer(), decoder_->getStream("v:local")); - } publishFrame(); return true; // continue decoding @@ -613,6 +606,12 @@ int VideoInput::getPixelFormat() const DeviceParams VideoInput::getParams() const { return decOpts_; } +MediaStream +VideoInput::getStream() const +{ + return decoder_->getStream("v:local"); +} + void VideoInput::foundDecOpts(const DeviceParams& params) { @@ -622,15 +621,4 @@ VideoInput::foundDecOpts(const DeviceParams& params) } } -void -VideoInput::initRecorder(const std::shared_ptr<MediaRecorder>& rec) -{ - if (rec) { - recorder_ = rec; - rec->incrementExpectedStreams(1); - } else { - recorder_.reset(); - } -} - }} // namespace ring::video diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h index 0e8d16ef2919511158d439e75dba28510dc9a5e4..1ccfc544f81c28f11c1996ad8790fcc2ce71d3d9 100644 --- a/src/media/video/video_input.h +++ b/src/media/video/video_input.h @@ -25,6 +25,7 @@ #include "noncopyable.h" #include "threadloop.h" +#include "media_stream.h" #include "media/media_device.h" // DeviceParams #include "media/video/video_base.h" @@ -42,7 +43,6 @@ namespace ring { class MediaDecoder; -class MediaRecorder; } namespace ring { namespace video { @@ -78,6 +78,7 @@ public: int getHeight() const; int getPixelFormat() const; DeviceParams getParams() const; + MediaStream getStream() const; std::shared_future<DeviceParams> switchInput(const std::string& resource); #if defined(__ANDROID__) || defined(RING_UWP) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) @@ -89,8 +90,6 @@ public: void releaseFrame(void *frame); #endif - void initRecorder(const std::shared_ptr<MediaRecorder>& rec); - private: NON_COPYABLE(VideoInput); @@ -144,8 +143,6 @@ private: void releaseBufferCb(uint8_t* ptr); std::array<struct VideoFrameBuffer, 8> buffers_; #endif - - std::weak_ptr<MediaRecorder> recorder_; }; }} // namespace ring::video diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp index a82a66a264ec9f9d1f61c64991ba73eadd6b2436..174fc09274a5b7cac15afbf76047dc7c02a788b7 100644 --- a/src/media/video/video_receive_thread.cpp +++ b/src/media/video/video_receive_thread.cpp @@ -56,8 +56,6 @@ VideoReceiveThread::VideoReceiveThread(const std::string& id, VideoReceiveThread::~VideoReceiveThread() { - if (auto rec = recorder_.lock()) - rec->stopRecording(); loop_.join(); } @@ -187,11 +185,6 @@ bool VideoReceiveThread::decodeFrame() switch (ret) { case MediaDecoder::Status::FrameFinished: - { - auto rec = recorder_.lock(); - if (rec && rec->isRecording()) - rec->recordData(frame.pointer(), videoDecoder_->getStream("v:remote")); - } publishFrame(); return true; @@ -253,18 +246,17 @@ int VideoReceiveThread::getHeight() const int VideoReceiveThread::getPixelFormat() const { return videoDecoder_->getPixelFormat(); } -void -VideoReceiveThread::triggerKeyFrameRequest() +MediaStream +VideoReceiveThread::getStream() const { - if (requestKeyFrameCallback_) - requestKeyFrameCallback_(id_); + return videoDecoder_->getStream("v:remote"); } void -VideoReceiveThread::initRecorder(std::shared_ptr<ring::MediaRecorder>& rec) +VideoReceiveThread::triggerKeyFrameRequest() { - recorder_ = rec; - rec->incrementExpectedStreams(1); + if (requestKeyFrameCallback_) + requestKeyFrameCallback_(id_); } }} // namespace ring::video diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h index 273a51e1f1cb5fe016f9cf769b59621444cb7301..7641f4627cfd6b6c20d0c60f8b30970bb93eb86d 100644 --- a/src/media/video/video_receive_thread.h +++ b/src/media/video/video_receive_thread.h @@ -26,6 +26,7 @@ #include "media_codec.h" #include "media_io_handle.h" #include "media_device.h" +#include "media_stream.h" #include "threadloop.h" #include "noncopyable.h" @@ -38,7 +39,6 @@ namespace ring { class SocketPair; class MediaDecoder; -class MediaRecorder; } // namespace ring namespace ring { namespace video { @@ -60,10 +60,9 @@ public: int getWidth() const; int getHeight() const; int getPixelFormat() const; + MediaStream getStream() const; void triggerKeyFrameRequest(); - void initRecorder(std::shared_ptr<ring::MediaRecorder>& rec); - private: NON_COPYABLE(VideoReceiveThread); @@ -89,8 +88,6 @@ private: static int interruptCb(void *ctx); static int readFunction(void *opaque, uint8_t *buf, int buf_size); - std::weak_ptr<MediaRecorder> recorder_; - ThreadLoop loop_; // used by ThreadLoop diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index 3d6f91e0c3251895eda0978855515df98e67a23c..07818aad6dba160cb396e4ecbb6e35ccfa82934e 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -567,11 +567,19 @@ VideoRtpSession::processPacketLoss() void VideoRtpSession::initRecorder(std::shared_ptr<MediaRecorder>& rec) { - // video recording needs to start with keyframes if (receiveThread_) - receiveThread_->initRecorder(rec); + receiveThread_->attach(rec.get()); if (auto vidInput = std::static_pointer_cast<VideoInput>(videoLocal_)) - vidInput->initRecorder(rec); + vidInput->attach(rec.get()); +} + +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()); } }} // namespace ring::video diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h index bb1bade19f3efd5fc213e470228e2ba0db00be91..b4a468025f126de5cca256b31c8997ae2aa94de1 100644 --- a/src/media/video/video_rtp_session.h +++ b/src/media/video/video_rtp_session.h @@ -81,6 +81,7 @@ public: bool useCodec(const AccountVideoCodecInfo* codec) const; void initRecorder(std::shared_ptr<MediaRecorder>& rec) override; + void deinitRecorder(std::shared_ptr<MediaRecorder>& rec) override; private: void setupConferenceVideoPipeline(Conference& conference); diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index b7e5b7c9174e8f8ab0d3dfc2d5e39f394af7ff70..c08533fe805007d800bc01ddd93c816423f2e7d9 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -1198,6 +1198,13 @@ SIPCall::toggleRecording() #ifdef RING_VIDEO if (!isAudioOnly_ && videortp_) videortp_->initRecorder(recorder_); +#endif + } else { + if (avformatrtp_) + avformatrtp_->deinitRecorder(recorder_); +#ifdef RING_VIDEO + if (!isAudioOnly_ && videortp_) + videortp_->deinitRecorder(recorder_); #endif } return startRecording;