Skip to content
Snippets Groups Projects
Select Git revision
  • 0490e3910916a2bdec676ebbbb5d16baa9be254f
  • 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.94 KiB
    /**
     *  Copyright (C) 2020-2021 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 <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()
    {
        if(pFormatCtx_) {
            avformat_close_input(&pFormatCtx_);
            avformat_free_context(pFormatCtx_);
        }
        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, const int pFormat)
    {
        int rSamples = 1024; // due to afir internal fifo
        int midSampleRate = pSampleRate * rSamples / pSamples;
        std::string outFormat {"s16"};
        if (pFormat == AV_SAMPLE_FMT_U8)
            outFormat = "u8";
        else if (pFormat == AV_SAMPLE_FMT_S32)
            outFormat = "s32";
        else if (pFormat == AV_SAMPLE_FMT_FLT)
            outFormat = "f32";
        else if (pFormat == AV_SAMPLE_FMT_DBL)
            outFormat = "f64";
        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 ] , "
    #ifdef __DEBUG__
                "[ reverb ] aformat=sample_fmts=" + outFormat + "p:sample_rates="
    #else
                "[ reverb ] aformat=sample_fmts=" + outFormat + ":sample_rates="
    #endif
              + 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->ch_layout.nb_channels,
                                   static_cast<AVSampleFormat>(
                                       pFormatCtx_->streams[audioStream_]->codecpar->format));
    
        return rAudioFormat;
    }
    
    void
    FilterAudioSubscriber::setIRAVFrame()
    {
        AVCodecContext* pCodecCtx;
    
        const AVCodec* pCodec = avcodec_find_decoder(pFormatCtx_->streams[audioStream_]->codecpar->codec_id);
        if (pCodec == NULL) {
            Plog::log(Plog::LogPriority::INFO, TAG, "Codec not found.");
            return;
        }
    
        pCodecCtx = avcodec_alloc_context3(pCodec);
        if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx_->streams[audioStream_]->codecpar) < 0) {
            Plog::log(Plog::LogPriority::INFO, __FILE__, "Failed to copy decoder parameters to decoder context.");
            return;
        }
        // Open codec
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            Plog::log(Plog::LogPriority::INFO, TAG, "Could not open codec.");
            return;
        }
    
        AVPacket* packet = av_packet_alloc();
        AVFrame* pFrame = av_frame_alloc();
    
        int idx = 0;
        while (av_read_frame(pFormatCtx_, packet) == 0 && idx < 40) { // Limit for filter coefficients
            idx++;
            av_frame_unref(pFrame);
            av_frame_free(&pFrame);
            pFrame = av_frame_alloc();
    
            if (avcodec_send_packet(pCodecCtx, packet) < 0) {
                Plog::log(Plog::LogPriority::INFO, __FILE__, "Error submitting the packet to the decoder");
                break;
            }
    
            // Read frames from decoder
            while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
                reverbFilter_.feedInput(pFrame, "ir0");
            }
            av_packet_unref(packet);
        }
    
        reverbFilter_.feedEOF("ir0");
    
        av_frame_unref(pFrame);
        av_frame_free(&pFrame);
        av_packet_unref(packet);
        av_packet_free(&packet);
        avcodec_close(pCodecCtx);
        avcodec_free_context(&pCodecCtx);
        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, pluginFrame->format);
            AudioFormat afmt_ = AudioFormat(pluginFrame->sample_rate,
                                            pluginFrame->ch_layout.nb_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;
    
        if (reverbFilter_.feedInput(pluginFrame, "input") == 0) {
            AVFrame* filteredFrame = reverbFilter_.readOutput();
            if (filteredFrame && filteredFrame->nb_samples == pluginFrame->nb_samples) {
                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