AudioFilter: add process test

GitLab: https://git.jami.net/savoirfairelinux/jami-daemon/-/issues/745

Change-Id: I4ec8adcc7cb1dbbc52b908fdddc7b380cded7350
diff --git a/AudioFilter/.gitignore b/AudioFilter/.gitignore
new file mode 100644
index 0000000..e090044
--- /dev/null
+++ b/AudioFilter/.gitignore
@@ -0,0 +1,3 @@
+/AudioFilter
+*.mp3
+AudioFilter*
diff --git a/AudioFilter/CMakeLists.txt b/AudioFilter/CMakeLists.txt
index 39b2590..0ff0a5e 100644
--- a/AudioFilter/CMakeLists.txt
+++ b/AudioFilter/CMakeLists.txt
@@ -2,7 +2,7 @@
 
 # set the project name
 set (ProjectName AudioFilter)
-set (Version 0.1.0)
+set (Version 1.0.0)
 
 project(${ProjectName} VERSION ${Version})
 
@@ -34,12 +34,19 @@
 
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED True)
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
+
+if(TESTPROCESS)
+    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D__DEBUG__ /MD")
+    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /D__DEBUG__ /MD")
+else()
+    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MD")
+    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
+endif()
 
 set(plugin_SRC FilterMediaHandler.cpp
                FilterAudioSubscriber.cpp
                main.cpp
+               ../lib/common.cpp
                ../lib/frameFilter.cpp
                ../lib/frameUtils.cpp
                )
@@ -51,11 +58,18 @@
                ../lib/frameFilter.h
                ../lib/mediaStream.h
                ../lib/pluglog.h
+               ../lib/common.h
                )
 
-add_library(${ProjectName} SHARED ${plugin_SRC}
-                                  ${plugin_HDR}
-                                  )
+if(TESTPROCESS)
+    add_executable(${ProjectName} ${plugin_SRC}
+                                    ${plugin_HDR}
+                                    )
+else()
+    add_library(${ProjectName} SHARED ${plugin_SRC}
+                                        ${plugin_HDR}
+                                        )
+endif()
 
 target_include_directories(${ProjectName} PUBLIC ${PROJECT_BINARY_DIR}
                                                  ${PROJECT_SOURCE_DIR}
@@ -64,32 +78,50 @@
                                                  ${CONTRIB_PATH}
                                                  ${CONTRIB_PATH}/build/fmt/include
                                                  ${FFMPEG}/include
+                                                 ${CONTRIB_PATH}/build/yaml-cpp/include
                                                  )
 target_link_directories(${ProjectName} PUBLIC ${CONTRIB_PATH}
                                         ${FFMPEG}/lib
                                         ${CONTRIB_PATH}/msvc/lib/x64
                                         ${CONTRIB_PATH}/build/fmt/msvc/Release
+                                        ${CONTRIB_PATH}/build/yaml-cpp/msvc/Release
                                         )
 
-target_link_libraries(${ProjectName} PUBLIC libavfilter libswscale libswresample libavformat libavcodec libavutil libvpx libx264 libopus libmfx
+target_link_libraries(${ProjectName} PUBLIC libyaml-cppmd libavfilter libswscale
+                                     libswresample libavformat libavcodec libavutil
+                                     libmp3lame libvpx libx264 libopus libmfx
                                      ws2_32 Bcrypt Secur32)
 
 add_custom_command(
     TARGET ${ProjectName}
     PRE_BUILD
     COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/ffmpeg/ ${CONTRIB_PATH}/src/ffmpeg
+    COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/../contrib/mp3lame ${CONTRIB_PATH}/src/mp3lame
     COMMAND python ${DAEMON}/compat/msvc/winmake.py -fb ffmpeg
-    COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --preassemble --plugin=${ProjectName}
-    COMMENT "Assembling Plugin files"
-)
-
-add_custom_command(
-    TARGET ${ProjectName}
-    POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${ProjectName}.lib ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
-    COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
-    COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=${ProjectName}
     COMMAND cd ${CONTRIB_PATH}/src/ffmpeg/
     COMMAND git checkout *
-    COMMENT "Generating JPL archive"
 )
+if(TESTPROCESS)
+    add_custom_command(
+        TARGET ${ProjectName}
+        PRE_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/testPreferences.yml ${PROJECT_BINARY_DIR}/
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/sample.mp3 ${PROJECT_BINARY_DIR}/
+    )
+else()
+    add_custom_command(
+        TARGET ${ProjectName}
+        PRE_BUILD
+        COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --preassemble --plugin=${ProjectName}
+        COMMENT "Assembling Plugin files"
+    )
+
+    add_custom_command(
+        TARGET ${ProjectName}
+        POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${ProjectName}.lib ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+        COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=${ProjectName}
+        COMMENT "Generating JPL archive"
+    )
+endif()
diff --git a/AudioFilter/FilterAudioSubscriber.cpp b/AudioFilter/FilterAudioSubscriber.cpp
index e3d2825..02c2555 100644
--- a/AudioFilter/FilterAudioSubscriber.cpp
+++ b/AudioFilter/FilterAudioSubscriber.cpp
@@ -23,7 +23,6 @@
 extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
-#include <libavfilter/buffersrc.h>
 }
 #include <frameUtils.h>
 
@@ -42,6 +41,10 @@
 
 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());
@@ -66,7 +69,11 @@
             "[ 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=s16p:sample_rates="
+#else
             "[ reverb ] aformat=sample_fmts=s16:sample_rates="
+#endif
           + std::to_string(pSampleRate) + ":channel_layouts=stereo ";
 }
 
