From 3f686f1fcc28171a49a1c0b3bf91fce6dfee76b2 Mon Sep 17 00:00:00 2001 From: philippegorley <philippe.gorley@savoirfairelinux.com> Date: Thu, 29 Nov 2018 11:41:41 -0500 Subject: [PATCH] audio: move sender and receiver to their own files Makes it easier to manage. Other classes can now reference the sender and receiver. Change-Id: I6c12458f7632ff2d37d49640f6276ba2ae1ac5bc --- src/media/audio/Makefile.am | 4 + src/media/audio/audio_receive_thread.cpp | 177 ++++++++++++ src/media/audio/audio_receive_thread.h | 87 ++++++ src/media/audio/audio_rtp_session.cpp | 346 +---------------------- src/media/audio/audio_sender.cpp | 139 +++++++++ src/media/audio/audio_sender.h | 79 ++++++ 6 files changed, 489 insertions(+), 343 deletions(-) create mode 100644 src/media/audio/audio_receive_thread.cpp create mode 100644 src/media/audio/audio_receive_thread.h create mode 100644 src/media/audio/audio_sender.cpp create mode 100644 src/media/audio/audio_sender.h diff --git a/src/media/audio/Makefile.am b/src/media/audio/Makefile.am index fa3adabbbc..34138871c4 100644 --- a/src/media/audio/Makefile.am +++ b/src/media/audio/Makefile.am @@ -48,6 +48,8 @@ libaudio_la_SOURCES = \ resampler.cpp \ $(RING_SPEEXDSP_SRC) \ dcblocker.cpp \ + audio_sender.cpp \ + audio_receive_thread.cpp \ audio_rtp_session.cpp \ tonecontrol.cpp @@ -70,6 +72,8 @@ noinst_HEADERS = \ resampler.h \ $(RING_SPEEXDSP_HEAD) \ dcblocker.h \ + audio_sender.h \ + audio_receive_thread.h \ audio_rtp_session.h \ tonecontrol.h diff --git a/src/media/audio/audio_receive_thread.cpp b/src/media/audio/audio_receive_thread.cpp new file mode 100644 index 0000000000..9e8826c759 --- /dev/null +++ b/src/media/audio/audio_receive_thread.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "audio_receive_thread.h" +#include "libav_deps.h" +#include "logger.h" +#include "manager.h" +#include "media_decoder.h" +#include "media_io_handle.h" +#include "media_recorder.h" +#include "ringbuffer.h" +#include "ringbufferpool.h" +#include "smartools.h" + +namespace ring { + +AudioReceiveThread::AudioReceiveThread(const std::string& id, + const AudioFormat& format, + const std::string& sdp, + const uint16_t mtu) + : id_(id) + , format_(format) + , stream_(sdp) + , sdpContext_(new MediaIOHandle(sdp.size(), false, &readFunction, + 0, 0, this)) + , mtu_(mtu) + , loop_(std::bind(&AudioReceiveThread::setup, this), + std::bind(&AudioReceiveThread::process, this), + std::bind(&AudioReceiveThread::cleanup, this)) +{} + +AudioReceiveThread::~AudioReceiveThread() +{ + if (auto rec = recorder_.lock()) + rec->stopRecording(); + loop_.join(); +} + +bool +AudioReceiveThread::setup() +{ + audioDecoder_.reset(new MediaDecoder()); + audioDecoder_->setInterruptCallback(interruptCb, this); + + // custom_io so the SDP demuxer will not open any UDP connections + args_.input = SDP_FILENAME; + args_.format = "sdp"; + args_.sdp_flags = "custom_io"; + + if (stream_.str().empty()) { + RING_ERR("No SDP loaded"); + return false; + } + + audioDecoder_->setIOContext(sdpContext_.get()); + if (audioDecoder_->openInput(args_)) { + RING_ERR("Could not open input \"%s\"", SDP_FILENAME); + return false; + } + + // Now replace our custom AVIOContext with one that will read packets + audioDecoder_->setIOContext(demuxContext_.get()); + if (audioDecoder_->setupFromAudioData()) { + RING_ERR("decoder IO startup failed"); + return false; + } + + Smartools::getInstance().setRemoteAudioCodec(audioDecoder_->getDecoderName()); + + ringbuffer_ = Manager::instance().getRingBufferPool().getRingBuffer(id_); + return true; +} + +void +AudioReceiveThread::process() +{ + AudioFormat mainBuffFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat(); + AudioFrame decodedFrame; + + 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); + return; + + case MediaDecoder::Status::DecodeError: + RING_WARN("decoding failure, trying to reset decoder..."); + if (not setup()) { + RING_ERR("fatal error, rx thread re-setup failed"); + loop_.stop(); + } else if (not audioDecoder_->setupFromAudioData()) { + RING_ERR("fatal error, a-decoder setup failed"); + loop_.stop(); + } + break; + + case MediaDecoder::Status::ReadError: + RING_ERR("fatal error, read failed"); + loop_.stop(); + break; + + case MediaDecoder::Status::Success: + case MediaDecoder::Status::EOFError: + default: + break; + } +} + +void +AudioReceiveThread::cleanup() +{ + audioDecoder_.reset(); + demuxContext_.reset(); +} + +int +AudioReceiveThread::readFunction(void* opaque, uint8_t* buf, int buf_size) +{ + std::istream& is = static_cast<AudioReceiveThread*>(opaque)->stream_; + is.read(reinterpret_cast<char*>(buf), buf_size); + + auto count = is.gcount(); + return count ? count : AVERROR_EOF; +} + +// This callback is used by libav internally to break out of blocking calls +int +AudioReceiveThread::interruptCb(void* data) +{ + auto context = static_cast<AudioReceiveThread*>(data); + return not context->loop_.isRunning(); +} + +void +AudioReceiveThread::addIOContext(SocketPair& socketPair) +{ + demuxContext_.reset(socketPair.createIOContext(mtu_)); +} + +void +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 new file mode 100644 index 0000000000..4f6572f8a6 --- /dev/null +++ b/src/media/audio/audio_receive_thread.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "audiobuffer.h" +#include "media_buffer.h" +#include "media_device.h" +#include "noncopyable.h" +#include "socket_pair.h" +#include "threadloop.h" + +#include <sstream> + +namespace ring { + +class MediaDecoder; +class MediaIOHandle; +class MediaRecorder; +class RingBuffer; + +class AudioReceiveThread +{ +public: + AudioReceiveThread(const std::string &id, + const AudioFormat& format, + const std::string& sdp, + const uint16_t mtu); + ~AudioReceiveThread(); + void addIOContext(SocketPair &socketPair); + void startLoop(); + + void initRecorder(std::shared_ptr<MediaRecorder>& rec); + +private: + NON_COPYABLE(AudioReceiveThread); + + static constexpr auto SDP_FILENAME = "dummyFilename"; + + static int interruptCb(void *ctx); + static int readFunction(void *opaque, uint8_t *buf, int buf_size); + + void openDecoder(); + bool decodeFrame(); + + std::weak_ptr<MediaRecorder> recorder_; + + /*-----------------------------------------------------------------*/ + /* These variables should be used in thread (i.e. process()) only! */ + /*-----------------------------------------------------------------*/ + const std::string id_; + const AudioFormat& format_; + + DeviceParams args_; + + std::istringstream stream_; + std::unique_ptr<MediaDecoder> audioDecoder_; + std::unique_ptr<MediaIOHandle> sdpContext_; + std::unique_ptr<MediaIOHandle> demuxContext_; + + std::shared_ptr<RingBuffer> ringbuffer_; + + uint16_t mtu_; + + ThreadLoop loop_; + bool setup(); + void process(); + void cleanup(); +}; + +} // namespace ring diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp index 19005578c2..c33fb4cac0 100644 --- a/src/media/audio/audio_rtp_session.cpp +++ b/src/media/audio/audio_rtp_session.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2014-2018 Savoir-faire Linux Inc. * * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,10 +27,8 @@ #include "noncopyable.h" #include "sip/sdp.h" -#ifdef RING_VIDEO -#include "video/video_base.h" -#endif //RING_VIDEO - +#include "audio_receive_thread.h" +#include "audio_sender.h" #include "socket_pair.h" #include "media_recorder.h" #include "media_encoder.h" @@ -49,345 +48,6 @@ namespace ring { -class AudioSender : public Observer<std::shared_ptr<AudioFrame>> { - public: - AudioSender(const std::string& id, - const std::string& dest, - const MediaDescription& args, - SocketPair& socketPair, - const uint16_t seqVal, - bool muteState, - const uint16_t mtu); - ~AudioSender(); - - void setMuted(bool isMuted); - uint16_t getLastSeqValue(); - - 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); - - bool setup(SocketPair& socketPair); - - std::string id_; - std::string dest_; - MediaDescription args_; - std::unique_ptr<MediaEncoder> audioEncoder_; - 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; - - AudioBuffer micData_; - AudioBuffer resampledData_; - const uint16_t seqVal_; - bool muteState_ = false; - uint16_t mtu_; - - const std::chrono::milliseconds msPerPacket_ {20}; -}; - -AudioSender::AudioSender(const std::string& id, - const std::string& dest, - const MediaDescription& args, - SocketPair& socketPair, - const uint16_t seqVal, - bool muteState, - const uint16_t mtu) : - id_(id), - dest_(dest), - args_(args), - seqVal_(seqVal), - muteState_(muteState), - mtu_(mtu) -{ - setup(socketPair); -} - -AudioSender::~AudioSender() -{ - if (auto rec = recorder_.lock()) - rec->stopRecording(); - audioInput_->detach(this); - audioInput_.reset(); - audioEncoder_.reset(); - muxContext_.reset(); - micData_.clear(); - resampledData_.clear(); -} - -bool -AudioSender::setup(SocketPair& socketPair) -{ - audioEncoder_.reset(new MediaEncoder); - muxContext_.reset(socketPair.createIOContext(mtu_)); - - try { - /* Encoder setup */ - RING_DBG("audioEncoder_->openLiveOutput %s", dest_.c_str()); - audioEncoder_->setMuted(muteState_); - audioEncoder_->openLiveOutput(dest_, args_); - audioEncoder_->setInitSeqVal(seqVal_); - audioEncoder_->setIOContext(muxContext_); - audioEncoder_->startIO(); - } catch (const MediaEncoderException &e) { - RING_ERR("%s", e.what()); - return false; - } - - Smartools::getInstance().setLocalAudioCodec(audioEncoder_->getEncoderName()); - -#ifdef DEBUG_SDP - audioEncoder_->print_sdp(); -#endif - - // NOTE do after encoder is ready to encode - auto codec = std::static_pointer_cast<AccountAudioCodecInfo>(args_.codec); - audioInput_ = ring::getAudioInput(id_); - audioInput_->setFormat(codec->audioformat); - audioInput_->attach(this); - - return true; -} - -void -AudioSender::update(Observable<std::shared_ptr<ring::AudioFrame>>* /*obs*/, const std::shared_ptr<ring::AudioFrame>& framePtr) -{ - auto frame = framePtr->pointer(); - auto ms = MediaStream("a:local", frame->format, rational<int>(1, frame->sample_rate), - frame->sample_rate, frame->channels, frame->nb_samples); - frame->pts = sent_samples; - 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"); -} - -void -AudioSender::setMuted(bool isMuted) -{ - muteState_ = isMuted; - audioEncoder_->setMuted(isMuted); -} - -uint16_t -AudioSender::getLastSeqValue() -{ - return audioEncoder_->getLastSeqValue(); -} - -void -AudioSender::initRecorder(std::shared_ptr<MediaRecorder>& rec) -{ - recorder_ = rec; - rec->incrementExpectedStreams(1); -} - -class AudioReceiveThread -{ - public: - AudioReceiveThread(const std::string &id, - const AudioFormat& format, - const std::string& sdp, - const uint16_t mtu); - ~AudioReceiveThread(); - void addIOContext(SocketPair &socketPair); - void startLoop(); - - void initRecorder(std::shared_ptr<MediaRecorder>& rec); - - private: - NON_COPYABLE(AudioReceiveThread); - - static constexpr auto SDP_FILENAME = "dummyFilename"; - - static int interruptCb(void *ctx); - static int readFunction(void *opaque, uint8_t *buf, int buf_size); - - void openDecoder(); - bool decodeFrame(); - - std::weak_ptr<MediaRecorder> recorder_; - - /*-----------------------------------------------------------------*/ - /* These variables should be used in thread (i.e. process()) only! */ - /*-----------------------------------------------------------------*/ - const std::string id_; - const AudioFormat& format_; - - DeviceParams args_; - - std::istringstream stream_; - std::unique_ptr<MediaDecoder> audioDecoder_; - std::unique_ptr<MediaIOHandle> sdpContext_; - std::unique_ptr<MediaIOHandle> demuxContext_; - - std::shared_ptr<RingBuffer> ringbuffer_; - - uint16_t mtu_; - - ThreadLoop loop_; - bool setup(); - void process(); - void cleanup(); -}; - -AudioReceiveThread::AudioReceiveThread(const std::string& id, - const AudioFormat& format, - const std::string& sdp, - const uint16_t mtu) - : id_(id) - , format_(format) - , stream_(sdp) - , sdpContext_(new MediaIOHandle(sdp.size(), false, &readFunction, - 0, 0, this)) - , mtu_(mtu) - , loop_(std::bind(&AudioReceiveThread::setup, this), - std::bind(&AudioReceiveThread::process, this), - std::bind(&AudioReceiveThread::cleanup, this)) -{} - -AudioReceiveThread::~AudioReceiveThread() -{ - if (auto rec = recorder_.lock()) - rec->stopRecording(); - loop_.join(); -} - - -bool -AudioReceiveThread::setup() -{ - audioDecoder_.reset(new MediaDecoder()); - audioDecoder_->setInterruptCallback(interruptCb, this); - - // custom_io so the SDP demuxer will not open any UDP connections - args_.input = SDP_FILENAME; - args_.format = "sdp"; - args_.sdp_flags = "custom_io"; - - if (stream_.str().empty()) { - RING_ERR("No SDP loaded"); - return false; - } - - audioDecoder_->setIOContext(sdpContext_.get()); - if (audioDecoder_->openInput(args_)) { - RING_ERR("Could not open input \"%s\"", SDP_FILENAME); - return false; - } - - // Now replace our custom AVIOContext with one that will read packets - audioDecoder_->setIOContext(demuxContext_.get()); - if (audioDecoder_->setupFromAudioData()) { - RING_ERR("decoder IO startup failed"); - return false; - } - - Smartools::getInstance().setRemoteAudioCodec(audioDecoder_->getDecoderName()); - - ringbuffer_ = Manager::instance().getRingBufferPool().getRingBuffer(id_); - return true; -} - -void -AudioReceiveThread::process() -{ - AudioFormat mainBuffFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat(); - AudioFrame decodedFrame; - - 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); - return; - - case MediaDecoder::Status::DecodeError: - RING_WARN("decoding failure, trying to reset decoder..."); - if (not setup()) { - RING_ERR("fatal error, rx thread re-setup failed"); - loop_.stop(); - } else if (not audioDecoder_->setupFromAudioData()) { - RING_ERR("fatal error, a-decoder setup failed"); - loop_.stop(); - } - break; - - case MediaDecoder::Status::ReadError: - RING_ERR("fatal error, read failed"); - loop_.stop(); - break; - - case MediaDecoder::Status::Success: - case MediaDecoder::Status::EOFError: - default: - break; - } -} - -void -AudioReceiveThread::cleanup() -{ - audioDecoder_.reset(); - demuxContext_.reset(); -} - -int -AudioReceiveThread::readFunction(void* opaque, uint8_t* buf, int buf_size) -{ - std::istream& is = static_cast<AudioReceiveThread*>(opaque)->stream_; - is.read(reinterpret_cast<char*>(buf), buf_size); - - auto count = is.gcount(); - return count ? count : AVERROR_EOF; -} - -// This callback is used by libav internally to break out of blocking calls -int -AudioReceiveThread::interruptCb(void* data) -{ - auto context = static_cast<AudioReceiveThread*>(data); - return not context->loop_.isRunning(); -} - -void -AudioReceiveThread::addIOContext(SocketPair& socketPair) -{ - demuxContext_.reset(socketPair.createIOContext(mtu_)); -} - -void -AudioReceiveThread::startLoop() -{ - loop_.start(); -} - -void -AudioReceiveThread::initRecorder(std::shared_ptr<MediaRecorder>& rec) -{ - recorder_ = rec; - rec->incrementExpectedStreams(1); -} - AudioRtpSession::AudioRtpSession(const std::string& id) : RtpSession(id) { diff --git a/src/media/audio/audio_sender.cpp b/src/media/audio/audio_sender.cpp new file mode 100644 index 0000000000..3d4167ad64 --- /dev/null +++ b/src/media/audio/audio_sender.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "audio_input.h" +#include "audio_sender.h" +#include "client/videomanager.h" +#include "libav_deps.h" +#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" + +namespace ring { + +AudioSender::AudioSender(const std::string& id, + const std::string& dest, + const MediaDescription& args, + SocketPair& socketPair, + const uint16_t seqVal, + bool muteState, + const uint16_t mtu) : + id_(id), + dest_(dest), + args_(args), + seqVal_(seqVal), + muteState_(muteState), + mtu_(mtu) +{ + setup(socketPair); +} + +AudioSender::~AudioSender() +{ + if (auto rec = recorder_.lock()) + rec->stopRecording(); + audioInput_->detach(this); + audioInput_.reset(); + audioEncoder_.reset(); + muxContext_.reset(); + micData_.clear(); + resampledData_.clear(); +} + +bool +AudioSender::setup(SocketPair& socketPair) +{ + audioEncoder_.reset(new MediaEncoder); + muxContext_.reset(socketPair.createIOContext(mtu_)); + + try { + /* Encoder setup */ + RING_DBG("audioEncoder_->openLiveOutput %s", dest_.c_str()); + audioEncoder_->setMuted(muteState_); + audioEncoder_->openLiveOutput(dest_, args_); + audioEncoder_->setInitSeqVal(seqVal_); + audioEncoder_->setIOContext(muxContext_); + audioEncoder_->startIO(); + } catch (const MediaEncoderException &e) { + RING_ERR("%s", e.what()); + return false; + } + + Smartools::getInstance().setLocalAudioCodec(audioEncoder_->getEncoderName()); + +#ifdef DEBUG_SDP + audioEncoder_->print_sdp(); +#endif + + // NOTE do after encoder is ready to encode + auto codec = std::static_pointer_cast<AccountAudioCodecInfo>(args_.codec); + audioInput_ = ring::getAudioInput(id_); + audioInput_->setFormat(codec->audioformat); + audioInput_->attach(this); + + return true; +} + +void +AudioSender::update(Observable<std::shared_ptr<ring::AudioFrame>>* /*obs*/, const std::shared_ptr<ring::AudioFrame>& framePtr) +{ + auto frame = framePtr->pointer(); + auto ms = MediaStream("a:local", frame->format, rational<int>(1, frame->sample_rate), + frame->sample_rate, frame->channels, frame->nb_samples); + frame->pts = sent_samples; + 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"); +} + +void +AudioSender::setMuted(bool isMuted) +{ + muteState_ = isMuted; + audioEncoder_->setMuted(isMuted); +} + +uint16_t +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 new file mode 100644 index 0000000000..b51d9db7f5 --- /dev/null +++ b/src/media/audio/audio_sender.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com> + * Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "audiobuffer.h" +#include "media_buffer.h" +#include "media_codec.h" +#include "noncopyable.h" +#include "observer.h" +#include "socket_pair.h" + +namespace ring { + +class AudioInput; +class MediaEncoder; +class MediaIOHandle; +class MediaRecorder; +class Resampler; + +class AudioSender : public Observer<std::shared_ptr<AudioFrame>> { +public: + AudioSender(const std::string& id, + const std::string& dest, + const MediaDescription& args, + SocketPair& socketPair, + const uint16_t seqVal, + bool muteState, + const uint16_t mtu); + ~AudioSender(); + + void setMuted(bool isMuted); + uint16_t getLastSeqValue(); + + 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); + + bool setup(SocketPair& socketPair); + + std::string id_; + std::string dest_; + MediaDescription args_; + std::unique_ptr<MediaEncoder> audioEncoder_; + 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; + + AudioBuffer micData_; + AudioBuffer resampledData_; + const uint16_t seqVal_; + bool muteState_ = false; + uint16_t mtu_; +}; + +} // namespace ring -- GitLab