diff --git a/src/media/audio/Makefile.am b/src/media/audio/Makefile.am index 904df3c81dd21b810d216da81cfff1ee435193b9..12bcbf21897559a05627297fe535d6e8d148ab1f 100644 --- a/src/media/audio/Makefile.am +++ b/src/media/audio/Makefile.am @@ -39,6 +39,7 @@ endif libaudio_la_SOURCES = \ audiobuffer.cpp \ + audio_input.cpp \ audioloop.cpp \ ringbuffer.cpp \ ringbufferpool.cpp \ @@ -59,13 +60,14 @@ endif noinst_HEADERS = \ audiobuffer.h \ + audio_input.h \ audioloop.h \ ringbuffer.h \ ringbufferpool.h \ audiolayer.h \ + resampler.h \ $(RING_SPEEXDSP_HEAD) \ dcblocker.h \ - resampler.h \ audio_rtp_session.h \ tonecontrol.h diff --git a/src/media/audio/audio_input.cpp b/src/media/audio/audio_input.cpp new file mode 100644 index 0000000000000000000000000000000000000000..295f1a95025d33e51d2f255d0b5e0bc1f7aab3a4 --- /dev/null +++ b/src/media/audio/audio_input.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Hugo Lefeuvre <hugo.lefeuvre@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 <future> +#include <chrono> +#include "socket_pair.h" +#include "audio/ringbufferpool.h" +#include "manager.h" +#include "smartools.h" + +namespace ring { namespace audio { + +AudioInput::AudioInput(const std::string& id) : + id_(id), + loop_([] {return true;}, // setup() + [this] {return process();}, + [this] {return cleanup();}) +{ + loop_.start(); +} + +AudioInput::~AudioInput() +{ + if (auto rec = recorder_.lock()) + rec->stopRecording(); + loop_.join(); +} + +// seq: frame number for video, sent samples audio +// sampleFreq: fps for video, sample rate for audio +// clock: stream time base (packetization interval times) +// FIXME duplicate code from media encoder +int64_t +getNextTimestamp(int64_t seq, rational<int64_t> sampleFreq, rational<int64_t> clock) +{ + return (seq / (sampleFreq * clock)).real<int64_t>(); +} + +void +AudioInput::process() +{ + auto& mainBuffer = Manager::instance().getRingBufferPool(); + auto ringBuffer = mainBuffer.getRingBuffer(id_); + auto bufferFormat = ringBuffer->getFormat(); + + // compute number of bytes contained in a frame with duration msPerPacket_ + auto bytesPerPacket = msPerPacket_ * bufferFormat.sample_rate; + const std::size_t bytesToGet = std::chrono::duration_cast<std::chrono::seconds>(bytesPerPacket).count(); + + if (ringBuffer->availableForGet(id_) < bytesToGet + && not ringBuffer->waitForDataAvailable(id_, bytesToGet)) { + return; + } + + // get data + micData_.setFormat(bufferFormat); + micData_.resize(bytesToGet); + const auto samples = ringBuffer->get(micData_, id_); + if (samples != bytesToGet) + return; + + if (muteState_) // audio is muted, set samples to 0 + micData_.reset(); + + // record frame + AVFrame* frame = micData_.toAVFrame(); + auto ms = MediaStream("a:local", micData_.getFormat()); + frame->pts = getNextTimestamp(sent_samples, ms.sampleRate, static_cast<rational<int64_t>>(ms.timeBase)); + sent_samples += frame->nb_samples; + + { + auto rec = recorder_.lock(); + if (rec && rec->isRecording()) { + rec->recordData(frame, ms); + } + } +} + +void +AudioInput::cleanup() +{ + micData_.clear(); +} + +void +AudioInput::setMuted(bool isMuted) +{ + muteState_ = isMuted; +} + +std::shared_future<DeviceParams> +AudioInput::switchInput(const std::string& resource) +{ + // TODO not implemented yet + return {}; +} + +void +AudioInput::initRecorder(const std::shared_ptr<MediaRecorder>& rec) +{ + rec->incrementExpectedStreams(1); + recorder_ = rec; +} + +}} // namespace ring::audio diff --git a/src/media/audio/audio_input.h b/src/media/audio/audio_input.h new file mode 100644 index 0000000000000000000000000000000000000000..040d68e9ce5d9a072dfcd22b655fb25e88329bfe --- /dev/null +++ b/src/media/audio/audio_input.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Hugo Lefeuvre <hugo.lefeuvre@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. + */ + +#pragma once + +#include <future> +#include "audio/audiobuffer.h" +#include "media/media_recorder.h" +#include "audio/resampler.h" + +namespace ring { +class MediaRecorder; +} + +namespace ring { namespace audio { + +class AudioInput +{ +public: + AudioInput(const std::string& id); + ~AudioInput(); + + std::shared_future<DeviceParams> switchInput(const std::string& resource); + + void setMuted(bool isMuted); + void initRecorder(const std::shared_ptr<MediaRecorder>& rec); + +private: + std::weak_ptr<MediaRecorder> recorder_; + uint64_t sent_samples = 0; + + std::string id_; + AudioBuffer micData_; + bool muteState_ = false; + + const std::chrono::milliseconds msPerPacket_ {20}; + + ThreadLoop loop_; + void process(); + void cleanup(); +}; + +}} // namespace ring::audio diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp index aa996f880111299205ea25dc02d24a6094a8b985..e354fd06816e07ad8f0b89859fc1a4706ad8d75b 100644 --- a/src/media/audio/audio_rtp_session.cpp +++ b/src/media/audio/audio_rtp_session.cpp @@ -222,6 +222,7 @@ AudioSender::process() if (audioEncoder_->encodeAudio(frame) < 0) RING_ERR("encoding failed"); } + void AudioSender::setMuted(bool isMuted) {