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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy