blob: ad9d779eead1f066f7cd17e442adb29ac971a421 [file] [log] [blame]
/**
* Copyright (C) 2020 Savoir-faire Linux Inc.
*
* Author: Aline Gondim Santos <aline.gondimsantos@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 "filterAudioSubscriber.h"
extern "C" {
#include <libavutil/display.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersrc.h>
}
#include <frameUtils.h>
#include <pluglog.h>
const std::string TAG = "filter";
const char sep = separator();
namespace jami {
FilterAudioSubscriber::FilterAudioSubscriber(const std::string& dataPath, const std::string& irFile)
: path_ {dataPath}
{
setIRFile(irFile);
}
FilterAudioSubscriber::~FilterAudioSubscriber()
{
std::ostringstream oss;
oss << "~filterMediaProcessor" << std::endl;
Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
}
void
FilterAudioSubscriber::setIRFile(const std::string& irFile)
{
irFile_ = path_ + "/" + irFile;
firstRun = true;
reverbFilter_.clean();
}
void
FilterAudioSubscriber::setFilterDescription(const int pSampleRate, const int pSamples)
{
int rSamples = 1024; // due to afir internal fifo
int midSampleRate = pSampleRate * rSamples / pSamples;
filterDescription_
= "[ input ] aformat=sample_fmts=s16:sample_rates=" + std::to_string(midSampleRate)
+ ":channel_layouts=stereo [ resample1 ] , "
"[ resample1 ] [ ir0 ] afir=maxir=1:wet=10:dry=10:irgain=1:irfmt=mono:maxp="
+ std::to_string(rSamples) + ":minp=" + std::to_string(rSamples)
+ " [ reverb ] , "
"[ reverb ] aformat=sample_fmts=s16:sample_rates="
+ std::to_string(pSampleRate) + ":channel_layouts=stereo ";
}
AudioFormat
FilterAudioSubscriber::getIRAVFrameInfos()
{
AudioFormat rAudioFormat = AudioFormat(0, 0);
int i;
pFormatCtx_ = avformat_alloc_context();
// Open
if (avformat_open_input(&pFormatCtx_, irFile_.c_str(), NULL, NULL) != 0) {
Plog::log(Plog::LogPriority::INFO, TAG, "Couldn't open input stream.");
return rAudioFormat;
}
// Retrieve stream information
if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) {
Plog::log(Plog::LogPriority::INFO, TAG, "Couldn't find stream information.");
return rAudioFormat;
}
// Dump valid information onto standard error
av_dump_format(pFormatCtx_, 0, irFile_.c_str(), false);
// Find the first audio stream
audioStream_ = -1;
for (i = 0; i < static_cast<int>(pFormatCtx_->nb_streams); i++)
if (pFormatCtx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStream_ = i;
break;
}
if (audioStream_ == -1) {
Plog::log(Plog::LogPriority::INFO, TAG, "Didn't find a audio stream.");
return rAudioFormat;
}
rAudioFormat = AudioFormat(pFormatCtx_->streams[audioStream_]->codecpar->sample_rate,
pFormatCtx_->streams[audioStream_]->codecpar->channels,
static_cast<AVSampleFormat>(
pFormatCtx_->streams[audioStream_]->codecpar->format));
return rAudioFormat;
}
void
FilterAudioSubscriber::setIRAVFrame()
{
int ret, got_frame;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
AVPacket* packet;
FILE* pFile = fopen(irFile_.c_str(), "rb");
pCodecCtx = pFormatCtx_->streams[audioStream_]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
Plog::log(Plog::LogPriority::INFO, TAG, "Codec not found.");
return;
}
// Open codec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
Plog::log(Plog::LogPriority::INFO, TAG, "Could not open codec.");
return;
}
packet = av_packet_alloc();
int buffsize
= av_samples_get_buffer_size(NULL,
pFormatCtx_->streams[audioStream_]->codecpar->channels,
pFormatCtx_->streams[audioStream_]->codecpar->frame_size,
static_cast<AVSampleFormat>(
pFormatCtx_->streams[audioStream_]->codecpar->format),
1);
int frames = static_cast<int>(pFormatCtx_->streams[audioStream_]->codecpar->sample_rate
/ pFormatCtx_->streams[audioStream_]->codecpar->frame_size);
int AUDIO_INBUF_SIZE = buffsize * frames / 4;
uint8_t* inbuf = new uint8_t[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
packet->data = inbuf;
packet->size = static_cast<int>(fread(inbuf, 1, AUDIO_INBUF_SIZE, pFile));
int idx = 0;
AVFrame* pFrame = av_frame_alloc();
while (packet->size > 0 && idx < frames) {
idx++;
got_frame = 0;
int len = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, packet);
if (len < 0) {
break;
}
if (got_frame) {
reverbFilter_.feedInput(pFrame, "ir0");
}
packet->size -= len;
packet->data += len;
}
av_frame_unref(pFrame);
reverbFilter_.feedEOF("ir0");
fclose(pFile);
avcodec_close(pCodecCtx);
av_frame_free(&pFrame);
av_packet_free(&packet);
avformat_close_input(&pFormatCtx_);
avformat_free_context(pFormatCtx_);
}
void
FilterAudioSubscriber::update(Observable<AVFrame*>*, AVFrame* const& pluginFrame)
{
if (!pluginFrame)
return;
if (firstRun) {
setFilterDescription(pluginFrame->sample_rate, pluginFrame->nb_samples);
AudioFormat afmt_ = AudioFormat(pluginFrame->sample_rate,
pluginFrame->channels,
static_cast<AVSampleFormat>(pluginFrame->format));
AudioFormat irfmt_ = getIRAVFrameInfos();
MediaStream ms_ = MediaStream("input", afmt_);
MediaStream irms_ = MediaStream("ir0", irfmt_);
reverbFilter_.initialize(filterDescription_, {ms_, irms_});
setIRAVFrame();
firstRun = false;
}
if (!reverbFilter_.initialized_)
return;
AVFrame* filteredFrame;
if (reverbFilter_.feedInput(pluginFrame, "input") == 0) {
if ((filteredFrame = reverbFilter_.readOutput()))
moveFrom(pluginFrame, filteredFrame);
av_frame_unref(filteredFrame);
av_frame_free(&filteredFrame);
}
}
void
FilterAudioSubscriber::attached(Observable<AVFrame*>* observable)
{
std::ostringstream oss;
oss << "::Attached ! " << std::endl;
Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
observable_ = observable;
}
void
FilterAudioSubscriber::detached(Observable<AVFrame*>*)
{
reverbFilter_.clean();
firstRun = true;
observable_ = nullptr;
std::ostringstream oss;
oss << "::Detached()" << std::endl;
Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
}
void
FilterAudioSubscriber::detach()
{
if (observable_) {
reverbFilter_.clean();
firstRun = true;
std::ostringstream oss;
oss << "::Calling detach()" << std::endl;
Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
observable_->detach(this);
}
}
} // namespace jami