diff --git a/src/media/audio/audio_input.cpp b/src/media/audio/audio_input.cpp index a3f397fec79af7b54848a8c61f18c7d35443a187..582e73cce474eb92f428944cf1ed27cfed087d87 100644 --- a/src/media/audio/audio_input.cpp +++ b/src/media/audio/audio_input.cpp @@ -26,6 +26,7 @@ #include "manager.h" #include "media_decoder.h" #include "resampler.h" +#include "ringbuffer.h" #include "ringbufferpool.h" #include "smartools.h" @@ -44,6 +45,7 @@ AudioInput::AudioInput(const std::string& id) : resampler_(new Resampler), resizer_(new AudioFrameResizer(format_, frameSize_, [this](std::shared_ptr<AudioFrame>&& f){ frameResized(std::move(f)); })), + fileId_(id + "_file"), loop_([] { return true; }, [this] { process(); }, [] {}) @@ -60,7 +62,9 @@ AudioInput::~AudioInput() void AudioInput::process() { - foundDevOpts(devOpts_); + // NOTE This is only useful if the device params weren't yet found in switchInput + // For both files and audio devices, this is already done + //foundDevOpts(devOpts_); if (switchPending_.exchange(false)) { if (devOpts_.input.empty()) RING_DBG() << "Switching to default audio input"; @@ -68,18 +72,13 @@ AudioInput::process() RING_DBG() << "Switching audio input to '" << devOpts_.input << "'"; } - // send frame to resizer, frameResized will be called when it can be output - if (decodingFile_) - nextFromFile(); - else - nextFromDevice(); + readFromDevice(); } void AudioInput::frameResized(std::shared_ptr<AudioFrame>&& ptr) { std::shared_ptr<AudioFrame> frame = std::move(ptr); - auto ms = MediaStream("a:local", format_, sent_samples); frame->pointer()->pts = sent_samples; sent_samples += frame->pointer()->nb_samples; @@ -87,14 +86,17 @@ AudioInput::frameResized(std::shared_ptr<AudioFrame>&& ptr) } void -AudioInput::nextFromDevice() +AudioInput::readFromDevice() { auto& mainBuffer = Manager::instance().getRingBufferPool(); auto bufferFormat = mainBuffer.getInternalAudioFormat(); - if (not mainBuffer.waitForDataAvailable(id_, MS_PER_PACKET)) { + if (decodingFile_ ) + while (fileBuf_->isEmpty()) + readFromFile(); + + if (not mainBuffer.waitForDataAvailable(id_, MS_PER_PACKET)) return; - } auto samples = mainBuffer.getData(id_); if (not samples) @@ -104,14 +106,14 @@ AudioInput::nextFromDevice() // micData_.reset(); // TODO handle mute - { - std::lock_guard<std::mutex> lk(fmtMutex_); - resizer_->enqueue(resampler_->resample(std::move(samples), format_)); - } + std::lock_guard<std::mutex> lk(fmtMutex_); + if (bufferFormat != format_) + samples = resampler_->resample(std::move(samples), format_); + resizer_->enqueue(std::move(samples)); } void -AudioInput::nextFromFile() +AudioInput::readFromFile() { if (!decoder_) return; @@ -120,7 +122,6 @@ AudioInput::nextFromFile() const auto ret = decoder_->decode(*frame); const auto inFmt = AudioFormat((unsigned)frame->pointer()->sample_rate, (unsigned)frame->pointer()->channels, (AVSampleFormat)frame->pointer()->format); - std::lock_guard<std::mutex> lk(fmtMutex_); switch(ret) { case MediaDecoder::Status::ReadError: case MediaDecoder::Status::DecodeError: @@ -131,7 +132,7 @@ AudioInput::nextFromFile() createDecoder(); break; case MediaDecoder::Status::FrameFinished: - resizer_->enqueue(resampler_->resample(std::move(frame), format_)); + fileBuf_->put(std::move(frame)); break; case MediaDecoder::Status::Success: default: @@ -165,6 +166,8 @@ AudioInput::initFile(const std::string& path) RING_WARN() << "Cannot decode audio from file, switching back to default device"; return initDevice(""); } + fileBuf_ = Manager::instance().getRingBufferPool().createRingBuffer(fileId_); + Manager::instance().getRingBufferPool().bindHalfDuplexOut(id_, fileId_); decodingFile_ = true; return true; } @@ -172,8 +175,7 @@ AudioInput::initFile(const std::string& path) std::shared_future<DeviceParams> AudioInput::switchInput(const std::string& resource) { - if (resource == currentResource_) - return futureDevOpts_; + // Always switch inputs, even if it's the same resource, so audio will be in sync with video if (switchPending_) { RING_ERR() << "Audio switch already requested"; @@ -184,6 +186,8 @@ AudioInput::switchInput(const std::string& resource) decoder_.reset(); decodingFile_ = false; + Manager::instance().getRingBufferPool().unBindHalfDuplexOut(id_, fileId_); + fileBuf_.reset(); currentResource_ = resource; devOptsFound_ = false; @@ -241,9 +245,8 @@ AudioInput::createDecoder() return false; } - // NOTE createDecoder is currently only used for files, which require rate emulation + // NOTE don't emulate rate, file is read as frames are needed auto decoder = std::make_unique<MediaDecoder>(); - decoder->emulateRate(); decoder->setInterruptCallback( [](void* data) -> int { return not static_cast<AudioInput*>(data)->isCapturing(); }, this); diff --git a/src/media/audio/audio_input.h b/src/media/audio/audio_input.h index ba09121b3023531276a8fdd9200aa97b68e20060..2016ee843146e27af61c9630c049c051ab060651 100644 --- a/src/media/audio/audio_input.h +++ b/src/media/audio/audio_input.h @@ -38,6 +38,7 @@ class MediaDecoder; class MediaRecorder; struct MediaStream; class Resampler; +class RingBuffer; class AudioInput : public Observable<std::shared_ptr<MediaFrame>> { @@ -53,8 +54,8 @@ public: MediaStream getInfo() const; private: - void nextFromDevice(); - void nextFromFile(); + void readFromDevice(); + void readFromFile(); bool initDevice(const std::string& device); bool initFile(const std::string& path); bool createDecoder(); @@ -70,9 +71,11 @@ private: std::unique_ptr<Resampler> resampler_; std::unique_ptr<AudioFrameResizer> resizer_; - std::weak_ptr<MediaRecorder> recorder_; std::unique_ptr<MediaDecoder> decoder_; + std::string fileId_; + std::shared_ptr<RingBuffer> fileBuf_; + std::string currentResource_; std::atomic_bool switchPending_ {false}; DeviceParams devOpts_;