@@ -114,11 +121,7 @@
 void
 FilterAudioSubscriber::setIRAVFrame()
 {
-    int ret, got_frame;
     AVCodecContext* pCodecCtx;
-    AVPacket* packet;
-
-    FILE* pFile = fopen(irFile_.c_str(), "rb");
 
     const AVCodec* pCodec = avcodec_find_decoder(pFormatCtx_->streams[audioStream_]->codecpar->codec_id);
     if (pCodec == NULL) {
@@ -127,63 +130,46 @@
     }
 
     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;
     }
 
-    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;
+    AVPacket* packet = av_packet_alloc();
     AVFrame* pFrame = av_frame_alloc();
 
-    if (avcodec_send_packet(pCodecCtx, packet) < 0) {
-        avcodec_close(pCodecCtx);
+    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);
-        av_packet_free(&packet);
-        avformat_close_input(&pFormatCtx_);
-        avformat_free_context(pFormatCtx_);
-        Plog::log(Plog::LogPriority::INFO, TAG, "Error submitting the packet to the decoder");
-        return;
-    }
+        pFrame = av_frame_alloc();
 
-    while (packet->size > 0 && idx < frames) {
-        idx++;
-        got_frame = 0;
-
-        if (avcodec_receive_frame(pCodecCtx, pFrame) < 0) {
+        if (avcodec_send_packet(pCodecCtx, packet) < 0) {
+            Plog::log(Plog::LogPriority::INFO, __FILE__, "Error submitting the packet to the decoder");
             break;
         }
 
-        auto len = av_get_bytes_per_sample(pCodecCtx->sample_fmt);
-
-        reverbFilter_.feedInput(pFrame, "ir0");
-        packet->size -= len;
-        packet->data += len;
+        // Read frames from decoder
+        while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
+            reverbFilter_.feedInput(pFrame, "ir0");
+        }
+        av_packet_unref(packet);
     }
-    av_frame_unref(pFrame);
 
     reverbFilter_.feedEOF("ir0");
 
-    fclose(pFile);
-    avcodec_close(pCodecCtx);
+    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_);
 }
@@ -210,12 +196,13 @@
     if (!reverbFilter_.initialized_)
         return;
 
-    AVFrame* filteredFrame;
     if (reverbFilter_.feedInput(pluginFrame, "input") == 0) {
-        if ((filteredFrame = reverbFilter_.readOutput()))
+        AVFrame* filteredFrame = reverbFilter_.readOutput();
+        if (filteredFrame) {
             moveFrom(pluginFrame, filteredFrame);
-        av_frame_unref(filteredFrame);
-        av_frame_free(&filteredFrame);
+            av_frame_unref(filteredFrame);
+            av_frame_free(&filteredFrame);
+        }
     }
 }
 
diff --git a/AudioFilter/FilterAudioSubscriber.h b/AudioFilter/FilterAudioSubscriber.h
index 9c3912d..b5ce828 100644
--- a/AudioFilter/FilterAudioSubscriber.h
+++ b/AudioFilter/FilterAudioSubscriber.h
@@ -50,7 +50,7 @@
     // Data
     std::string path_;
     FrameFilter reverbFilter_;
-    AVFormatContext* pFormatCtx_;
+    AVFormatContext* pFormatCtx_ = NULL;
     int audioStream_ = -1;
 
     // Status variables of the processing
diff --git a/AudioFilter/FilterMediaHandler.cpp b/AudioFilter/FilterMediaHandler.cpp
index e04a603..02a0e06 100644
--- a/AudioFilter/FilterMediaHandler.cpp
+++ b/AudioFilter/FilterMediaHandler.cpp
@@ -38,13 +38,13 @@
     setId(datapath_);
     auto it = preferences_.find("irFile");
     if (it != preferences_.end())
-        mAS = std::make_shared<FilterAudioSubscriber>(datapath_, it->second);
+        mAS_ = std::make_shared<FilterAudioSubscriber>(datapath_, it->second);
 }
 
 void
 FilterMediaHandler::notifyAVFrameSubject(const StreamData& data, jami::avSubjectPtr subject)
 {
-    if (!mAS)
+    if (!mAS_)
         return;
     std::ostringstream oss;
     std::string_view direction = data.direction ? "Receive" : "Preview";
@@ -57,12 +57,12 @@
     oss << "preferredStreamDirection " << preferredStreamDirection << std::endl;
     if (data.type == StreamType::audio && !data.direction
         && data.direction == preferredStreamDirection) {
-        subject->attach(mAS.get()); // your image
+        subject->attach(mAS_.get()); // your image
         oss << "got my sent audio attached" << std::endl;
         attached_ = '1';
     } else if (data.type == StreamType::audio && data.direction
                && data.direction == preferredStreamDirection) {
-        subject->attach(mAS.get()); // the image you receive from others on the call
+        subject->attach(mAS_.get()); // the image you receive from others on the call
         oss << "got received audio attached" << std::endl;
         attached_ = '1';
     }
@@ -86,8 +86,8 @@
     auto it = preferences_.find(key);
     if (it != preferences_.end() && it->second != value) {
         it->second = value;
-        if (key == "irFile")
-            mAS->setIRFile(value);
+        if (key == "irFile" && mAS_)
+            mAS_->setIRFile(value);
     }
 }
 
@@ -103,7 +103,8 @@
 FilterMediaHandler::detach()
 {
     attached_ = '0';
-    mAS->detach();
+    if (mAS_)
+        mAS_->detach();
 }
 
 FilterMediaHandler::~FilterMediaHandler()
