agsantos | c9181b4 | 2020-11-26 12:03:04 -0500 | [diff] [blame^] | 1 | /** |
| 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 | |
| 23 | extern "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 | |
| 33 | const std::string TAG = "filter"; |
| 34 | const char sep = separator(); |
| 35 | |
| 36 | namespace jami { |
| 37 | |
| 38 | FilterAudioSubscriber::FilterAudioSubscriber(const std::string& dataPath, const std::string& irFile) |
| 39 | : path_ {dataPath} |
| 40 | { |
| 41 | setIRFile(irFile); |
| 42 | } |
| 43 | |
| 44 | FilterAudioSubscriber::~FilterAudioSubscriber() |
| 45 | { |
| 46 | std::ostringstream oss; |
| 47 | oss << "~filterMediaProcessor" << std::endl; |
| 48 | Plog::log(Plog::LogPriority::INFO, TAG, oss.str()); |
| 49 | } |
| 50 | |
| 51 | void |
| 52 | FilterAudioSubscriber::setIRFile(const std::string& irFile) |
| 53 | { |
| 54 | irFile_ = path_ + "/" + irFile; |
| 55 | firstRun = true; |
| 56 | reverbFilter_.clean(); |
| 57 | } |
| 58 | |
| 59 | void |
| 60 | FilterAudioSubscriber::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 | |
| 74 | AudioFormat |
| 75 | FilterAudioSubscriber::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 | |
| 115 | void |
| 116 | FilterAudioSubscriber::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 | |
| 180 | void |
| 181 | FilterAudioSubscriber::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 | |
| 211 | void |
| 212 | FilterAudioSubscriber::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 | |
| 220 | void |
| 221 | FilterAudioSubscriber::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 | |
| 231 | void |
| 232 | FilterAudioSubscriber::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 |