blob: ad9d779eead1f066f7cd17e442adb29ac971a421 [file] [log] [blame]
agsantosc9181b42020-11-26 12:03:04 -05001/**
2 * Copyright (C) 2020 Savoir-faire Linux Inc.
3 *
4 * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "filterAudioSubscriber.h"
22
23extern "C" {
24#include <libavutil/display.h>
25#include <libavcodec/avcodec.h>
26#include <libavformat/avformat.h>
27#include <libavfilter/buffersrc.h>
28}
29#include <frameUtils.h>
30
31#include <pluglog.h>
32
33const std::string TAG = "filter";
34const char sep = separator();
35
36namespace jami {
37
38FilterAudioSubscriber::FilterAudioSubscriber(const std::string& dataPath, const std::string& irFile)
39 : path_ {dataPath}
40{
41 setIRFile(irFile);
42}
43
44FilterAudioSubscriber::~FilterAudioSubscriber()
45{
46 std::ostringstream oss;
47 oss << "~filterMediaProcessor" << std::endl;
48 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
49}
50
51void
52FilterAudioSubscriber::setIRFile(const std::string& irFile)
53{
54 irFile_ = path_ + "/" + irFile;
55 firstRun = true;
56 reverbFilter_.clean();
57}
58
59void
60FilterAudioSubscriber::setFilterDescription(const int pSampleRate, const int pSamples)
61{
62 int rSamples = 1024; // due to afir internal fifo
63 int midSampleRate = pSampleRate * rSamples / pSamples;
64 filterDescription_
65 = "[ input ] aformat=sample_fmts=s16:sample_rates=" + std::to_string(midSampleRate)
66 + ":channel_layouts=stereo [ resample1 ] , "
67 "[ resample1 ] [ ir0 ] afir=maxir=1:wet=10:dry=10:irgain=1:irfmt=mono:maxp="
68 + std::to_string(rSamples) + ":minp=" + std::to_string(rSamples)
69 + " [ reverb ] , "
70 "[ reverb ] aformat=sample_fmts=s16:sample_rates="
71 + std::to_string(pSampleRate) + ":channel_layouts=stereo ";
72}
73
74AudioFormat
75FilterAudioSubscriber::getIRAVFrameInfos()
76{
77 AudioFormat rAudioFormat = AudioFormat(0, 0);
78 int i;
79
80 pFormatCtx_ = avformat_alloc_context();
81 // Open
82 if (avformat_open_input(&pFormatCtx_, irFile_.c_str(), NULL, NULL) != 0) {
83 Plog::log(Plog::LogPriority::INFO, TAG, "Couldn't open input stream.");
84 return rAudioFormat;
85 }
86 // Retrieve stream information
87 if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) {
88 Plog::log(Plog::LogPriority::INFO, TAG, "Couldn't find stream information.");
89 return rAudioFormat;
90 }
91
92 // Dump valid information onto standard error
93 av_dump_format(pFormatCtx_, 0, irFile_.c_str(), false);
94
95 // Find the first audio stream
96 audioStream_ = -1;
97 for (i = 0; i < static_cast<int>(pFormatCtx_->nb_streams); i++)
98 if (pFormatCtx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
99 audioStream_ = i;
100 break;
101 }
102
103 if (audioStream_ == -1) {
104 Plog::log(Plog::LogPriority::INFO, TAG, "Didn't find a audio stream.");
105 return rAudioFormat;
106 }
107 rAudioFormat = AudioFormat(pFormatCtx_->streams[audioStream_]->codecpar->sample_rate,
108 pFormatCtx_->streams[audioStream_]->codecpar->channels,
109 static_cast<AVSampleFormat>(
110 pFormatCtx_->streams[audioStream_]->codecpar->format));
111
112 return rAudioFormat;
113}
114
115void
116FilterAudioSubscriber::setIRAVFrame()
117{
118 int ret, got_frame;
119 AVCodecContext* pCodecCtx;
120 AVCodec* pCodec;
121 AVPacket* packet;
122
123 FILE* pFile = fopen(irFile_.c_str(), "rb");
124
125 pCodecCtx = pFormatCtx_->streams[audioStream_]->codec;
126 pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
127 if (pCodec == NULL) {
128 Plog::log(Plog::LogPriority::INFO, TAG, "Codec not found.");
129 return;
130 }
131
132 // Open codec
133 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
134 Plog::log(Plog::LogPriority::INFO, TAG, "Could not open codec.");
135 return;
136 }
137
138 packet = av_packet_alloc();
139 int buffsize
140 = av_samples_get_buffer_size(NULL,
141 pFormatCtx_->streams[audioStream_]->codecpar->channels,
142 pFormatCtx_->streams[audioStream_]->codecpar->frame_size,
143 static_cast<AVSampleFormat>(
144 pFormatCtx_->streams[audioStream_]->codecpar->format),
145 1);
146 int frames = static_cast<int>(pFormatCtx_->streams[audioStream_]->codecpar->sample_rate
147 / pFormatCtx_->streams[audioStream_]->codecpar->frame_size);
148 int AUDIO_INBUF_SIZE = buffsize * frames / 4;
149 uint8_t* inbuf = new uint8_t[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
150
151 packet->data = inbuf;
152 packet->size = static_cast<int>(fread(inbuf, 1, AUDIO_INBUF_SIZE, pFile));
153 int idx = 0;
154 AVFrame* pFrame = av_frame_alloc();
155 while (packet->size > 0 && idx < frames) {
156 idx++;
157 got_frame = 0;
158 int len = avcodec_decode_audio4(pCodecCtx, pFrame, &got_frame, packet);
159 if (len < 0) {
160 break;
161 }
162 if (got_frame) {
163 reverbFilter_.feedInput(pFrame, "ir0");
164 }
165 packet->size -= len;
166 packet->data += len;
167 }
168 av_frame_unref(pFrame);
169
170 reverbFilter_.feedEOF("ir0");
171
172 fclose(pFile);
173 avcodec_close(pCodecCtx);
174 av_frame_free(&pFrame);
175 av_packet_free(&packet);
176 avformat_close_input(&pFormatCtx_);
177 avformat_free_context(pFormatCtx_);
178}
179
180void
181FilterAudioSubscriber::update(Observable<AVFrame*>*, AVFrame* const& pluginFrame)
182{
183 if (!pluginFrame)
184 return;
185
186 if (firstRun) {
187 setFilterDescription(pluginFrame->sample_rate, pluginFrame->nb_samples);
188 AudioFormat afmt_ = AudioFormat(pluginFrame->sample_rate,
189 pluginFrame->channels,
190 static_cast<AVSampleFormat>(pluginFrame->format));
191 AudioFormat irfmt_ = getIRAVFrameInfos();
192 MediaStream ms_ = MediaStream("input", afmt_);
193 MediaStream irms_ = MediaStream("ir0", irfmt_);
194 reverbFilter_.initialize(filterDescription_, {ms_, irms_});
195 setIRAVFrame();
196 firstRun = false;
197 }
198
199 if (!reverbFilter_.initialized_)
200 return;
201
202 AVFrame* filteredFrame;
203 if (reverbFilter_.feedInput(pluginFrame, "input") == 0) {
204 if ((filteredFrame = reverbFilter_.readOutput()))
205 moveFrom(pluginFrame, filteredFrame);
206 av_frame_unref(filteredFrame);
207 av_frame_free(&filteredFrame);
208 }
209}
210
211void
212FilterAudioSubscriber::attached(Observable<AVFrame*>* observable)
213{
214 std::ostringstream oss;
215 oss << "::Attached ! " << std::endl;
216 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
217 observable_ = observable;
218}
219
220void
221FilterAudioSubscriber::detached(Observable<AVFrame*>*)
222{
223 reverbFilter_.clean();
224 firstRun = true;
225 observable_ = nullptr;
226 std::ostringstream oss;
227 oss << "::Detached()" << std::endl;
228 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
229}
230
231void
232FilterAudioSubscriber::detach()
233{
234 if (observable_) {
235 reverbFilter_.clean();
236 firstRun = true;
237 std::ostringstream oss;
238 oss << "::Calling detach()" << std::endl;
239 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
240 observable_->detach(this);
241 }
242}
243} // namespace jami