diff --git a/AudioFilter/FilterMediaHandler.h b/AudioFilter/FilterMediaHandler.h
index 01ed64e..c19b8b4 100644
--- a/AudioFilter/FilterMediaHandler.h
+++ b/AudioFilter/FilterMediaHandler.h
@@ -41,7 +41,7 @@
     virtual void setPreferenceAttribute(const std::string& key, const std::string& value) override;
     virtual bool preferenceMapHasKey(const std::string& key) override;
 
-    std::shared_ptr<FilterAudioSubscriber> mAS;
+    std::shared_ptr<FilterAudioSubscriber> mAS_{};
 
 private:
     const std::string datapath_;
diff --git a/AudioFilter/build.sh b/AudioFilter/build.sh
index 91ee4ae..36e6d30 100755
--- a/AudioFilter/build.sh
+++ b/AudioFilter/build.sh
@@ -6,9 +6,10 @@
 EXTRAPATH=''
 # Flags:
 
-# -p: number of processors to use
+# -p: number of processors to use.
 # -c: Runtime plugin cpu/gpu setting.
 # -t: target platform.
+# -d: debug program.
 
 
 if [ -z "${DAEMON}" ]; then
@@ -38,8 +39,12 @@
     echo "Building with ${PLATFORM}"
 fi
 
-while getopts t:c:p OPT; do
+while getopts t:c:p:d OPT; do
   case "$OPT" in
+    d)
+      DEBUG=true
+      export __DEBUG__=true
+    ;;
     t)
       PLATFORM="${OPTARG}"
     ;;
@@ -55,6 +60,7 @@
 done
 
 cp -r ffmpeg ${CONTRIB_PATH}/src/
+cp -r ../contrib/mp3lame ${CONTRIB_PATH}/src/
 
 if [ "${PLATFORM}" = "linux-gnu" ] || [ "${PLATFORM}" = "redhat-linux" ]
 then
@@ -67,21 +73,30 @@
     rm .ffmpeg
     cd ${WORKPATH}
 
-    python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
-
     CONTRIB_PLATFORM=${CONTRIB_PLATFORM_CURT}-${PLATFORM}
 
+    if [ ${DEBUG} ]; then
+      OUTPUT="${PLUGIN_NAME}"
+      CLANG_OPTS="-g -fsanitize=address"
+      EXTRA_DEBUG_LIBRARIES="-lyaml-cpp -lvdpau -lX11 -lva-drm -lva-x11 -lmp3lame"
+      EXTRA_DEFINES="-D__DEBUG__"
+    else
+      python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
+      CLANG_OPTS="-O3 -shared"
+      OUTPUT="build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+    fi
+
     # Compile
-    clang++ -std=c++17 -shared -fPIC \
+    clang++ -std=c++17 -fPIC ${CLANG_OPTS} \
     -Wl,-Bsymbolic,-rpath,"\${ORIGIN}" \
     -Wall -Wextra \
-    -Wno-unused-variable \
-    -Wno-unused-function \
     -Wno-unused-parameter \
+    ${EXTRA_DEFINES} \
     -I"." \
     -I"${DAEMON_SRC}" \
     -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/include" \
     -I"${PLUGINS_LIB}" \
+    ./../lib/common.cpp \
     ./../lib/frameFilter.cpp \
     ./../lib/frameUtils.cpp \
     FilterMediaHandler.cpp \
@@ -94,10 +109,14 @@
     -l:libavformat.a \
     -l:libavcodec.a \
     -l:libavutil.a \
-    -l:libvpx.a \
-    -l:libx264.a \
+    -lvpx \
+    -lx264 \
+    -lspeex \
+    -lopus \
+    -lz \
     -lva \
-    -o "build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+    ${EXTRA_DEBUG_LIBRARIES} \
+    -o "${OUTPUT}"
 
 elif [ "${PLATFORM}" = "darwin" ]
 then
@@ -110,24 +129,33 @@
     rm .ffmpeg
     cd ${WORKPATH}
 
-    python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
-  
     CONTRIB_PLATFORM=${CONTRIB_PLATFORM_CURT}-${PLATFORM}
 
+    if [ ${DEBUG} ]; then
+      OUTPUT="${PLUGIN_NAME}"
+      CLANG_OPTS="-g -fsanitize=address"
+      EXTRA_DEBUG_LIBRARIES="-lyaml-cpp -lmp3lame"
+      EXTRA_DEFINES="-D__DEBUG__"
+    else
+      python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
+      CLANG_OPTS="-O3 -shared"
+      OUTPUT="build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+    fi
+
     # Compile
-    clang++ -std=c++17 -shared -fPIC \
+    clang++ -std=c++17 -fPIC ${CLANG_OPTS} \
     -Wl,-no_compact_unwind -Wl,-framework,CoreFoundation \
     -Wl,-framework,Security -Wl,-framework,VideoToolbox \
     -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo \
     -Wl,-rpath,"\${ORIGIN}" \
     -Wall -Wextra \
-    -Wno-unused-variable \
-    -Wno-unused-function \
     -Wno-unused-parameter \
+    ${EXTRA_DEFINES} \
     -I"." \
     -I"${DAEMON_SRC}" \
     -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}${CONTRIB_PLATFORM_EXTRA}/include" \
     -I"${PLUGINS_LIB}" \
+    ./../lib/common.cpp \
     ./../lib/frameFilter.cpp \
     ./../lib/frameUtils.cpp \
     FilterMediaHandler.cpp \
@@ -141,11 +169,16 @@
     -lavcodec \
     -lavutil \
     -lvpx -lx264 -lbz2 -liconv -lz \
