audio_input.cpp 8.24 KB
Newer Older
1
/*
Sébastien Blin's avatar
Sébastien Blin committed
2
 *  Copyright (C) 2018-2019 Savoir-faire Linux Inc.
3 4
 *
 *  Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>
5
 *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 *  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.
 */

22
#include "audio_frame_resizer.h"
23
#include "audio_input.h"
24 25
#include "dring/media_const.h"
#include "fileutils.h" // access
26
#include "manager.h"
27
#include "media_decoder.h"
28
#include "resampler.h"
29
#include "ringbuffer.h"
30 31
#include "ringbufferpool.h"
#include "smartools.h"
32 33 34

#include <future>
#include <chrono>
35
#include <memory>
36

37
namespace ring {
38

39 40
static constexpr auto MS_PER_PACKET = std::chrono::milliseconds(20);

41 42
AudioInput::AudioInput(const std::string& id) :
    id_(id),
Philippe Gorley's avatar
Philippe Gorley committed
43
    format_(Manager::instance().getRingBufferPool().getInternalAudioFormat()),
44
    frameSize_(format_.sample_rate * MS_PER_PACKET.count() / 1000),
Philippe Gorley's avatar
Philippe Gorley committed
45
    resampler_(new Resampler),
46 47
    resizer_(new AudioFrameResizer(format_, frameSize_,
       [this](std::shared_ptr<AudioFrame>&& f){ frameResized(std::move(f)); })),
48
    fileId_(id + "_file"),
49 50 51
    loop_([] { return true; },
          [this] { process(); },
          [] {})
52
{
53
    RING_DBG() << "Creating audio input with id: " << id;
54 55 56 57 58 59 60 61 62 63
    loop_.start();
}

AudioInput::~AudioInput()
{
    loop_.join();
}

void
AudioInput::process()
64
{
65 66 67
    // 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_);
68 69 70 71 72 73 74
    if (switchPending_.exchange(false)) {
        if (devOpts_.input.empty())
            RING_DBG() << "Switching to default audio input";
        else
            RING_DBG() << "Switching audio input to '" << devOpts_.input << "'";
    }

75
    readFromDevice();
76
}
77

78 79 80 81
void
AudioInput::frameResized(std::shared_ptr<AudioFrame>&& ptr)
{
    std::shared_ptr<AudioFrame> frame = std::move(ptr);
82 83 84
    frame->pointer()->pts = sent_samples;
    sent_samples += frame->pointer()->nb_samples;

85
    notify(std::static_pointer_cast<MediaFrame>(frame));
86 87
}

88
void
89
AudioInput::readFromDevice()
90 91
{
    auto& mainBuffer = Manager::instance().getRingBufferPool();
92
    auto bufferFormat = mainBuffer.getInternalAudioFormat();
93

94 95 96 97 98
    if (decodingFile_ )
        while (fileBuf_->isEmpty())
            readFromFile();

    if (not mainBuffer.waitForDataAvailable(id_, MS_PER_PACKET))
99
        return;
100

101 102
    auto samples = mainBuffer.getData(id_);
    if (not samples)
103
        return;
104

Philippe Gorley's avatar
Philippe Gorley committed
105 106
    if (muteState_)
        libav_utils::fillWithSilence(samples->pointer());
107

108 109 110 111
    std::lock_guard<std::mutex> lk(fmtMutex_);
    if (bufferFormat != format_)
        samples = resampler_->resample(std::move(samples), format_);
    resizer_->enqueue(std::move(samples));
112 113 114
}

void
115
AudioInput::readFromFile()
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
{
    if (!decoder_)
        return;

    auto frame = std::make_unique<AudioFrame>();
    const auto ret = decoder_->decode(*frame);
    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:
132
        fileBuf_->put(std::move(frame));
133 134 135 136 137
        break;
    case MediaDecoder::Status::Success:
    default:
        break;
    }
138
}
139

140 141 142 143 144 145 146 147 148 149
bool
AudioInput::initDevice(const std::string& device)
{
    devOpts_ = {};
    devOpts_.input = device;
    devOpts_.channel = format_.nb_channels;
    devOpts_.framerate = format_.sample_rate;
    return true;
}

150 151 152 153 154 155 156 157 158 159 160
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";
161 162 163 164 165
    // sets devOpts_'s sample rate and number of channels
    if (!createDecoder()) {
        RING_WARN() << "Cannot decode audio from file, switching back to default device";
        return initDevice("");
    }
