Skip to content

Commit bec1155

Browse files
author
Clement Champetier
committed
FilterGraph: used Filter class to manage a graph of video/audio filters
1 parent d441d25 commit bec1155

File tree

2 files changed

+200
-41
lines changed

2 files changed

+200
-41
lines changed
Lines changed: 142 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,184 @@
11
#include "FilterGraph.hpp"
22

3+
#include <AvTranscoder/util.hpp>
4+
#include <AvTranscoder/data/decoded/AudioFrame.hpp>
5+
#include <AvTranscoder/data/decoded/VideoFrame.hpp>
6+
37
extern "C" {
48
#include <libavfilter/avfilter.h>
9+
#include <libavfilter/buffersrc.h>
10+
#include <libavfilter/buffersink.h>
511
}
612

713
#include <stdexcept>
14+
#include <sstream>
815

916
namespace avtranscoder
1017
{
1118

12-
FilterGraph::FilterGraph()
19+
FilterGraph::FilterGraph(const ICodec& codec)
1320
: _graph(avfilter_graph_alloc())
1421
, _filters()
22+
, _codec(codec)
23+
, _isInit(false)
1524
{
1625
if(!_graph)
1726
throw std::runtime_error("Unable to create filter graph: out of memory.");
1827
}
1928

2029
FilterGraph::~FilterGraph()
2130
{
22-
for(std::vector<Filter>::iterator it = _filters.begin(); it < _filters.end(); ++it)
31+
for(std::vector<Filter*>::iterator it = _filters.begin(); it < _filters.end(); ++it)
2332
{
24-
avfilter_free(it->second);
33+
it = _filters.erase(it);
2534
}
2635
avfilter_graph_free(&_graph);
2736
}
2837

29-
void FilterGraph::addFilter(const std::string& filtername)
38+
void FilterGraph::process(Frame& frame)
3039
{
31-
AVFilter* filter = avfilter_get_by_name(filtername.c_str());
32-
if(filter)
40+
if(!hasFilters())
3341
{
34-
AVFilterContext* context = NULL;
35-
const int err = avfilter_graph_create_filter(&context, filter, NULL, filtername.c_str(), NULL, _graph);
36-
if(err < 0)
37-
{
38-
std::string msg("Cannot add filter ");
39-
msg += filtername;
40-
msg += " to the graph: ";
41-
msg += getDescriptionFromErrorCode(err);
42-
throw std::runtime_error(msg);
43-
}
44-
else
45-
_filters.push_back(std::make_pair(filter, context));
42+
LOG_DEBUG("No filter to process.")
43+
return;
4644
}
47-
else
45+
46+
// init filter graph
47+
if(!_isInit)
48+
init(frame);
49+
50+
// setup source frame
51+
int ret = av_buffersrc_write_frame(_filters.at(0)->getAVFilterContext(), &frame.getAVFrame());
52+
if(ret < 0)
4853
{
49-
throw std::runtime_error("Cannot find filter " + filtername);
54+
throw std::runtime_error("Error when adding a frame to the source buffer used to start to process filters: " +
55+
getDescriptionFromErrorCode(ret));
56+
}
57+
58+
// pull filtered data from the filter graph
59+
for(;;)
60+
{
61+
ret = av_buffersink_get_frame(_filters.at(_filters.size() - 1)->getAVFilterContext(), &frame.getAVFrame());
62+
if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
63+
break;
64+
if(ret < 0)
65+
{
66+
throw std::runtime_error("Error reading buffer from buffersink: " + getDescriptionFromErrorCode(ret));
67+
}
5068
}
5169
}
5270

53-
void FilterGraph::connectFilters()
71+
Filter& FilterGraph::addFilter(const std::string& filterName, const std::string& filterOptions,
72+
const std::string& instanceName)
73+
{
74+
LOG_INFO("Add filter " << filterName << " to the graph.")
75+
Filter* filter = new Filter(filterName, filterOptions, instanceName);
76+
_filters.push_back(filter);
77+
return *_filters.back();
78+
}
79+
80+
void FilterGraph::init(const Frame& frame)
5481
{
55-
// connecting filters
56-
for(size_t index = 0; index < _filters.size() + 1; index += 2)
82+
// push filters to the graph
83+
pushInBuffer(frame);
84+
for(size_t i = 1; i < _filters.size(); ++i)
5785
{
58-
const int err = avfilter_link(_filters.at(index).second, 0, _filters.at(index + 1).second, 0);
86+
pushFilter(*_filters.at(i));
87+
}
88+
pushOutBuffer(frame);
89+
90+
// connect filters
91+
for(size_t index = 0; index < _filters.size() - 1; ++index)
92+
{
93+
LOG_INFO("Connect filter " << _filters.at(index)->getName() << " to filter " << _filters.at(index + 1)->getName())
94+
const int err =
95+
avfilter_link(_filters.at(index)->getAVFilterContext(), 0, _filters.at(index + 1)->getAVFilterContext(), 0);
5996
if(err < 0)
6097
{
61-
throw std::runtime_error("Error connecting filters.");
98+
throw std::runtime_error("Error when connecting filters.");
6299
}
63100
}
64-
// configuring
101+
102+
// configuring the graph
103+
LOG_INFO("Configuring filter graph.")
65104
const int err = avfilter_graph_config(_graph, NULL);
66105
if(err < 0)
67-
throw std::runtime_error("Error configuring the filter graph.");
106+
{
107+
throw std::runtime_error("Error configuring the filter graph: " + getDescriptionFromErrorCode(err));
108+
}
109+
110+
_isInit = true;
111+
}
112+
113+
void FilterGraph::pushFilter(Filter& filter)
114+
{
115+
AVFilterContext* context = NULL;
116+
const int err = avfilter_graph_create_filter(&context, &filter.getAVFilter(), filter.getInstanceName().c_str(),
117+
filter.getOptions().c_str(), NULL, _graph);
118+
filter.setAVFilterContext(context);
119+
if(err < 0)
120+
{
121+
std::string msg("Cannot add filter ");
122+
msg += filter.getName();
123+
msg += " (instance=";
124+
msg += filter.getInstanceName();
125+
msg += ") to the graph: ";
126+
msg += getDescriptionFromErrorCode(err);
127+
throw std::runtime_error(msg);
128+
}
129+
}
130+
131+
void FilterGraph::pushInBuffer(const Frame& frame)
132+
{
133+
std::string filterName;
134+
std::stringstream filterOptions;
135+
// audio frame
136+
if(frame.isAudioFrame())
137+
{
138+
filterName = "abuffer";
139+
const AudioFrame& audioFrame = dynamic_cast<const AudioFrame&>(frame);
140+
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
141+
<< _codec.getAVCodecContext().time_base.den << ":";
142+
filterOptions << "sample_rate=" << audioFrame.getSampleRate() << ":";
143+
filterOptions << "sample_fmt=" << getSampleFormatName(audioFrame.getSampleFormat()) << ":";
144+
filterOptions << "channel_layout=0x" << audioFrame.getChannelLayout();
145+
}
146+
// video frame
147+
else if(frame.isVideoFrame())
148+
{
149+
filterName = "buffer";
150+
const VideoFrame& videoFrame = dynamic_cast<const VideoFrame&>(frame);
151+
filterOptions << "video_size=" << videoFrame.getWidth() << "x" << videoFrame.getHeight() << ":";
152+
filterOptions << "pix_fmt=" << getPixelFormatName(videoFrame.getPixelFormat()) << ":";
153+
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
154+
<< _codec.getAVCodecContext().time_base.den << ":";
155+
filterOptions << "pixel_aspect=" << _codec.getAVCodecContext().sample_aspect_ratio.num << "/"
156+
<< _codec.getAVCodecContext().sample_aspect_ratio.den;
157+
}
158+
// invalid frame
159+
else
160+
throw std::runtime_error("Cannot create input buffer of filter graph: the given frame is invalid.");
161+
162+
// add in buffer
163+
Filter* in = new Filter(filterName, filterOptions.str(), "in");
164+
LOG_INFO("Add filter '" << filterName << "' at the beginning of the graph.")
165+
_filters.insert(_filters.begin(), in);
166+
pushFilter(*in);
167+
}
168+
169+
void FilterGraph::pushOutBuffer(const Frame& frame)
170+
{
171+
std::string filterName;
172+
173+
if(frame.isAudioFrame())
174+
filterName = "abuffersink";
175+
else if(frame.isVideoFrame())
176+
filterName = "buffersink";
177+
else
178+
throw std::runtime_error("Cannot create output buffer of filter graph: the given frame is invalid.");
179+
180+
// add out buffer
181+
Filter& out = addFilter(filterName, "", "out");
182+
pushFilter(out);
68183
}
69184
}