-    "/usr/local/opt/speex/lib/libspeex.a" \
-    "/usr/local/opt/opus/lib/libopus.a" \
-    -o "build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+    -lspeex \
+    -lopus \
+    ${EXTRA_DEBUG_LIBRARIES} \
+    -o "${OUTPUT}"
 
-    install_name_tool -id "@loader_path/${SO_FILE_NAME}" "build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+    if [ ! ${DEBUG} ]; then
+        install_name_tool -id "@loader_path/${PLUGIN_NAME}" "${OUTPUT}"
+    else
+        install_name_tool -id "@loader_path/${SO_FILE_NAME}" "${OUTPUT}"
+    fi
 
     if [ -n "${APPLE_SIGN_CERTIFICATE}" ]; then
       codesign --force --verify --timestamp -o runtime --sign "${APPLE_SIGN_CERTIFICATE}"  "build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
@@ -308,7 +341,9 @@
     done
 fi
 
-python3 ./../SDK/jplManipulation.py --assemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM} --extraPath=${EXTRAPATH}
+if [ ! ${DEBUG} ]; then
+  python3 ./../SDK/jplManipulation.py --assemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM} --extraPath=${EXTRAPATH}
+fi
 cd ${CONTRIB_PATH}/src/ffmpeg/
 # ffmpeg build configuration files were changed during plugin build
 # this git checkout will remove these changes
diff --git a/AudioFilter/ffmpeg/package.json b/AudioFilter/ffmpeg/package.json
index 69c2a52..ef1d754 100644
--- a/AudioFilter/ffmpeg/package.json
+++ b/AudioFilter/ffmpeg/package.json
@@ -3,6 +3,7 @@
     "version": "n5.0",
     "url": "https://git.ffmpeg.org/gitweb/ffmpeg.git/snapshot/__VERSION__.tar.gz",
     "deps": [
+        "mp3lame",
         "vpx",
         "x264",
         "opus",
@@ -16,7 +17,8 @@
         "libopusdec-enable-FEC.patch",
         "windows-configure.patch",
         "windows-configure-ffnvcodec.patch",
-        "windows-configure-libmfx.patch"
+        "windows-configure-libmfx.patch",
+        "windows-configure-mp3lame.patch"
     ],
     "win_patches": [
     ],
diff --git a/AudioFilter/ffmpeg/rules.mak b/AudioFilter/ffmpeg/rules.mak
index a2bf7d9..eda60ec 100644
--- a/AudioFilter/ffmpeg/rules.mak
+++ b/AudioFilter/ffmpeg/rules.mak
@@ -3,7 +3,11 @@
 
 PKGS+=ffmpeg
 
+ifdef __DEBUG__
+DEPS_ffmpeg = iconv zlib vpx opus speex x264 mp3lame
+else
 DEPS_ffmpeg = iconv zlib vpx opus speex x264
+endif
 
 FFMPEGCONF = \
 	--cc="$(CC)" \
@@ -110,6 +114,13 @@
 	--enable-encoder=libopus \
 	--enable-decoder=libopus
 
+ifdef __DEBUG__
+FFMPEGCONF += \
+	--enable-libmp3lame \
+	--enable-encoder=libmp3lame \
+	--enable-muxer=mp3
+endif
+
 # decoders for ringtones and audio streaming
 FFMPEGCONF += \
 	--enable-decoder=flac \
@@ -184,6 +195,7 @@
 	--target-os=android \
 	--enable-jni \
 	--enable-mediacodec \
+	--disable-vulkan \
 	--enable-decoder=vp8_mediacodec \
 	--enable-decoder=h264_mediacodec \
 	--enable-decoder=mpeg4_mediacodec \
diff --git a/AudioFilter/ffmpeg/windows-configure-make.sh b/AudioFilter/ffmpeg/windows-configure-make.sh
index 8fa4935..cf4fa01 100644
--- a/AudioFilter/ffmpeg/windows-configure-make.sh
+++ b/AudioFilter/ffmpeg/windows-configure-make.sh
@@ -63,6 +63,11 @@
             --enable-decoder=mjpeg

             --enable-decoder=mjpegb'

 

+FFMPEGCONF+='

+            --enable-libmp3lame

+            --enable-encoder=libmp3lame

+            --enable-muxer=mp3'

+

 # decoders for ringtones and audio streaming

 FFMPEGCONF+='

             --enable-decoder=flac

@@ -144,7 +149,7 @@
             OUTDIR=Output/Windows10/x86

     fi

 elif [ "$1" == "win32" ]; then

-    EXTRACFLAGS='-MD -D_WINDLL -I../../../../../msvc/include -I../../../../../msvc/include/opus -I../../../../../msvc/include/vpx -I../../../../../msvc/include/ffnvcodec -I../../../../../msvc/include/mfx' 

+    EXTRACFLAGS='-MD -D_WINDLL -I../../../../../msvc/include -I../../../../../msvc/include/opus -I../../../../../msvc/include/vpx -I../../../../../msvc/include/ffnvcodec -I../../../../../msvc/include/mfx -I../../../../../msvc/include/lame'

     FFMPEGCONF+='

                 --enable-libvpx

                 --enable-encoder=libvpx_vp8

@@ -179,13 +184,13 @@
                 --enable-filter=overlay_qsv'

     if [ "$2" == "x64" ]; then

         echo "configure and make ffmpeg for win32-x64..."

