Skip to content
Snippets Groups Projects
Commit 005c6ffd authored by Philippe Gorley's avatar Philippe Gorley
Browse files

audio: add file streaming support

When sharing a file, its audio will also be decoded and sent. Audio and
video are synchronized for the first loop of the file only. Further
loops desynchronize them as the audio and video are decoded separately
instead of with the same decoder.

Device audio is muted while streaming. Mixing of the microphone with the
file audio will be added in a later patch.

Adds common audio decoders for file streaming.

Change-Id: Id0593ce4f2d32c249eb7a9672b0091c0d6e07a00
parent 51904192
Branches
Tags
No related merge requests found
...@@ -74,6 +74,10 @@ FFMPEGCONF += \ ...@@ -74,6 +74,10 @@ FFMPEGCONF += \
FFMPEGCONF += \ FFMPEGCONF += \
--enable-decoder=flac \ --enable-decoder=flac \
--enable-decoder=vorbis \ --enable-decoder=vorbis \
--enable-decoder=aac \
--enable-decoder=ac3 \
--enable-decoder=eac3 \
--enable-decoder=mp3 \
--enable-decoder=pcm_u24be \ --enable-decoder=pcm_u24be \
--enable-decoder=pcm_u24le \ --enable-decoder=pcm_u24le \
--enable-decoder=pcm_u32be \ --enable-decoder=pcm_u32be \
......
...@@ -19,9 +19,12 @@ ...@@ -19,9 +19,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "dring/media_const.h" #include "audio_frame_resizer.h"
#include "audio_input.h" #include "audio_input.h"
#include "dring/media_const.h"
#include "fileutils.h" // access
#include "manager.h" #include "manager.h"
#include "media_decoder.h"
#include "resampler.h" #include "resampler.h"
#include "ringbufferpool.h" #include "ringbufferpool.h"
#include "smartools.h" #include "smartools.h"
...@@ -37,7 +40,10 @@ static constexpr auto MS_PER_PACKET = std::chrono::milliseconds(20); ...@@ -37,7 +40,10 @@ static constexpr auto MS_PER_PACKET = std::chrono::milliseconds(20);
AudioInput::AudioInput(const std::string& id) : AudioInput::AudioInput(const std::string& id) :
id_(id), id_(id),
format_(Manager::instance().getRingBufferPool().getInternalAudioFormat()), format_(Manager::instance().getRingBufferPool().getInternalAudioFormat()),
frameSize_(format_.sample_rate * MS_PER_PACKET.count() / 1000),
resampler_(new Resampler), resampler_(new Resampler),
resizer_(new AudioFrameResizer(format_, frameSize_,
[this](std::shared_ptr<AudioFrame>&& f){ frameResized(std::move(f)); })),
loop_([] { return true; }, loop_([] { return true; },
[this] { process(); }, [this] { process(); },
[] {}) [] {})
...@@ -62,10 +68,17 @@ AudioInput::process() ...@@ -62,10 +68,17 @@ AudioInput::process()
RING_DBG() << "Switching audio input to '" << devOpts_.input << "'"; RING_DBG() << "Switching audio input to '" << devOpts_.input << "'";
} }
auto frame = std::make_shared<AudioFrame>(); // send frame to resizer, frameResized will be called when it can be output
if (!nextFromDevice(*frame)) if (decodingFile_)
return; // no frame nextFromFile();
else
nextFromDevice();
}
void
AudioInput::frameResized(std::shared_ptr<AudioFrame>&& ptr)
{
std::shared_ptr<AudioFrame> frame = std::move(ptr);
auto ms = MediaStream("a:local", format_, sent_samples); auto ms = MediaStream("a:local", format_, sent_samples);
frame->pointer()->pts = sent_samples; frame->pointer()->pts = sent_samples;
sent_samples += frame->pointer()->nb_samples; sent_samples += frame->pointer()->nb_samples;
...@@ -73,8 +86,8 @@ AudioInput::process() ...@@ -73,8 +86,8 @@ AudioInput::process()
notify(std::static_pointer_cast<MediaFrame>(frame)); notify(std::static_pointer_cast<MediaFrame>(frame));
} }
bool void
AudioInput::nextFromDevice(AudioFrame& frame) AudioInput::nextFromDevice()
{ {
auto& mainBuffer = Manager::instance().getRingBufferPool(); auto& mainBuffer = Manager::instance().getRingBufferPool();
auto bufferFormat = mainBuffer.getInternalAudioFormat(); auto bufferFormat = mainBuffer.getInternalAudioFormat();
...@@ -85,7 +98,7 @@ AudioInput::nextFromDevice(AudioFrame& frame) ...@@ -85,7 +98,7 @@ AudioInput::nextFromDevice(AudioFrame& frame)
if (mainBuffer.availableForGet(id_) < samplesToGet if (mainBuffer.availableForGet(id_) < samplesToGet
&& not mainBuffer.waitForDataAvailable(id_, samplesToGet, MS_PER_PACKET)) { && not mainBuffer.waitForDataAvailable(id_, samplesToGet, MS_PER_PACKET)) {
return false; return;
} }
// getData resets the format to internal hardware format, will have to be resampled // getData resets the format to internal hardware format, will have to be resampled
...@@ -93,7 +106,7 @@ AudioInput::nextFromDevice(AudioFrame& frame) ...@@ -93,7 +106,7 @@ AudioInput::nextFromDevice(AudioFrame& frame)
micData_.resize(samplesToGet); micData_.resize(samplesToGet);
const auto samples = mainBuffer.getData(micData_, id_); const auto samples = mainBuffer.getData(micData_, id_);
if (samples != samplesToGet) if (samples != samplesToGet)
return false; return;
if (muteState_) // audio is muted, set samples to 0 if (muteState_) // audio is muted, set samples to 0
micData_.reset(); micData_.reset();
...@@ -108,8 +121,45 @@ AudioInput::nextFromDevice(AudioFrame& frame) ...@@ -108,8 +121,45 @@ AudioInput::nextFromDevice(AudioFrame& frame)
} }
auto audioFrame = resampled.toAVFrame(); auto audioFrame = resampled.toAVFrame();
frame.copyFrom(*audioFrame); resizer_->enqueue(std::move(audioFrame));
return true; }
void
AudioInput::nextFromFile()
{
if (!decoder_)
return;
auto frame = std::make_unique<AudioFrame>();
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:
RING_ERR() << "Failed to decode frame";
break;
case MediaDecoder::Status::RestartRequired:
case MediaDecoder::Status::EOFError:
createDecoder();
break;
case MediaDecoder::Status::FrameFinished:
if (inFmt != format_) {
AudioFrame out;
out.pointer()->format = format_.sampleFormat;
out.pointer()->sample_rate = format_.sample_rate;
out.pointer()->channel_layout = av_get_default_channel_layout(format_.nb_channels);
out.pointer()->channels = format_.nb_channels;
resampler_->resample(frame->pointer(), out.pointer());
frame->copyFrom(out);
}
resizer_->enqueue(std::move(frame));
break;
case MediaDecoder::Status::Success:
default:
break;
}
} }
bool bool
...@@ -122,6 +172,22 @@ AudioInput::initDevice(const std::string& device) ...@@ -122,6 +172,22 @@ AudioInput::initDevice(const std::string& device)
return true; return true;
} }
bool
AudioInput::initFile(const std::string& path)
{
if (access(path.c_str(), R_OK) != 0) {
RING_ERR() << "File '" << path << "' not available";
return false;
}
devOpts_ = {};
devOpts_.input = path;
devOpts_.loop = "1";
createDecoder(); // sets devOpts_'s sample rate and number of channels
decodingFile_ = true;
return true; // all required info found
}
std::shared_future<DeviceParams> std::shared_future<DeviceParams>
AudioInput::switchInput(const std::string& resource) AudioInput::switchInput(const std::string& resource)
{ {
...@@ -135,6 +201,9 @@ AudioInput::switchInput(const std::string& resource) ...@@ -135,6 +201,9 @@ AudioInput::switchInput(const std::string& resource)
RING_DBG() << "Switching audio source to match '" << resource << "'"; RING_DBG() << "Switching audio source to match '" << resource << "'";
decoder_.reset();
decodingFile_ = false;
currentResource_ = resource; currentResource_ = resource;
devOptsFound_ = false; devOptsFound_ = false;
...@@ -159,7 +228,13 @@ AudioInput::switchInput(const std::string& resource) ...@@ -159,7 +228,13 @@ AudioInput::switchInput(const std::string& resource)
return {}; return {};
const auto suffix = resource.substr(pos + sep.size()); const auto suffix = resource.substr(pos + sep.size());
if (initDevice(suffix)) bool ready = false;
if (prefix == DRing::Media::VideoProtocolPrefix::FILE)
ready = initFile(suffix);
else
ready = initDevice(suffix);
if (ready)
foundDevOpts(devOpts_); foundDevOpts(devOpts_);
switchPending_ = true; switchPending_ = true;
...@@ -176,11 +251,51 @@ AudioInput::foundDevOpts(const DeviceParams& params) ...@@ -176,11 +251,51 @@ AudioInput::foundDevOpts(const DeviceParams& params)
} }
} }
void
AudioInput::createDecoder()
{
decoder_.reset();
if (devOpts_.input.empty()) {
foundDevOpts(devOpts_);
return;
}
// NOTE createDecoder is currently only used for files, which require rate emulation
auto decoder = std::make_unique<MediaDecoder>();
decoder->emulateRate();
decoder->setInterruptCallback(
[](void* data) -> int { return not static_cast<AudioInput*>(data)->isCapturing(); },
this);
if (decoder->openInput(devOpts_) < 0) {
RING_ERR() << "Could not open input '" << devOpts_.input << "'";
foundDevOpts(devOpts_);
return;
}
if (decoder->setupFromAudioData() < 0) {
RING_ERR() << "Could not setup decoder for '" << devOpts_.input << "'";
foundDevOpts(devOpts_);
return;
}
auto ms = decoder->getStream(devOpts_.input);
devOpts_.channel = ms.nbChannels;
devOpts_.framerate = ms.sampleRate;
RING_DBG() << "Created audio decoder: " << ms;
decoder_ = std::move(decoder);
foundDevOpts(devOpts_);
}
void void
AudioInput::setFormat(const AudioFormat& fmt) AudioInput::setFormat(const AudioFormat& fmt)
{ {
std::lock_guard<std::mutex> lk(fmtMutex_); std::lock_guard<std::mutex> lk(fmtMutex_);
format_ = fmt; format_ = fmt;
frameSize_ = format_.sample_rate * MS_PER_PACKET.count() / 1000;
resizer_.reset(new AudioFrameResizer(format_, frameSize_,
[this](std::shared_ptr<AudioFrame>&& f){ frameResized(std::move(f)); }));
} }
void void
......
...@@ -33,6 +33,9 @@ ...@@ -33,6 +33,9 @@
namespace ring { namespace ring {
class AudioFrameResizer;
class MediaDecoder;
class MediaRecorder;
struct MediaStream; struct MediaStream;
class Resampler; class Resampler;
...@@ -50,8 +53,12 @@ public: ...@@ -50,8 +53,12 @@ public:
MediaStream getInfo() const; MediaStream getInfo() const;
private: private:
bool nextFromDevice(AudioFrame& frame); void nextFromDevice();
void nextFromFile();
bool initDevice(const std::string& device); bool initDevice(const std::string& device);
bool initFile(const std::string& path);
void createDecoder();
void frameResized(std::shared_ptr<AudioFrame>&& ptr);
std::string id_; std::string id_;
AudioBuffer micData_; AudioBuffer micData_;
...@@ -59,8 +66,12 @@ private: ...@@ -59,8 +66,12 @@ private:
uint64_t sent_samples = 0; uint64_t sent_samples = 0;
mutable std::mutex fmtMutex_ {}; mutable std::mutex fmtMutex_ {};
AudioFormat format_; AudioFormat format_;
int frameSize_;
std::unique_ptr<Resampler> resampler_; std::unique_ptr<Resampler> resampler_;
std::unique_ptr<AudioFrameResizer> resizer_;
std::weak_ptr<MediaRecorder> recorder_;
std::unique_ptr<MediaDecoder> decoder_;
std::string currentResource_; std::string currentResource_;
std::atomic_bool switchPending_ {false}; std::atomic_bool switchPending_ {false};
...@@ -69,6 +80,7 @@ private: ...@@ -69,6 +80,7 @@ private:
std::shared_future<DeviceParams> futureDevOpts_; std::shared_future<DeviceParams> futureDevOpts_;
std::atomic_bool devOptsFound_ {false}; std::atomic_bool devOptsFound_ {false};
void foundDevOpts(const DeviceParams& params); void foundDevOpts(const DeviceParams& params);
std::atomic_bool decodingFile_ {false};
ThreadLoop loop_; ThreadLoop loop_;
void process(); void process();
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
#include "audiobuffer.h" #include "audiobuffer.h"
#include "media_device.h" #include "media_device.h"
#include "threadloop.h"
#include "rtp_session.h" #include "rtp_session.h"
#include "threadloop.h"
#include <string> #include <string>
#include <memory> #include <memory>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment