Commit 98518e99 authored by Philippe Gorley's avatar Philippe Gorley Committed by Adrien Béraud

filter: add automatic reinitialization

If one of the inputs has changed, the filter will reinitialize itself.

Change-Id: I884195b6fdf19ad72329364eeeefd33e3b3bbfff
parent 2c64e35a
......@@ -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
......@@ -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.
*
......
......@@ -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());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment