Skip to content

[WIP] add avfilter #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jan 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/AvTranscoder/Library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ extern "C" {
#include <libavcodec/version.h>
#include <libswscale/version.h>
#include <libswscale/swscale.h>
#include <libavfilter/version.h>
#ifdef AVTRANSCODER_LIBAV_DEPENDENCY
#include <libavresample/version.h>
#else
#include <libswresample/version.h>
#endif
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
}

#include <cstring>
Expand Down Expand Up @@ -94,6 +96,8 @@ Libraries getLibraries()
#endif
libs.push_back(
Library("swscale", swscale_license(), LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO));
libs.push_back(Library("avfilter", avfilter_license(), LIBAVFILTER_VERSION_MAJOR, LIBAVFILTER_VERSION_MINOR,
LIBAVFILTER_VERSION_MICRO));

return libs;
}
Expand Down
1 change: 1 addition & 0 deletions src/AvTranscoder/avTranscoder.i
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@
%include "AvTranscoder/transform/transform.i"
%include "AvTranscoder/file/file.i"
%include "AvTranscoder/stat/stat.i"
%include "AvTranscoder/filter/filter.i"
%include "AvTranscoder/transcoder/transcoder.i"
%include "AvTranscoder/reader/reader.i"
2 changes: 2 additions & 0 deletions src/AvTranscoder/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

extern "C" {
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavutil/error.h>
}

Expand All @@ -15,6 +16,7 @@ namespace avtranscoder
void preloadCodecsAndFormats()
{
av_register_all();
avfilter_register_all();
}

std::string getDescriptionFromErrorCode(const int code)
Expand Down
1 change: 1 addition & 0 deletions src/AvTranscoder/data/decoded/AudioFrame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class AvExport AudioFrame : public Frame

size_t getSampleRate() const { return av_frame_get_sample_rate(_frame); }
size_t getNbChannels() const { return av_frame_get_channels(_frame); }
size_t getChannelLayout() const { return av_frame_get_channel_layout(_frame); }
AVSampleFormat getSampleFormat() const { return static_cast<AVSampleFormat>(_frame->format); }
size_t getNbSamplesPerChannel() const { return _frame->nb_samples; }
AudioFrameDesc desc() const { return AudioFrameDesc(getSampleRate(), getNbChannels(), getSampleFormat()); }
Expand Down
14 changes: 14 additions & 0 deletions src/AvTranscoder/data/decoded/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,18 @@ void Frame::allocateAVFrame()
throw std::runtime_error("Unable to allocate an empty Frame.");
}
}

bool Frame::isAudioFrame() const
{
if(_frame->sample_rate && _frame->channels && _frame->channel_layout && _frame->format != -1)
return true;
return false;
}

bool Frame::isVideoFrame() const
{
if(_frame->width && _frame->height && _frame->format != -1)
return true;
return false;
}
}
12 changes: 12 additions & 0 deletions src/AvTranscoder/data/decoded/Frame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ class AvExport Frame
*/
void clear();

/**
* @return If it corresponds to a valid audio frame.
* @see AudioFrame
*/
bool isAudioFrame() const;

/**
* @return If it corresponds to a valid video frame.
* @see VideoFrame
*/
bool isVideoFrame() const;

#ifndef SWIG
AVFrame& getAVFrame() { return *_frame; }
const AVFrame& getAVFrame() const { return *_frame; }
Expand Down
4 changes: 2 additions & 2 deletions src/AvTranscoder/decoder/AudioGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class AvExport AudioGenerator : public IDecoder
void setNextFrame(Frame& inputFrame);

private:
Frame* _inputFrame; ///< Has link (no ownership)
AudioFrame* _silent; ///< The generated silent (has ownership)
Frame* _inputFrame; ///< Has link (no ownership)
AudioFrame* _silent; ///< The generated silent (has ownership)
};
}

Expand Down
37 changes: 37 additions & 0 deletions src/AvTranscoder/filter/Filter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "Filter.hpp"

extern "C" {
#include <libavfilter/avfilter.h>
}

#include <stdexcept>

namespace avtranscoder
{

Filter::Filter(const std::string& name, const std::string& options, const std::string& instanceName)
: _filter(NULL)
, _context(NULL)
, _options(options)
, _instanceName(instanceName.empty() ? name : instanceName)
{
_filter = avfilter_get_by_name(name.c_str());
if(!_filter)
{
std::string msg("Cannot find filter ");
msg += name;
msg += ". It will not be added to the filter graph.";
throw std::runtime_error(msg);
}
}

Filter::~Filter()
{
avfilter_free(_context);
}

std::string Filter::getName() const
{
return _filter->name ? std::string(_filter->name) : "";
}
}
40 changes: 40 additions & 0 deletions src/AvTranscoder/filter/Filter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef _AV_TRANSCODER_FILTER_FILTER_HPP_
#define _AV_TRANSCODER_FILTER_FILTER_HPP_

#include <AvTranscoder/common.hpp>

struct AVFilter;
struct AVFilterContext;

namespace avtranscoder
{

/**
* @brief Describe a filter and its options.
**/
class AvExport Filter
{
public:
Filter(const std::string& name, const std::string& options = "", const std::string& instanceName = "");
~Filter();

std::string getName() const;
std::string getOptions() const { return _options; }
std::string getInstanceName() const { return _instanceName; }

#ifndef SWIG
AVFilter& getAVFilter() { return *_filter; }
AVFilterContext* getAVFilterContext() { return _context; }

void setAVFilterContext(AVFilterContext* newContext) { _context = newContext; }
#endif

private:
AVFilter* _filter;
AVFilterContext* _context;
std::string _options;
std::string _instanceName;
};
}

#endif
184 changes: 184 additions & 0 deletions src/AvTranscoder/filter/FilterGraph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#include "FilterGraph.hpp"

