Skip to content

Commit 4c480ce

Browse files
committed
Merge pull request #180 from cchampet/dev_addAvFilter
Manage filters
2 parents 8a895ac + 6371c40 commit 4c480ce

File tree

16 files changed

+558
-6
lines changed

16 files changed

+558
-6
lines changed

src/AvTranscoder/Library.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ extern "C" {
55
#include <libavcodec/version.h>
66
#include <libswscale/version.h>
77
#include <libswscale/swscale.h>
8+
#include <libavfilter/version.h>
89
#ifdef AVTRANSCODER_LIBAV_DEPENDENCY
910
#include <libavresample/version.h>
1011
#else
1112
#include <libswresample/version.h>
1213
#endif
1314
#include <libavformat/avformat.h>
15+
#include <libavfilter/avfilter.h>
1416
}
1517

1618
#include <cstring>
@@ -94,6 +96,8 @@ Libraries getLibraries()
9496
#endif
9597
libs.push_back(
9698
Library("swscale", swscale_license(), LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO));
99+
libs.push_back(Library("avfilter", avfilter_license(), LIBAVFILTER_VERSION_MAJOR, LIBAVFILTER_VERSION_MINOR,
100+
LIBAVFILTER_VERSION_MICRO));
97101

98102
return libs;
99103
}

src/AvTranscoder/avTranscoder.i

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@
3838
%include "AvTranscoder/transform/transform.i"
3939
%include "AvTranscoder/file/file.i"
4040
%include "AvTranscoder/stat/stat.i"
41+
%include "AvTranscoder/filter/filter.i"
4142
%include "AvTranscoder/transcoder/transcoder.i"
4243
%include "AvTranscoder/reader/reader.i"

src/AvTranscoder/common.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
extern "C" {
44
#include <libavformat/avformat.h>
5+
#include <libavfilter/avfilter.h>
56
#include <libavutil/error.h>
67
}
78

@@ -15,6 +16,7 @@ namespace avtranscoder
1516
void preloadCodecsAndFormats()
1617
{
1718
av_register_all();
19+
avfilter_register_all();
1820
}
1921

2022
std::string getDescriptionFromErrorCode(const int code)

src/AvTranscoder/data/decoded/AudioFrame.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class AvExport AudioFrame : public Frame
4444

4545
size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); }
4646
size_t getNbChannels() const { return av_frame_get_channels(_frame); }
47+
size_t getChannelLayout() const { return av_frame_get_channel_layout(_frame); }
4748
AVSampleFormat getSampleFormat() const { return static_cast<AVSampleFormat>(_frame->format); }
4849
size_t getNbSamplesPerChannel() const { return _frame->nb_samples; }
4950
AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); }

src/AvTranscoder/data/decoded/Frame.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,18 @@ void Frame::allocateAVFrame()
8080
throw std::runtime_error("Unable to allocate an empty Frame.");
8181
}
8282
}
83+
84+
bool Frame::isAudioFrame() const
85+
{
86+
if(_frame->sample_rate && _frame->channels && _frame->channel_layout && _frame->format != -1)
87+
return true;
88+
return false;
89+
}
90+
91+
bool Frame::isVideoFrame() const
92+
{
93+
if(_frame->width && _frame->height && _frame->format != -1)
94+
return true;
95+
return false;
96+
}
8397
}

src/AvTranscoder/data/decoded/Frame.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ class AvExport Frame
6363
*/
6464
void clear();
6565

66+
/**
67+
* @return If it corresponds to a valid audio frame.
68+
* @see AudioFrame
69+
*/
70+
bool isAudioFrame() const;
71+
72+
/**
73+
* @return If it corresponds to a valid video frame.
74+
* @see VideoFrame
75+
*/
76+
bool isVideoFrame() const;
77+
6678
#ifndef SWIG
6779
AVFrame& getAVFrame() { return *_frame; }
6880
const AVFrame& getAVFrame() const { return *_frame; }

src/AvTranscoder/decoder/AudioGenerator.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class AvExport AudioGenerator : public IDecoder
2222
void setNextFrame(Frame& inputFrame);
2323

2424
private:
25-
Frame* _inputFrame; ///< Has link (no ownership)
26-
AudioFrame* _silent; ///< The generated silent (has ownership)
25+
Frame* _inputFrame; ///< Has link (no ownership)
26+
AudioFrame* _silent; ///< The generated silent (has ownership)
2727
};
2828
}
2929