166
    fileBuf_ = Manager::instance().getRingBufferPool().createRingBuffer(fileId_);
167
    // have file audio mixed into the call buffer so it gets sent to the peer
168
    Manager::instance().getRingBufferPool().bindHalfDuplexOut(id_, fileId_);
169 170
    // have file audio mixed into the local buffer so it gets played
    Manager::instance().getRingBufferPool().bindHalfDuplexOut(RingBufferPool::DEFAULT_ID, fileId_);
171
    decodingFile_ = true;
172
    return true;
173 174
}

175 176 177
std::shared_future<DeviceParams>
AudioInput::switchInput(const std::string& resource)
{
178
    // Always switch inputs, even if it's the same resource, so audio will be in sync with video
179 180 181 182

    if (switchPending_) {
        RING_ERR() << "Audio switch already requested";
        return {};
183
    }
184

185 186
    RING_DBG() << "Switching audio source to match '" << resource << "'";

187 188
    decoder_.reset();
    decodingFile_ = false;
189
    Manager::instance().getRingBufferPool().unBindHalfDuplexOut(id_, fileId_);
190
    Manager::instance().getRingBufferPool().unBindHalfDuplexOut(RingBufferPool::DEFAULT_ID, fileId_);
191
    fileBuf_.reset();
192

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    currentResource_ = resource;
    devOptsFound_ = false;

    std::promise<DeviceParams> p;
    foundDevOpts_.swap(p);

    if (resource.empty()) {
        devOpts_ = {};
        switchPending_ = true;
        futureDevOpts_ = foundDevOpts_.get_future();
        return futureDevOpts_;
    }

    static const std::string sep = DRing::Media::VideoProtocolPrefix::SEPARATOR;

    const auto pos = resource.find(sep);
    if (pos == std::string::npos)
        return {};

    const auto prefix = resource.substr(0, pos);
    if ((pos + sep.size()) >= resource.size())
        return {};

    const auto suffix = resource.substr(pos + sep.size());
217 218 219 220 221 222 223
    bool ready = false;
    if (prefix == DRing::Media::VideoProtocolPrefix::FILE)
        ready = initFile(suffix);
    else
        ready = initDevice(suffix);

    if (ready)
224 225 226 227 228 229 230 231 232 233 234 235 236 237
        foundDevOpts(devOpts_);

    switchPending_ = true;
    futureDevOpts_ = foundDevOpts_.get_future().share();
    return futureDevOpts_;
}

void
AudioInput::foundDevOpts(const DeviceParams& params)
{
    if (!devOptsFound_) {
        devOptsFound_ = true;
        foundDevOpts_.set_value(params);
    }
238 239
}

240
bool
241 242 243 244 245
AudioInput::createDecoder()
{
    decoder_.reset();
    if (devOpts_.input.empty()) {
        foundDevOpts(devOpts_);
246
        return false;
247 248
    }

249
    // NOTE don't emulate rate, file is read as frames are needed
250 251 252 253 254 255 256 257
    auto decoder = std::make_unique<MediaDecoder>();
    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_);
258
        return false;
259 260 261 262 263
    }

    if (decoder->setupFromAudioData() < 0) {
        RING_ERR() << "Could not setup decoder for '" << devOpts_.input << "'";
        foundDevOpts(devOpts_);
264
        return false;
265 266 267 268 269 270 271 272 273
    }

    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_);
274
    return true;
275 276
}

Philippe Gorley's avatar
Philippe Gorley committed
277 278 279 280 281
void
AudioInput::setFormat(const AudioFormat& fmt)
{
    std::lock_guard<std::mutex> lk(fmtMutex_);
    format_ = fmt;
282
    resizer_->setFormat(format_, format_.sample_rate * MS_PER_PACKET.count() / 1000);
Philippe Gorley's avatar
Philippe Gorley committed
283 284
}

285 286 287 288 289 290
void
AudioInput::setMuted(bool isMuted)
{
    muteState_ = isMuted;
}

291 292 293 294 295 296 297 298
MediaStream
AudioInput::getInfo() const
{
    std::lock_guard<std::mutex> lk(fmtMutex_);
    auto ms = MediaStream("a:local", format_, sent_samples);
    return ms;
}

299
} // namespace ring