diff --git a/src/media/media_filter.cpp b/src/media/media_filter.cpp
index cd08b275a6b62370ef04acd9abd05ce8e670612d..fb0744f8d0feaf2a6801342653e3ed1a00dc888d 100644
--- a/src/media/media_filter.cpp
+++ b/src/media/media_filter.cpp
@@ -155,19 +155,27 @@ MediaFilter::getOutputParams() const
 }
 
 int
-MediaFilter::feedInput(AVFrame* frame, std::string inputName)
+MediaFilter::feedInput(AVFrame* frame, const std::string& inputName)
 {
     int ret = 0;
     if (!initialized_)
         return fail("Filter not initialized", -1);
 
     for (size_t i = 0; i < inputs_.size(); ++i) {
-        auto filterCtx = inputs_[i];
-        if (inputParams_[i].name != inputName)
+        auto& ms = inputParams_[i];
+        if (ms.name != inputName)
             continue;
 
+        if (ms.format != frame->format
+            || (ms.isVideo && (ms.width != frame->width || ms.height != frame->height))
+            || (!ms.isVideo && (ms.sampleRate != frame->sample_rate || ms.nbChannels != frame->channels))) {
+            ms.update(frame);
+            if ((ret = reinitialize()) < 0)
+                return fail("Failed to reinitialize filter with new input parameters", ret);
+        }
+
         int flags = AV_BUFFERSRC_FLAG_PUSH | AV_BUFFERSRC_FLAG_KEEP_REF;
-        if ((ret = av_buffersrc_add_frame_flags(filterCtx, frame, flags)) < 0)
+        if ((ret = av_buffersrc_add_frame_flags(inputs_[i], frame, flags)) < 0)
             return fail("Could not pass frame to filters", ret);
         else
             return 0;
@@ -281,6 +289,19 @@ MediaFilter::initInputFilter(AVFilterInOut* in, MediaStream msp)
     return ret;
 }
 
+int
+MediaFilter::reinitialize()
+{
+    // keep parameters needed for initialization before clearing filter
+    auto params = std::move(inputParams_);
+    auto desc = std::move(desc_);
+    clean();
+    auto ret = initialize(desc, params);
+    if (ret >= 0)
+        RING_DBG() << "Filter graph reinitialized";
+    return ret;
+}
+
 int
 MediaFilter::fail(std::string msg, int err) const
 {
@@ -292,8 +313,12 @@ MediaFilter::fail(std::string msg, int err) const
 void
 MediaFilter::clean()
 {
-    avfilter_graph_free(&graph_);
     initialized_ = false;
+    avfilter_graph_free(&graph_); // frees inputs_ and output_
+    desc_.clear();
+    inputs_.clear(); // don't point to freed memory
+    output_ = nullptr; // don't point to freed memory
+    inputParams_.clear();
 }
 
 } // namespace ring
diff --git a/src/media/media_filter.h b/src/media/media_filter.h
index 3868183362b192953408fccd30c6d7aa702281d5..3b05a13db2cc0abe65f1bb91b70532f3465fb03f 100644
--- a/src/media/media_filter.h
+++ b/src/media/media_filter.h
@@ -88,7 +88,7 @@ class MediaFilter {
          *
          * NOTE Will fail if @inputName is not found in the graph.
          */
-        int feedInput(AVFrame* frame, std::string inputName);
+        int feedInput(AVFrame* frame, const std::string& inputName);
 
         /**
          * Pull a frame from the filter graph. Caller owns the frame reference.
@@ -112,6 +112,11 @@ class MediaFilter {
          */
         int initInputFilter(AVFilterInOut* in, MediaStream msp);
 
+        /**
+         * Reinitializes the filter graph with @inputParams_, which should be updated beforehand.
+         */
+        int reinitialize();
+
         /**
          * Convenience method that prints @msg and returns err.
          *
diff --git a/test/unitTest/media/test_media_filter.cpp b/test/unitTest/media/test_media_filter.cpp
index ad3f3e0eb1ca83cc51d4c4b1485d5b2c518cbc5f..e904545888b988e72fc62bbf7335419ab75cd22e 100644
--- a/test/unitTest/media/test_media_filter.cpp
+++ b/test/unitTest/media/test_media_filter.cpp
@@ -41,11 +41,13 @@ private:
     void testAudioFilter();
     void testVideoFilter();
     void testFilterParams();
+    void testReinit();
 
     CPPUNIT_TEST_SUITE(MediaFilterTest);
     CPPUNIT_TEST(testAudioFilter);
     CPPUNIT_TEST(testVideoFilter);
     CPPUNIT_TEST(testFilterParams);
+    CPPUNIT_TEST(testReinit);
     CPPUNIT_TEST_SUITE_END();
 
     std::unique_ptr<MediaFilter> filter_;
@@ -240,6 +242,36 @@ MediaFilterTest::testFilterParams()
     CPPUNIT_ASSERT(ms.format >= 0 && ms.width == width1 && ms.height == height1);
 }
 
+void
+MediaFilterTest::testReinit()
+{
+    std::string filterSpec = "[in1] aresample=48000";
+
+    // prepare audio frame
+    frame_ = av_frame_alloc();
+    frame_->format = AV_SAMPLE_FMT_S16;
+    frame_->channel_layout = AV_CH_LAYOUT_STEREO;
+    frame_->nb_samples = 100;
+    frame_->sample_rate = 44100;
+    frame_->channels = 2;
+
+    // construct the filter parameters with different sample rate
+    auto params = MediaStream("in1", frame_->format, rational<int>(1, 16000), 16000, frame_->channels);
+
+    // allocate and fill frame buffers
+    CPPUNIT_ASSERT(av_frame_get_buffer(frame_, 0) >= 0);
+    fill_samples(reinterpret_cast<uint16_t*>(frame_->data[0]), frame_->sample_rate, frame_->nb_samples, frame_->channels, 440.0);
+
+    // prepare filter
+    std::vector<MediaStream> vec;
+    vec.push_back(params);
+    CPPUNIT_ASSERT(filter_->initialize(filterSpec, vec) >= 0);
+
+    // filter should reinitialize on feedInput
+    CPPUNIT_ASSERT(filter_->feedInput(frame_, "in1") >= 0);
+    av_frame_free(&frame_);
+}
+
 }} // namespace ring::test
 
 RING_TEST_RUNNER(ring::test::MediaFilterTest::name());