-        EXTRALDFLAGS='-APPCONTAINER:NO -MACHINE:x64 Ole32.lib Kernel32.lib Gdi32.lib User32.lib Strmiids.lib Advapi32.lib OleAut32.lib Shlwapi.lib Vfw32.lib Secur32.lib Advapi32.lib libopus.lib libx264.lib libvpx.lib libmfx.lib -LIBPATH:../../../../../msvc/lib/x64'

+        EXTRALDFLAGS='-APPCONTAINER:NO -MACHINE:x64 Ole32.lib Kernel32.lib Gdi32.lib User32.lib Strmiids.lib Advapi32.lib OleAut32.lib Shlwapi.lib Vfw32.lib Secur32.lib Advapi32.lib libopus.lib libx264.lib libvpx.lib libmfx.lib libmp3lame.lib -LIBPATH:../../../../../msvc/lib/x64'

         FFMPEGCONF+=' --arch=x86_64'

         PREFIX=../../../Build/win32/x64

         OUTDIR=Output/win32/x64

     elif [ "$2" == "x86" ]; then

         echo "configure and make ffmpeg for win32-x86..."

-        EXTRALDFLAGS='-APPCONTAINER:NO -MACHINE:x86 Ole32.lib Kernel32.lib Gdi32.lib User32.lib Strmiids.lib OleAut32.lib Shlwapi.lib Vfw32.lib Secur32.lib Advapi32.lib libopus.lib libx264.lib libvpx.lib libmfx.lib -LIBPATH:../../../../../msvc/lib/x86'

+        EXTRALDFLAGS='-APPCONTAINER:NO -MACHINE:x86 Ole32.lib Kernel32.lib Gdi32.lib User32.lib Strmiids.lib OleAut32.lib Shlwapi.lib Vfw32.lib Secur32.lib Advapi32.lib libopus.lib libx264.lib libvpx.lib libmfx.lib libmp3lame.lib -LIBPATH:../../../../../msvc/lib/x86'

         FFMPEGCONF+=' --arch=x86'

         PREFIX=../../../Build/win32/x86

         OUTDIR=Output/win32/x86

diff --git a/AudioFilter/ffmpeg/windows-configure-mp3lame.patch b/AudioFilter/ffmpeg/windows-configure-mp3lame.patch
new file mode 100644
index 0000000..76e6b74
--- /dev/null
+++ b/AudioFilter/ffmpeg/windows-configure-mp3lame.patch
@@ -0,0 +1,24 @@
+From ccf53cdcdbb11a57d5874ba803538c3cc98031bd Mon Sep 17 00:00:00 2001
+From: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+Date: Fri, 26 Aug 2022 17:01:30 -0300
+Subject: [PATCH] windows-configure-mp3lame
+
+---
+ configure | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/configure b/configure
+index 70e909c603..eebb3dd510 100755
+--- a/configure
++++ b/configure
+@@ -6549,7 +6549,6 @@ enabled liblensfun        && require_pkg_config liblensfun lensfun lensfun.h lf_
+ # can find the libraries and headers through other means.
+ 
+ enabled libmodplug        && require_pkg_config libmodplug libmodplug libmodplug/modplug.h ModPlug_Load
+-enabled libmp3lame        && require "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame $libm_extralibs
+ enabled libmysofa         && { check_pkg_config libmysofa libmysofa mysofa.h mysofa_neighborhood_init_withstepdefine ||
+                                require libmysofa mysofa.h mysofa_neighborhood_init_withstepdefine -lmysofa $zlib_extralibs; }
+ enabled libnpp            && { check_lib libnpp npp.h nppGetLibVersion -lnppig -lnppicc -lnppc -lnppidei -lnppif ||
+-- 
+2.30.2.windows.1
+
diff --git a/AudioFilter/main.cpp b/AudioFilter/main.cpp
index 4b30b2d..1a28e49 100644
--- a/AudioFilter/main.cpp
+++ b/AudioFilter/main.cpp
@@ -26,14 +26,21 @@
 
 #include "FilterMediaHandler.h"
 
+#ifdef __DEBUG__
+#include <common.h>
+#include <assert.h>
+#include <yaml-cpp/yaml.h>
+#include <AVFrameIO.h>
+#endif
+
 #ifdef WIN32
 #define EXPORT_PLUGIN __declspec(dllexport)
 #else
 #define EXPORT_PLUGIN
 #endif
 
-#define AudioFilter_VERSION_MAJOR 0
-#define AudioFilter_VERSION_MINOR 1
+#define AudioFilter_VERSION_MAJOR 1
+#define AudioFilter_VERSION_MINOR 0
 #define AudioFilter_VERSION_PATCH 0
 
 extern "C" {
@@ -68,3 +75,54 @@
     return pluginExit;
 }
 }
