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());