Skip to content

Commit

Permalink
avresample support by mapping to swresample
Browse files Browse the repository at this point in the history
  • Loading branch information
wang-bin committed Jul 5, 2013
1 parent 79a735d commit bdd55f8
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 3 deletions.
2 changes: 1 addition & 1 deletion QtAV.pro
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ OTHER_FILES += \

EssentialDepends = avutil avcodec avformat swscale
OptionalDepends = portaudio direct2d gdiplus gl \
swresample
swresample avresample
unix {
isEqual(QT_MAJOR_VERSION, 4) {
OptionalDepends += xv
Expand Down
11 changes: 11 additions & 0 deletions config.tests/avresample/avresample.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CONFIG -= qt
CONFIG += console
DEFINES += __STDC_CONSTANT_MACROS
*msvc* {
#link FFmpeg and portaudio which are built by gcc need /SAFESEH:NO
QMAKE_LFLAGS += /SAFESEH:NO
INCLUDEPATH += ../../src/compat/msvc
}
SOURCES += main.cpp

LIBS += -lavresample
26 changes: 26 additions & 0 deletions config.tests/avresample/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/******************************************************************************
QtAV: Media play library based on Qt and FFmpeg
Copyright (C) 2013 Wang Bin <[email protected]>
* This file is part of QtAV
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#include <libavresample/avresample.h>

int main()
{
return 0;
}
8 changes: 6 additions & 2 deletions src/AudioResampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ namespace QtAV {
FACTORY_DEFINE(AudioResampler)

extern void RegisterAudioResamplerFF_Man();
extern void RegisterAudioResamplerLibav_Man();

void AudioResampler_RegisterAll()
{
#if QTAV_HAVE(SWRESAMPLE) || QTAV_HAVE(AVRESAMPLE)
#if QTAV_HAVE(SWRESAMPLE)
RegisterAudioResamplerFF_Man();
#endif
#endif //QTAV_HAVE(SWRESAMPLE)
#if QTAV_HAVE(AVRESAMPLE)
RegisterAudioResamplerLibav_Man();
#endif //QTAV_HAVE(AVRESAMPLE)
}

AudioResampler::AudioResampler()
Expand Down
170 changes: 170 additions & 0 deletions src/AudioResamplerLibav.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#define QTAV_HAVE_SWR_AVR_MAP 1
#include "AudioResampler.h"
#include "private/AudioResampler_p.h"
#include "QtAV/QtAV_Compat.h"
#include "prepost.h"

namespace QtAV {

class AudioResamplerLibavPrivate;
class AudioResamplerLibav : public AudioResampler //Q_EXPORT is not needed
{
DPTR_DECLARE_PRIVATE(AudioResampler)
public:
AudioResamplerLibav();
virtual bool convert(const quint8** data);
virtual bool prepare();
};

AudioResamplerId AudioResamplerId_Libav = 1;
FACTORY_REGISTER_ID_AUTO(AudioResampler, Libav, "Libav")

void RegisterAudioResamplerLibav_Man()
{
FACTORY_REGISTER_ID_MAN(AudioResampler, Libav, "Libav")
}


class AudioResamplerLibavPrivate : public AudioResamplerPrivate
{
public:
AudioResamplerLibavPrivate():
context(0)
{
}
~AudioResamplerLibavPrivate() {
if (context) {
swr_free(&context);
context = 0;
}
}
SwrContext *context;
};

AudioResamplerLibav::AudioResamplerLibav():
AudioResampler(*new AudioResamplerLibavPrivate())
{
}

bool AudioResamplerLibav::convert(const quint8 **data)
{
DPTR_D(AudioResamplerLibav);
/*
* swr_get_delay: Especially when downsampling by a large value, the output sample rate may be a poor choice to represent
* the delay, similarly upsampling and the input sample rate.
*/
d.out_samples_per_channel = av_rescale_rnd(
#if HAVE_SWR_GET_DELAY
swr_get_delay(d.context, qMax(d.in_format.sampleRate(), d.out_format.sampleRate())) +
#else
128 + //TODO: QtAV_Compat
#endif //HAVE_SWR_GET_DELAY
d.in_samples_per_channel //TODO: wanted_samples(ffplay mplayer2)
, d.out_format.sampleRate(), d.in_format.sampleRate(), AV_ROUND_UP);
//TODO: why crash for swr 0.5?
//int out_size = av_samples_get_buffer_size(NULL/*out linesize*/, d.out_channels, d.out_samples_per_channel, (AVSampleFormat)d.out_sample_format, 0/*alignment default*/);
int size_per_sample_with_channels = d.out_format.channels()*d.out_format.bytesPerSample();
int out_size = d.out_samples_per_channel*size_per_sample_with_channels;
if (out_size > d.data_out.size())
d.data_out.resize(out_size);
uint8_t *out[] = {(uint8_t*)d.data_out.data()};
//number of input/output samples available in one channel
int converted_samplers_per_channel = swr_convert(d.context, out, d.out_samples_per_channel, data, d.in_samples_per_channel);
if (converted_samplers_per_channel < 0) {
qWarning("[AudioResamplerLibav] %s", av_err2str(converted_samplers_per_channel));
return false;
}
//TODO: converted_samplers_per_channel==out_samples_per_channel means out_size is too small, see mplayer2
//converted_samplers_per_channel*d.out_channels*av_get_bytes_per_sample(d.out_sample_format)
//av_samples_get_buffer_size(0, d.out_channels, converted_samplers_per_channel, d.out_sample_format, 0)
//if (converted_samplers_per_channel != out_size)
d.data_out.resize(converted_samplers_per_channel*size_per_sample_with_channels);
return true;
}

/*
*TODO: broken sample rate(AAC), see mplayer
*/
bool AudioResamplerLibav::prepare()
{
DPTR_D(AudioResamplerLibav);
if (!d.in_format.isValid()) {
qWarning("src audio parameters 'channel layout(or channels), sample rate and sample format must be set before initialize resampler");
return false;
}
//TODO: also in do this statistics
qDebug("in cs: %d, cl: %lld", d.in_format.channels(), d.in_format.channelLayout());
if (!d.in_format.channels()) {
if (!d.in_format.channelLayout()) { //FIXME: already return
d.in_format.setChannels(2);
d.in_format.setChannelLayout(av_get_default_channel_layout(d.in_format.channels())); //from mplayer2
qWarning("both channels and channel layout are not available, assume channels=%d, channel layout=%lld", d.in_format.channels(), d.in_format.channelLayout());
} else {
d.in_format.setChannels(av_get_channel_layout_nb_channels(d.in_format.channelLayout()));
}
}
if (!d.in_format.channels())
d.in_format.setChannels(2); //TODO: why av_get_channel_layout_nb_channels() may return 0?
if (!d.in_format.channelLayout()) {
qWarning("channel layout not available, use default layout");
d.in_format.setChannelLayout(av_get_default_channel_layout(d.in_format.channels()));
}
qDebug("in cs: %d, cl: %lld", d.in_format.channels(), d.in_format.channelLayout());

if (!d.out_format.channelLayout())
d.out_format.setChannelLayout(d.in_format.channelLayout());
if (!d.out_format.channels())
d.out_format.setChannels(av_get_channel_layout_nb_channels(d.out_format.channelLayout()));
if (!d.out_format.channels())
d.out_format.setChannels(inAudioFormat().channels());
if (!d.out_format.sampleRate())
d.out_format.setSampleRate(inAudioFormat().sampleRate());
if (d.speed <= 0)
d.speed = 1.0;
//DO NOT set sample rate here, we should keep the original and multiply 1/speed when needed
//if (d.speed != 1.0)
// d.out_format.setSampleRate(int(qreal(d.out_format.sampleFormat())/d.speed));
qDebug("swr speed=%.2f", d.speed);

//d.in_planes = av_sample_fmt_is_planar((enum AVSampleFormat)d.in_sample_format) ? d.in_channels : 1;
//d.out_planes = av_sample_fmt_is_planar((enum AVSampleFormat)d.out_sample_format) ? d.out_channels : 1;
//swr_free(&d.context); //ffplay
//If use swr_alloc() need to set the parameters (av_opt_set_xxx() manually or with swr_alloc_set_opts()) before calling swr_init()
d.context = swr_alloc_set_opts(d.context
, d.out_format.channelLayout()
, (enum AVSampleFormat)outAudioFormat().sampleFormatFFmpeg()
, qreal(outAudioFormat().sampleRate())/d.speed
, d.in_format.channelLayout()
, (enum AVSampleFormat)inAudioFormat().sampleFormatFFmpeg()
, inAudioFormat().sampleRate()
, 0 /*log_offset*/, 0 /*log_ctx*/);
/*
av_opt_set_int(d.context, "in_channel_layout", d.in_channel_layout, 0);
av_opt_set_int(d.context, "in_sample_rate", d.in_format.sampleRate(), 0);
av_opt_set_sample_fmt(d.context, "in_sample_fmt", (enum AVSampleFormat)in_format.sampleFormatFFmpeg(), 0);
av_opt_set_int(d.context, "out_channel_layout", d.out_channel_layout, 0);
av_opt_set_int(d.context, "out_sample_rate", d.out_format.sampleRate(), 0);
av_opt_set_sample_fmt(d.context, "out_sample_fmt", (enum AVSampleFormat)out_format.sampleFormatFFmpeg(), 0);
*/
av_log(NULL, AV_LOG_INFO, "out cl: %lld\n", d.out_format.channelLayout());
av_log(NULL, AV_LOG_INFO, "out fmt: %d\n", d.out_format.sampleFormat());
av_log(NULL, AV_LOG_INFO, "out freq: %d\n", d.out_format.sampleRate());
av_log(NULL, AV_LOG_INFO, "in cl: %lld\n", d.in_format.channelLayout());
av_log(NULL, AV_LOG_INFO, "in fmt: %d\n", d.in_format.sampleFormat());
av_log(NULL, AV_LOG_INFO, "in freq: %d\n", d.in_format.sampleRate());


if (d.context) {
int ret = swr_init(d.context);
if (ret < 0) {
qWarning("swr_init failed: %s", av_err2str(ret));
return false;
}
} else {
qWarning("Allocat swr context failed!");
return false;
}
return true;
}

} //namespace QtAV
1 change: 1 addition & 0 deletions src/QtAV/AudioResamplerTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
namespace QtAV {

extern Q_EXPORT AudioResamplerId AudioResamplerId_FF;
extern Q_EXPORT AudioResamplerId AudioResamplerId_Libav;

Q_EXPORT void AudioResampler_RegisterAll();

Expand Down
25 changes: 25 additions & 0 deletions src/QtAV/QtAV_Compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,28 @@ av_always_inline char* av_err2str(int errnum)
#define swresample_license() "Not available."
#endif
#endif

/*
* mapping avresample to swresample
* https://github.com/xbmc/xbmc/commit/274679d
*/
#if (QTAV_HAVE(SWR_AVR_MAP) || !QTAV_HAVE(SWRESAMPLE)) && QTAV_HAVE(AVRESAMPLE)
#define SwrContext AVAudioResampleContext
#define swr_init(ctx) avresample_open(ctx)
#define swr_free(ctx) avresample_close(*ctx)
#define swr_get_class() avresample_get_class()
#define swr_alloc() avresample_alloc_context()
//#define swr_next_pts()
#define swr_set_compensation() avresample_set_compensation()
#define swr_set_channel_mapping(ctx, map) avresample_set_channel_mapping(ctx, map)
#define swr_set_matrix(ctx, matrix, stride) avresample_set_matrix(ctx, matrix, stride)
//#define swr_drop_output(ctx, count)
//#define swr_inject_silence(ctx, count)
#define swr_get_delay(ctx, ...) avresample_get_delay(ctx)
#define swr_convert(ctx, out, out_count, in, in_count) \
avresample_convert(ctx, out, 0, out_count, const_cast<uint8_t**>(in), 0, in_count)
struct SwrContext *swr_alloc_set_opts(struct SwrContext *s, int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate, int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate, int log_offset, void *log_ctx);
#define swresample_version() avresample_version()
#define swresample_configuration() avresample_configuration()
#define swresample_license() avresample_license()
#endif //MAP_SWR_AVR
32 changes: 32 additions & 0 deletions src/QtAV_Compat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,35 @@ PRE_FUNC_ADD(ffmpeg_version_print);

#endif //av_err2str

/*
* always need this function if avresample available
* use AVAudioResampleContext to avoid func type confliction when swr is also available
*/
#if QTAV_HAVE(AVRESAMPLE)
AVAudioResampleContext *swr_alloc_set_opts(AVAudioResampleContext *s
, int64_t out_ch_layout
, enum AVSampleFormat out_sample_fmt
, int out_sample_rate
, int64_t in_ch_layout
, enum AVSampleFormat in_sample_fmt
, int in_sample_rate
, int log_offset, void *log_ctx)
{
//DO NOT use swr_alloc() because it's not defined as a macro in QtAV_Compat.h
if (!s)
s = avresample_alloc_context();
if (!s)
return 0;

Q_UNUSED(log_offset);
Q_UNUSED(log_ctx);

av_opt_set_int(s, "out_channel_layout", out_ch_layout , 0);
av_opt_set_int(s, "out_sample_fmt" , out_sample_fmt , 0);
av_opt_set_int(s, "out_sample_rate" , out_sample_rate, 0);
av_opt_set_int(s, "in_channel_layout" , in_ch_layout , 0);
av_opt_set_int(s, "in_sample_fmt" , in_sample_fmt , 0);
av_opt_set_int(s, "in_sample_rate" , in_sample_rate , 0);
return s;
}
#endif
3 changes: 3 additions & 0 deletions src/QtAV_Global.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ QString aboutFFmpeg_HTML()
#if QTAV_HAVE(SWRESAMPLE)
{ FF_COMPONENT(swresample, SWRESAMPLE) },
#endif //QTAV_HAVE(SWRESAMPLE)
#if QTAV_HAVE(AVRESAMPLE)
{ FF_COMPONENT(avresample, AVRESAMPLE) },
#endif //QTAV_HAVE(AVRESAMPLE)
#undef FF_COMPONENT
{ 0, 0, 0, 0, 0 }
};
Expand Down
1 change: 1 addition & 0 deletions src/libQtAV.pro
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ config_swresample {
}
config_avresample {
DEFINES += QTAV_HAVE_AVRESAMPLE=1
SOURCES += AudioResamplerLibav.cpp
LIBS += -lavresample
}

Expand Down

0 comments on commit bdd55f8

Please sign in to comment.