+
+#ifdef __DEBUG__
+
+int
+main ()
+{
+    std::cout << "*********************************" << std::endl;
+    std::cout << "**  AudioFilter Debug Version  **" << std::endl;
+    std::cout << "*********************************" << std::endl;
+    std::cout << "Version " << AudioFilter_VERSION_MAJOR << "." << AudioFilter_VERSION_MINOR << "."
+              << AudioFilter_VERSION_PATCH << std::endl << std::endl;
+
+    std::ifstream file;
+    file_utils::openStream(file, "testPreferences.yml");
+
+    assert(file.is_open());
+    YAML::Node node = YAML::Load(file);
+
+    assert(node.IsMap());
+    std::map<std::string, std::string> preferences;
+    for (const auto& kv : node) {
+        preferences[kv.first.as<std::string>()] = kv.second.as<std::string>();
+        std::cout << "Key: " << kv.first.as<std::string>() << "; Value: " << kv.second.as<std::string>() << std::endl;
+    }
+
+#ifdef _WIN32
+    std::string dataPath = "../data";
+#else
+    std::string dataPath = "data";
+#endif
+
+    auto fmpFilterMediaHandler = std::make_unique<jami::FilterMediaHandler>(std::move(
+                                                                                preferences),
+                                                                            std::move(dataPath));
+
+    auto subject = std::make_shared<jami::PublishObservable<AVFrame*>>();
+
+    // Valid Read frames from sample file and send to subscriber
+    std::cout << "Test 1" << std::endl << "Received audio: " << preferences["sample"] << std::endl;
+    fmpFilterMediaHandler->notifyAVFrameSubject(StreamData("testCall", true, StreamType::audio, "origin", "destiny"), subject);
+    av_utils::readAndNotifyAVFrame(preferences["sample"], subject.get(), preferences["output1"], AVMEDIA_TYPE_AUDIO);
+
+    // Valid Read frames from sample file and send to subscriber
+    std::cout << "Test 2" << std::endl << "Sent audio: " << preferences["sample"] << std::endl;
+    fmpFilterMediaHandler->detach();
+    fmpFilterMediaHandler->notifyAVFrameSubject(StreamData("testCall", false, StreamType::audio, "origin", "destiny"), subject);
+    av_utils::readAndNotifyAVFrame(preferences["sample"], subject.get(), preferences["output2"], AVMEDIA_TYPE_AUDIO);
+
+    return 0;
+}
+#endif
diff --git a/AudioFilter/manifest.json b/AudioFilter/manifest.json
index 7de0b23..bc5b9fe 100644
--- a/AudioFilter/manifest.json
+++ b/AudioFilter/manifest.json
@@ -1,6 +1,6 @@
 {
     "name": "AudioFilter",
     "description": "Provides audio filter for audio and video calls: reverb",
-    "version": "0.1.0",
+    "version": "1.0.0",
     "iconPath" : "icon.svg"
 }
