Skip to content
Snippets Groups Projects
Select Git revision
  • 82678f3d9454766cd37d4a6fe4966f448eec12ca
  • master default protected
2 results

FilterAudioSubscriber.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    FilterAudioSubscriber.cpp 7.71 KiB
    /**
     *  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 <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