#include <AvTranscoder/util.hpp>
#include <AvTranscoder/data/decoded/AudioFrame.hpp>
#include <AvTranscoder/data/decoded/VideoFrame.hpp>

extern "C" {
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
}

#include <stdexcept>
#include <sstream>

namespace avtranscoder
{

FilterGraph::FilterGraph(const ICodec& codec)
: _graph(avfilter_graph_alloc())
, _filters()
, _codec(codec)
, _isInit(false)
{
if(!_graph)
throw std::runtime_error("Unable to create filter graph: out of memory.");
}

FilterGraph::~FilterGraph()
{
for(std::vector<Filter*>::iterator it = _filters.begin(); it < _filters.end(); ++it)
{
it = _filters.erase(it);
}
avfilter_graph_free(&_graph);
}

void FilterGraph::process(Frame& frame)
{
if(!hasFilters())
{
LOG_DEBUG("No filter to process.")
return;
}

// init filter graph
if(!_isInit)
init(frame);

// setup source frame
int ret = av_buffersrc_write_frame(_filters.at(0)->getAVFilterContext(), &frame.getAVFrame());
if(ret < 0)
{
throw std::runtime_error("Error when adding a frame to the source buffer used to start to process filters: " +
getDescriptionFromErrorCode(ret));
}

// pull filtered data from the filter graph
for(;;)
{
ret = av_buffersink_get_frame(_filters.at(_filters.size() - 1)->getAVFilterContext(), &frame.getAVFrame());
if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
break;
if(ret < 0)
{
throw std::runtime_error("Error reading buffer from buffersink: " + getDescriptionFromErrorCode(ret));
}
}
}

Filter& FilterGraph::addFilter(const std::string& filterName, const std::string& filterOptions,
const std::string& instanceName)
{
LOG_INFO("Add filter " << filterName << " to the graph.")
Filter* filter = new Filter(filterName, filterOptions, instanceName);
_filters.push_back(filter);
return *_filters.back();
}

void FilterGraph::init(const Frame& frame)
{
// push filters to the graph
pushInBuffer(frame);
for(size_t i = 1; i < _filters.size(); ++i)
{
pushFilter(*_filters.at(i));
}
pushOutBuffer(frame);

// connect filters
for(size_t index = 0; index < _filters.size() - 1; ++index)
{
LOG_INFO("Connect filter " << _filters.at(index)->getName() << " to filter " << _filters.at(index + 1)->getName())
const int err =
avfilter_link(_filters.at(index)->getAVFilterContext(), 0, _filters.at(index + 1)->getAVFilterContext(), 0);
if(err < 0)
{
throw std::runtime_error("Error when connecting filters.");
}
}

// configuring the graph
LOG_INFO("Configuring filter graph.")
const int err = avfilter_graph_config(_graph, NULL);
if(err < 0)
{
throw std::runtime_error("Error configuring the filter graph: " + getDescriptionFromErrorCode(err));
}

_isInit = true;
}

void FilterGraph::pushFilter(Filter& filter)
{
AVFilterContext* context = NULL;
const int err = avfilter_graph_create_filter(&context, &filter.getAVFilter(), filter.getInstanceName().c_str(),
filter.getOptions().c_str(), NULL, _graph);
filter.setAVFilterContext(context);
if(err < 0)
{
std::string msg("Cannot add filter ");
msg += filter.getName();
msg += " (instance=";
msg += filter.getInstanceName();
msg += ") to the graph: ";
msg += getDescriptionFromErrorCode(err);
throw std::runtime_error(msg);
}
}

void FilterGraph::pushInBuffer(const Frame& frame)
{
std::string filterName;
std::stringstream filterOptions;
// audio frame
if(frame.isAudioFrame())
{
filterName = "abuffer";
const AudioFrame& audioFrame = dynamic_cast<const AudioFrame&>(frame);
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
<< _codec.getAVCodecContext().time_base.den << ":";
filterOptions << "sample_rate=" << audioFrame.getSampleRate() << ":";
filterOptions << "sample_fmt=" << getSampleFormatName(audioFrame.getSampleFormat()) << ":";
filterOptions << "channel_layout=0x" << std::hex << audioFrame.getChannelLayout();
}
// video frame
else if(frame.isVideoFrame())
{
filterName = "buffer";
const VideoFrame& videoFrame = dynamic_cast<const VideoFrame&>(frame);
filterOptions << "video_size=" << videoFrame.getWidth() << "x" << videoFrame.getHeight() << ":";
filterOptions << "pix_fmt=" << getPixelFormatName(videoFrame.getPixelFormat()) << ":";
filterOptions << "time_base=" << _codec.getAVCodecContext().time_base.num << "/"
<< _codec.getAVCodecContext().time_base.den << ":";
filterOptions << "pixel_aspect=" << _codec.getAVCodecContext().sample_aspect_ratio.num << "/"
<< _codec.getAVCodecContext().sample_aspect_ratio.den;
}
// invalid frame
else
throw std::runtime_error("Cannot create input buffer of filter graph: the given frame is invalid.");

// add in buffer
Filter* in = new Filter(filterName, filterOptions.str(), "in");
LOG_INFO("Add filter '" << filterName << "' at the beginning of the graph.")
_filters.insert(_filters.begin(), in);
pushFilter(*in);
}

void FilterGraph::pushOutBuffer(const Frame& frame)
{
std::string filterName;

if(frame.isAudioFrame())
filterName = "abuffersink";
else if(frame.isVideoFrame())
filterName = "buffersink";
else
throw std::runtime_error("Cannot create output buffer of filter graph: the given frame is invalid.");

// add out buffer
Filter& out = addFilter(filterName, "", "out");
pushFilter(out);
}
}
Loading
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