\ No newline at end of file
diff --git a/AudioFilter/package.json b/AudioFilter/package.json
index 1206532..8d6d69e 100644
--- a/AudioFilter/package.json
+++ b/AudioFilter/package.json
@@ -1,12 +1,14 @@
 {
     "name": "AudioFilter",
-    "version": "0.1.0",
+    "version": "1.0.0",
     "extractLibs": false,
     "deps": [
         "fmt",
-        "ffmpeg"
+        "yaml-cpp"
     ],
-    "defines": [],
+    "defines": [
+        "TESTPROCESS=False"
+    ],
     "custom_scripts": {
         "pre_build": [
             "mkdir msvc"
diff --git a/AudioFilter/sample.mp3 b/AudioFilter/sample.mp3
new file mode 100644
index 0000000..e68cb46
--- /dev/null
+++ b/AudioFilter/sample.mp3
Binary files differ
diff --git a/AudioFilter/testPreferences.yml b/AudioFilter/testPreferences.yml
new file mode 100644
index 0000000..30e970c
--- /dev/null
+++ b/AudioFilter/testPreferences.yml
@@ -0,0 +1,5 @@
+irFile: "rir_jack_lyons_lp2_96k.mp3"
+streamlist: "in"
+sample: "sample.mp3"
+output1: "processed.mp3"
+output2: "notprocessed.mp3"
\ No newline at end of file
diff --git a/contrib/mp3lame/package.json b/contrib/mp3lame/package.json
new file mode 100644
index 0000000..f0a572b
--- /dev/null
+++ b/contrib/mp3lame/package.json
@@ -0,0 +1,15 @@
+{
+    "name": "mp3lame",
+    "version": "b8d31e1d07b851208ad413ee82deca875d8a1c80",
+    "url": "https://github.com/ShiftMediaProject/lame/archive/__VERSION__.tar.gz",
+    "deps": [],
+    "patches": [],
+    "win_patches": [],
+    "project_paths": ["SMP/libmp3lame.vcxproj"],
+    "with_env" : "",
+    "custom_scripts": {
+        "pre_build": [],
+        "build": [],
+        "post_build": []
+    }
+}
\ No newline at end of file
diff --git a/contrib/mp3lame/rules.mak b/contrib/mp3lame/rules.mak
new file mode 100644
index 0000000..418e1cd
--- /dev/null
+++ b/contrib/mp3lame/rules.mak
@@ -0,0 +1,25 @@
+# lame
+LAME_HASH := f416c19b3140a8610507ebb60ac7cd06e94472b8
+LAME_GITURL := https://github.com/gypified/libmp3lame.git
+
+LAMECONFIG := --prefix="$(PREFIX)"
+
+$(TARBALLS)/mp3lame-$(LAME_HASH).tar.xz:
+	$(call download_git,$(LAME_GITURL),master,$(LAME_HASH))
+
+.sum-mp3lame: mp3lame-$(LAME_HASH).tar.xz
+	$(warning $@ not implemented)
+	touch $@
+
+mp3lame: mp3lame-$(LAME_HASH).tar.xz .sum-mp3lame
+	rm -Rf $@-$(LAME_HASH)
+	mkdir -p $@-$(LAME_HASH)
+	(cd $@-$(LAME_HASH) && tar x $(if ${BATCH_MODE},,-v) --strip-components=1 -f $<)
+	$(UPDATE_AUTOCONFIG)
+	$(MOVE)
+
+.mp3lame: mp3lame
+	cd $< && $(HOSTVARS) ./configure $(LAMECONFIG)
+	cd $< && $(MAKE)
+	cd $< && $(MAKE) install
+	touch $@
diff --git a/lib/AVFrameIO.h b/lib/AVFrameIO.h
new file mode 100644
index 0000000..867ffa8
--- /dev/null
+++ b/lib/AVFrameIO.h
@@ -0,0 +1,240 @@
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavfilter/buffersrc.h>
+#include <libavutil/channel_layout.h>
+#include <libavutil/common.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+#include <libavutil/rational.h>
+}
+
+#include "pluglog.h"
+
+#include <observer.h>
+
+namespace av_utils
+{
+
+const AVCodec*
+openDecoder(AVCodecContext*& decCodecCtx,
+            AVFormatContext*& decFormatCtx,
+            const std::string& file,
+            AVStream*& decStream,
+            const AVMediaType& mediaType)
+{
+    // Open
+    if (avformat_open_input(&decFormatCtx, file.c_str(), NULL, NULL) != 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Couldn't open input stream.");
+        return nullptr;
+    }
+    // Retrieve stream information
+    if (avformat_find_stream_info(decFormatCtx, NULL) < 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Couldn't find stream information.");
+        return nullptr;
+    }
+    // Dump valid information onto standard error
+    av_dump_format(decFormatCtx, 0, file.c_str(), false);
+
+    // Find the first audio/video stream
+    const AVCodec* decCodec;
+    auto stream = av_find_best_stream(decFormatCtx, mediaType, -1, -1, &decCodec, 0);
+
+    if (stream < 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Didn't find an audio stream.");
+        return nullptr;
+    }
+    decStream = decFormatCtx->streams[stream];
+
+    if (decCodec == NULL) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Decoder not found.");
+        return nullptr;
+    }
+
+    decCodecCtx = avcodec_alloc_context3(decCodec);
+    if (avcodec_parameters_to_context(decCodecCtx, decStream->codecpar) < 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Failed to copy decoder parameters to decoder context.");
+        return nullptr;
+    }
+
+    if (decCodecCtx->codec_type == AVMEDIA_TYPE_VIDEO)
+        decCodecCtx->framerate = av_guess_frame_rate(decFormatCtx, decStream, NULL);
+
+    // Open codec
+    if (avcodec_open2(decCodecCtx, decCodec, NULL) < 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Could not open codec.");
+        return nullptr;
+    }
+    return decCodec;
+}
+
+const AVCodec*
+openEncoder(AVCodecContext*& encCodecCtx,
+            AVCodecContext*& decCodecCtx,
+            AVFormatContext*& encFormatCtx,
+            AVStream*& encStream,
+            const std::string& rFile,
+            AVStream*& decStream,
+            const AVMediaType& mediaType)
+{
+    avformat_alloc_output_context2(&encFormatCtx, NULL, NULL, rFile.c_str());
+    if (!encFormatCtx) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Could not create output context.");
+        return nullptr;
+    }
+
+    encStream = avformat_new_stream(encFormatCtx, NULL);
+    if (!encStream) {
+        return nullptr;
+    }
+
+    const AVCodec* encCodec = avcodec_find_encoder(decStream->codecpar->codec_id);
+    if (encCodec == NULL) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Encoder not found.");
+        return nullptr;
+    }
+    encCodecCtx = avcodec_alloc_context3(encCodec);
+    if (encCodecCtx == NULL) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Could not allocate audio codec context.");
+        return nullptr;
+    }
+
+    if (mediaType == AVMEDIA_TYPE_AUDIO) {
+        encCodecCtx->sample_rate    = decCodecCtx->sample_rate;
+        encCodecCtx->channel_layout = decCodecCtx->channel_layout;
+        encCodecCtx->channels       = decCodecCtx->channels;
+        encCodecCtx->channels = av_get_channel_layout_nb_channels(encCodecCtx->channel_layout);
+        encCodecCtx->sample_fmt = decCodecCtx->sample_fmt;
+        encCodecCtx->time_base = AVRational{1, encCodecCtx->sample_rate};
+    } else if (mediaType == AVMEDIA_TYPE_VIDEO) {
+        encCodecCtx->height = decCodecCtx->height;
+        encCodecCtx->width = decCodecCtx->width;
+        encCodecCtx->sample_aspect_ratio = decCodecCtx->sample_aspect_ratio;
+        encCodecCtx->pix_fmt = decCodecCtx->pix_fmt;
+        encCodecCtx->time_base = decStream->time_base;
+        encCodecCtx->bit_rate = decCodecCtx->bit_rate;
+    } else {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Unsupported media type");
+        return nullptr;
+    }
+
+    if (avcodec_open2(encCodecCtx, encCodec, NULL) < 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Could not open codec.");
+        return nullptr;
+    }
+    if (avcodec_parameters_from_context(encStream->codecpar, encCodecCtx) < 0) {
+        return nullptr;
+    }
+    encStream->time_base = encCodecCtx->time_base;
+
+    av_dump_format(encFormatCtx, 0, rFile.c_str(), 1);
+
+    if (!(encFormatCtx->oformat->flags & AVFMT_NOFILE)) {
+        if (avio_open(&encFormatCtx->pb, rFile.c_str(), AVIO_FLAG_WRITE) < 0) {
+            Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Could not open output file.");
+            return nullptr;
+        }
+    }
+
+    if (avformat_write_header(encFormatCtx, NULL) < 0) {
+        Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Could not open output file.");
+        return nullptr;
+    }
+    return encCodec;
+}
+
+void
+readAndNotifyAVFrame(const std::string& file, jami::PublishObservable<AVFrame*>* subject, const std::string& rFile, const AVMediaType mediaType)
+{
+    // Open Decoder
+    AVCodecContext* decCodecCtx;
+    AVFormatContext* decFormatCtx = avformat_alloc_context();
+    AVStream* decStream;
+    const AVCodec* decCodec = openDecoder(decCodecCtx, decFormatCtx, file, decStream, mediaType);
+    if (!decCodec) {
+        avcodec_close(decCodecCtx);
+        avcodec_free_context(&decCodecCtx);
+        avformat_close_input(&decFormatCtx);
+        avformat_free_context(decFormatCtx);
+        return;
+    }
+
+    // Open Encoder
+    AVCodecContext* encCodecCtx;
+    AVFormatContext* encFormatCtx;
+    AVStream* encStream;
+    const AVCodec* encCodec = openEncoder(encCodecCtx, decCodecCtx, encFormatCtx, encStream, rFile, decStream, mediaType);
+    if (!encCodec) {
+        avcodec_close(decCodecCtx);
+        avcodec_free_context(&decCodecCtx);
+        avformat_close_input(&decFormatCtx);
+        avformat_free_context(decFormatCtx);
+
+        avio_closep(&encFormatCtx->pb);
+        avcodec_close(encCodecCtx);
+        avcodec_free_context(&encCodecCtx);
+        avformat_close_input(&encFormatCtx);
+        avformat_free_context(encFormatCtx);
+        return;
+    }
+
+    AVPacket* packet = av_packet_alloc();
+    AVFrame* pFrame = av_frame_alloc();
+
+    while (av_read_frame(decFormatCtx, packet) == 0) {
+        av_frame_unref(pFrame);
+        av_frame_free(&pFrame);
+        pFrame = av_frame_alloc();
+        if (packet->stream_index == decStream->index) {
+            if (avcodec_send_packet(decCodecCtx, packet) < 0) {
+                Plog::log(Plog::LogPriority::INFO, __FILE__ + std::string(":") + std::to_string(__LINE__), "Error submitting the packet to the decoder");
+                break;
+            }
+
+            // Read frames from decoder
+            while (avcodec_receive_frame(decCodecCtx, pFrame) == 0) {
+                // Publish frames for the plugin subscriber
+                subject->publish(pFrame);
+
+                // Send frame to encoder
+                if (avcodec_send_frame(encCodecCtx, pFrame) < 0) {
+                    break;
+                }
+
+                // Read packet from encoder
+                AVPacket *enc_pkt = av_packet_alloc();
+                while (avcodec_receive_packet(encCodecCtx, enc_pkt) == 0) {
+                    enc_pkt->stream_index = 0; // 0 -> we only created one stream in the encoder
+                    av_packet_rescale_ts(enc_pkt,
+                                        encCodecCtx->time_base,
+                                        encFormatCtx->streams[0]->time_base);
+
+                    av_interleaved_write_frame(encFormatCtx, enc_pkt);
+                }
+                av_packet_unref(enc_pkt);
+                av_packet_free(&enc_pkt);
+            }
+        }
+        av_packet_unref(packet);
+    }
+
+    av_write_trailer(encFormatCtx);
+
+    av_frame_unref(pFrame);
+    av_frame_free(&pFrame);
+    av_packet_unref(packet);
+    av_packet_free(&packet);
+
+    avcodec_close(decCodecCtx);
+    avcodec_free_context(&decCodecCtx);
+    avformat_close_input(&decFormatCtx);
+    avformat_free_context(decFormatCtx);
+
+    avio_closep(&encFormatCtx->pb);
+    avcodec_close(encCodecCtx);
+    avcodec_free_context(&encCodecCtx);
+    avformat_close_input(&encFormatCtx);
+    avformat_free_context(encFormatCtx);
+}
+} //av_utils
diff --git a/lib/common.cpp b/lib/common.cpp
index 2047278..4803f1c 100644
--- a/lib/common.cpp
+++ b/lib/common.cpp
@@ -41,3 +41,16 @@
 }
 } // namespace string_utils
 #endif // WIN32