src/AvTranscoder/filter/Filter.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "Filter.hpp"
2+
3+
extern "C" {
4+
#include <libavfilter/avfilter.h>
5+
}
6+
7+
#include <stdexcept>
8+
9+
namespace avtranscoder
10+
{
11+
12+
Filter::Filter(const std::string& name, const std::string& options, const std::string& instanceName)
13+
: _filter(NULL)
14+
, _context(NULL)
15+
, _options(options)
16+
, _instanceName(instanceName.empty() ? name : instanceName)
17+
{
18+
_filter = avfilter_get_by_name(name.c_str());
19+
if(!_filter)
20+
{
21+
std::string msg("Cannot find filter ");
22+
msg += name;
23+
msg += ". It will not be added to the filter graph.";
24+
throw std::runtime_error(msg);
25+
}
26+
}
27+
28+
Filter::~Filter()
29+
{
30+
avfilter_free(_context);
31+
}
32+
33+
std::string Filter::getName() const
34+
{
35+
return _filter->name ? std::string(_filter->name) : "";
36+
}
37+
}

src/AvTranscoder/filter/Filter.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#ifndef _AV_TRANSCODER_FILTER_FILTER_HPP_
2+
#define _AV_TRANSCODER_FILTER_FILTER_HPP_
3+
4+
#include <AvTranscoder/common.hpp>
5+
6+
struct AVFilter;
7+
struct AVFilterContext;
8+
9+
namespace avtranscoder
10+
{
11+
12+
/**
13+
* @brief Describe a filter and its options.
14+
**/
15+
class AvExport Filter
16+
{
17+
public:
18+
Filter(const std::string& name, const std::string& options = "", const std::string& instanceName = "");
19+
~Filter();
20+
21+
std::string getName() const;
22+
std::string getOptions() const { return _options; }
23+
std::string getInstanceName() const { return _instanceName; }
24+
25+
#ifndef SWIG
26+
AVFilter& getAVFilter() { return *_filter; }
27+
AVFilterContext* getAVFilterContext() { return _context; }
28+
29+
void setAVFilterContext(AVFilterContext* newContext) { _context = newContext; }
30+
#endif
31+
32+
private:
33+
AVFilter* _filter;
34+
AVFilterContext* _context;
35+
std::string _options;
36+
std::string _instanceName;
37+
};
38+
}
39+
40+
#endif
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#include "FilterGraph.hpp"
2+
3+
#include <AvTranscoder/util.hpp>
4+
#include <AvTranscoder/data/decoded/AudioFrame.hpp>
5+
#include <AvTranscoder/data/decoded/VideoFrame.hpp>
6+
7+
extern "C" {
8+
#include <libavfilter/avfilter.h>
9+
#include <libavfilter/buffersrc.h>
10+
#include <libavfilter/buffersink.h>
11+
}
12+
13+
#include <stdexcept>
14+
#include <sstream>
15+
16+
namespace avtranscoder
17+
{
18+
19+
FilterGraph::FilterGraph(const ICodec& codec)
20+
: _graph(avfilter_graph_alloc())
21+
, _filters()
22+
, _codec(codec)
23+
, _isInit(false)
24+
{
25+
if(!_graph)
26+
throw std::runtime_error("Unable to create filter graph: out of memory.");
27+
}
28+
29+
FilterGraph::~FilterGraph()
30+
{
31+
for(std::vector<Filter*>::iterator it = _filters.begin(); it < _filters.end(); ++it)
32+
{
33+
it = _filters.erase(it);
34+
}
35+
avfilter_graph_free(&_graph);
36+
}
37+
38+
void FilterGraph::process(Frame& frame)
39+
{
40+
if(!hasFilters())
41+
{
42+
LOG_DEBUG("No filter to process.")
43+
return;
44+
}
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)
53+
{
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+
}
68+
}
69+
}
70+
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)
81+
{
82+
// push filters to the graph
83+
pushInBuffer(frame);
84+
for(size_t i = 1; i < _filters.size(); ++i)
85+
{
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);
96+
if(err < 0)
97+
{
98+
throw std::runtime_error("Error when connecting filters.");
99+
}
100+
}
101+
102+
// configuring the graph
103+
LOG_INFO("Configuring filter graph.")
104+
const int err = avfilter_graph_config(_graph, NULL);
105+
if(err < 0)
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" << std::hex << 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);
183+
}
184+
}

0 commit comments

Comments
 (0)