src/AvTranscoder/filter/FilterGraph.hpp

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22
#define _AV_TRANSCODER_FILTER_FILTER_GRAPH_HPP_
33

44
#include <AvTranscoder/common.hpp>
5+
#include <AvTranscoder/filter/Filter.hpp>
6+
#include <AvTranscoder/codec/ICodec.hpp>
7+
#include <AvTranscoder/data/decoded/Frame.hpp>
58

69
#include <vector>
7-
#include <utility>
810

911
struct AVFilterGraph;
10-
struct AVFilter;
11-
struct AVFilterContext;
1212

1313
namespace avtranscoder
1414
{
1515

1616
/**
17-
* @brief
17+
* @brief Manager of filters.
18+
* @warning Currently, the class manages only filters which has one input and one output.
19+
* @note See 'complex graph' definition in ffmpeg documentation.
1820
**/
1921
class AvExport FilterGraph
2022
{
@@ -23,23 +25,65 @@ class AvExport FilterGraph
2325
FilterGraph& operator=(const FilterGraph& otherFilterGraph);
2426

2527
public:
26-
typedef std::pair<AVFilter*, AVFilterContext*> Filter;
27-
28-
public:
29-
FilterGraph();
28+
FilterGraph(const ICodec& codec);
3029
~FilterGraph();
3130

32-
void addFilter(const std::string& filtername);
33-
Filter& getFirstFilter() { return _filters.at(0); }
34-
Filter& getLastFilter() { return _filters.at(_filters.size() - 1); }
31+
/**
32+
* @brief Add a filter.
33+
* @param filterName: the method gets the filter definition from this name.
34+
* @param filterArgs: options to initialize the filter with. This must be a ':'-separated list of options in the
35+
* 'key=value' form.
36+
* @param instanceName: name of the instance filter in the graph (if empty, same as filterName).
37+
* @return the filter added
38+
* @throw runtime exception if the filter is not found
39+
* @warning The filter will be added to the filter graph when calling process method.
40+
* @see process
41+
*/
42+
Filter& addFilter(const std::string& filterName, const std::string& filterOptions = "",
43+
const std::string& instanceName = "");
3544

45+
/**
46+
* @brief Pull filtered data from the filter graph, and put result to the given frame.
47+
* @param frame: both input and output data of the method.
48+
* @note Do nothing if there was no filter added.
49+
*/
50+
void process(Frame& frame);
51+
52+
private:
53+
/**
54+
* @return If at least one filter has been added to the filter graph
55+
*/
3656
bool hasFilters() const { return !_filters.empty(); }
3757

38-
void connectFilters();
58+
/**
59+
* @brief Initialize the graph of filters to process.
60+
* @see pushFilterToGraph
61+
* @see pushInBuffer
62+
* @see pushOutBuffer
63+
*/
64+
void init(const Frame& frame);
65+
66+
/**
67+
* @brief Push the given Filter to the graph.
68+
*/
69+
void pushFilter(Filter& filter);
70+
71+
///@{
72+
/// @brief Push the input and output buffer at the beginning and the end of the graph.
73+
void pushInBuffer(const Frame& frame);
74+
void pushOutBuffer(const Frame& frame);
75+
//@}
3976

4077
private:
41-
AVFilterGraph* _graph;
42-
std::vector<Filter> _filters;
78+
AVFilterGraph* _graph; ///< The graph which holds the filters.
79+
std::vector<Filter*> _filters; ///< List of filters to process.
80+
const ICodec& _codec; ///< Codec of the stream on which the filters will be applied.
81+
82+
/**
83+
* @brief Is the FilterGraph initialized.
84+
* @see init
85+
*/
86+
bool _isInit;
4387
};
4488
}
4589

0 commit comments

Comments
 (0)