+
+namespace file_utils {
+
+void
+openStream(std::ifstream& file, const std::string& path)
+{
+#ifdef _WIN32
+    file = std::ifstream(string_utils::to_wstring(path));
+#else
+    file = std::ifstream(path);
+#endif
+}
+} // namespace file_utils
diff --git a/lib/common.h b/lib/common.h
index f0543d0..5e25091 100644
--- a/lib/common.h
+++ b/lib/common.h
@@ -21,6 +21,8 @@
 #ifndef COMMON_H
 #define COMMON_H
 
+#include <fstream>
+
 #ifdef WIN32
 
 #include <string>
@@ -29,4 +31,8 @@
 std::wstring to_wstring(const std::string& str);
 } // namespace string_utils
 #endif // WIN32
+
+namespace file_utils {
+void openStream(std::ifstream& file, const std::string& path);
+}
 #endif // COMMON_H
diff --git a/lib/frameFilter.cpp b/lib/frameFilter.cpp
index 1cba04d..dc571d4 100644
--- a/lib/frameFilter.cpp
+++ b/lib/frameFilter.cpp
@@ -216,6 +216,8 @@
 
     auto type = av_buffersink_get_type(output_);
     if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
+        av_frame_unref(frame);
+        av_frame_free(&frame);
         return nullptr;
     }
     auto err = av_buffersink_get_frame(output_, frame);
@@ -228,6 +230,8 @@
     } else {
         fail("Error occurred while pulling from filter graph", err);
     }
+    av_frame_unref(frame);
+    av_frame_free(&frame);
     return nullptr;
 }