diff --git a/src/AVDecoder.cpp b/src/AVDecoder.cpp index 2021a346..f8183a04 100644 --- a/src/AVDecoder.cpp +++ b/src/AVDecoder.cpp @@ -59,6 +59,11 @@ bool AVDecoder::isAvailable() const return d_func().codec_ctx != 0; } +bool AVDecoder::prepare() +{ + return true; +} + bool AVDecoder::decode(const QByteArray &encoded) { Q_UNUSED(encoded); diff --git a/src/AVPlayer.cpp b/src/AVPlayer.cpp index 17edbc7c..e06d58e7 100644 --- a/src/AVPlayer.cpp +++ b/src/AVPlayer.cpp @@ -543,6 +543,7 @@ void AVPlayer::setupAudioThread() } qDebug("setCodecContext"); audio_dec->setCodecContext(aCodecCtx); + audio_dec->prepare(); //TODO: setAudioOutput() like vo if (!_audio) { qDebug("new audio output"); @@ -608,6 +609,7 @@ void AVPlayer::setupVideoThread() video_dec = new VideoDecoder(); } video_dec->setCodecContext(vCodecCtx); + video_dec->prepare(); if (!video_thread) { video_thread = new VideoThread(this); video_thread->setClock(clock); diff --git a/src/AudioDecoder.cpp b/src/AudioDecoder.cpp index 2b6434b9..a19df6b2 100644 --- a/src/AudioDecoder.cpp +++ b/src/AudioDecoder.cpp @@ -22,19 +22,45 @@ #include #include #include +#include +#include namespace QtAV { class AudioDecoderPrivate : public AVDecoderPrivate { public: - //AudioDecoderPrivate(); + AudioDecoderPrivate(): + resampler(0) + { + resampler = AudioResamplerFactory::create(AudioResamplerId_FF); + resampler->setOutSampleFormat(AV_SAMPLE_FMT_FLT); + } + virtual ~AudioDecoderPrivate() { + if (resampler) { + delete resampler; + resampler = 0; + } + } + + AudioResampler *resampler; }; AudioDecoder::AudioDecoder() :AVDecoder(*new AudioDecoderPrivate) { +} +bool AudioDecoder::prepare() +{ + DPTR_D(AudioDecoder); + if (!d.codec_ctx || !d.resampler) + return false; + d.resampler->setInChannelLayout(d.codec_ctx->channel_layout); + d.resampler->setInChannels(d.codec_ctx->channels); + d.resampler->setInSampleFormat(d.codec_ctx->sample_fmt); + d.resampler->setInSampleRate(d.codec_ctx->sample_rate); + d.resampler->prepare(); } // @@ -49,6 +75,9 @@ bool AudioDecoder::decode(const QByteArray &encoded) //TODO: use AVPacket directly instead of Packet? int ret = avcodec_decode_audio4(d.codec_ctx, d.frame, &d.got_frame_ptr, &packet); av_free_packet(&packet); + if (ret == AVERROR(EAGAIN)) { + return false; + } if (ret < 0) { qWarning("[AudioDecoder] %s", av_err2str(ret)); return false; @@ -57,6 +86,7 @@ bool AudioDecoder::decode(const QByteArray &encoded) qWarning("[AudioDecoder] got_frame_ptr=false"); return false; } +#if 0 || (!(QTAV_HAVE(SWRESAMPLE) && !QTAV_HAVE(AVRESAMPLE))) int samples_with_channels = d.frame->nb_samples * d.codec_ctx->channels; int samples_with_channels_half = samples_with_channels/2; d.decoded.resize(samples_with_channels * sizeof(float)); @@ -66,7 +96,6 @@ bool AudioDecoder::decode(const QByteArray &encoded) static const float kInt32_inv = 1.0f/2147483648.0f; //TODO: hwa //https://code.google.com/p/lavfilters/source/browse/decoder/LAVAudio/LAVAudio.cpp -#if 1 || (!(QTAV_HAVE(SWRESAMPLE) && !QTAV_HAVE(AVRESAMPLE))) switch (d.codec_ctx->sample_fmt) { case AV_SAMPLE_FMT_U8: { @@ -166,6 +195,13 @@ bool AudioDecoder::decode(const QByteArray &encoded) d.decoded.clear(); break; } +#else + d.resampler->setInSampes(d.frame->nb_samples); + if (!d.resampler->convert((const quint8**)d.frame->extended_data)) { + return false; + } + d.decoded = d.resampler->outData(); + return true; #endif //1 || (!(QTAV_HAVE(SWRESAMPLE) && !QTAV_HAVE(AVRESAMPLE))) /* diff --git a/src/AudioResampler.cpp b/src/AudioResampler.cpp new file mode 100644 index 00000000..e8221646 --- /dev/null +++ b/src/AudioResampler.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + QtAV: Media play library based on Qt and FFmpeg + Copyright (C) 2013 Wang Bin + +* 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 "AudioResampler.h" +#include "private/AudioResampler_p.h" +#include "factory.h" + +namespace QtAV { + +FACTORY_DEFINE(AudioResampler) + +extern void RegisterAudioResamplerFF_Man(); + +void AudioResampler_RegisterAll() +{ + RegisterAudioResamplerFF_Man(); +} + +AudioResampler::AudioResampler() +{ +} + +AudioResampler::AudioResampler(AudioResamplerPrivate& d):DPTR_INIT(&d) +{ +} + +AudioResampler::~AudioResampler() +{ +} + +QByteArray AudioResampler::outData() const +{ + return d_func().data_out; +} + +bool AudioResampler::prepare() +{ + DPTR_D(AudioResampler); + if (!d.in_channel_layout || !d.in_sample_rate || d.in_sample_format == AV_SAMPLE_FMT_NONE) { + qWarning("src audio parameters in_channel_layout, in_sample_rate, in_sample_format must be set before initialize resampler"); + return false; + } + return true; +} + +bool AudioResampler::convert(const quint8 **data) +{ + return false; +} + +void AudioResampler::setSpeed(qreal speed) +{ + d_func().speed = speed; +} + +qreal AudioResampler::speed() const +{ + return d_func().speed; +} + +void AudioResampler::setInSampes(int samples) +{ + d_func().in_samples = samples; +} + +//channel count can be computed by av_get_channel_layout_nb_channels(chl) +void AudioResampler::setInSampleRate(int isr) +{ + d_func().in_sample_rate = isr; +} + +void AudioResampler::setOutSampleRate(int osr) +{ + d_func().out_sample_rate = osr; +} + +void AudioResampler::setInSampleFormat(int isf) +{ + d_func().in_sample_format = isf; +} + +void AudioResampler::setOutSampleFormat(int osf) +{ + d_func().out_sample_format = osf; +} + +void AudioResampler::setInChannelLayout(int icl) +{ + d_func().in_channel_layout = icl; +} + +void AudioResampler::setOutChannelLayout(int ocl) +{ + d_func().out_channel_layout = ocl; +} + +void AudioResampler::setInChannels(int channels) +{ + d_func().in_nb_channels = channels; +} + +void AudioResampler::setOutChannels(int channels) +{ + d_func().out_nb_channels = channels; +} + +} //namespace QtAV diff --git a/src/AudioResamplerFF.cpp b/src/AudioResamplerFF.cpp new file mode 100644 index 00000000..caa99af2 --- /dev/null +++ b/src/AudioResamplerFF.cpp @@ -0,0 +1,124 @@ + +#include "AudioResampler.h" +#include "private/AudioResampler_p.h" +#include "QtAV/QtAV_Compat.h" +#include "prepost.h" + +namespace QtAV { + +class AudioResamplerFFPrivate; +class AudioResamplerFF : public AudioResampler //Q_EXPORT is not needed +{ + DPTR_DECLARE_PRIVATE(AudioResampler) +public: + AudioResamplerFF(); + virtual bool convert(const quint8** data); + virtual bool prepare(); +}; + +AudioResamplerId AudioResamplerId_FF = 0; +FACTORY_REGISTER_ID_AUTO(AudioResampler, FF, "FFmpeg") + +void RegisterAudioResamplerFF_Man() +{ + FACTORY_REGISTER_ID_MAN(AudioResampler, FF, "FFmpeg") +} + + +class AudioResamplerFFPrivate : public AudioResamplerPrivate +{ +public: + AudioResamplerFFPrivate(): + context(0) + { + } + ~AudioResamplerFFPrivate() { + if (context) { + swr_free(&context); + context = 0; + } + } + SwrContext *context; +}; + +AudioResamplerFF::AudioResamplerFF(): + AudioResampler(*new AudioResamplerFFPrivate()) +{ +} + +bool AudioResamplerFF::convert(const quint8 **data) +{ + DPTR_D(AudioResamplerFF); + /* + * 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. + */ + int64_t a = qMax(d.in_sample_rate, d.out_sample_rate); + int in_samples_per_channel = d.in_samples/d.in_nb_channels; + int out_samples_per_channel = av_rescale_rnd( + swr_get_delay(d.context, a) + + in_samples_per_channel + , d.out_sample_rate, d.in_sample_rate, AV_ROUND_UP); + int out_size = av_samples_get_buffer_size(NULL/*out linesize*/, d.out_nb_channels, out_samples_per_channel, (AVSampleFormat)d.out_sample_format, 0/*alignment default*/); + 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, out_samples_per_channel, data, in_samples_per_channel); + if (converted_samplers_per_channel < 0) { + qWarning("[AudioResamplerFF] %s", av_err2str(converted_samplers_per_channel)); + return false; + } + //converted_samplers_per_channel*d.out_nb_channels*av_get_bytes_per_sample(d.out_sample_format) + //av_samples_get_buffer_size(0, d.out_nb_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*d.out_nb_channels*av_get_bytes_per_sample((AVSampleFormat)d.out_sample_format)); + return true; +} + +/* + *TODO: broken sample rate(AAC), see mplayer + */ +bool AudioResamplerFF::prepare() +{ + DPTR_D(AudioResamplerFF); + if (!d.in_channel_layout || !d.in_sample_rate || d.in_sample_format == AV_SAMPLE_FMT_NONE) { + qWarning("src audio parameters in_channel_layout, in_sample_rate, in_sample_format must be set before initialize resampler"); + return false; + } + if (!d.out_channel_layout) + d.out_channel_layout = d.in_channel_layout; + if (!d.out_sample_rate) + d.out_sample_rate = d.in_sample_rate; + if (d.speed <= 0) + d.speed = 1.0; + if (d.speed != 1.0) + d.out_sample_rate = qreal(d.out_sample_rate)/d.speed; + //d.in_nb_channels = av_get_channel_layout_nb_channels(d.in_channel_layout); + //d.out_nb_channels = av_get_channel_layout_nb_channels(d.out_channel_layout); + if (!d.out_nb_channels) + d.out_nb_channels = d.in_nb_channels; + d.in_planes = av_sample_fmt_is_planar((enum AVSampleFormat)d.in_sample_format) ? d.in_nb_channels : 1; + d.out_planes = av_sample_fmt_is_planar((enum AVSampleFormat)d.out_sample_format) ? d.out_nb_channels : 1; + + //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_channel_layout + , (enum AVSampleFormat)d.out_sample_format, d.out_sample_rate + , d.in_channel_layout + , (enum AVSampleFormat)d.in_sample_format, d.in_sample_rate + , 0 /*log_offset*/, 0 /*log_ctx*/); + if (d.context) { + int ret = swr_init(d.context); + if (!ret) { + qWarning("swr_init failed: %s", av_err2str(ret)); + return false; + } + } else { + qWarning("Allocat swr context failed!"); + return false; + } + return true; +} + +} //namespace QtAV diff --git a/src/QtAV/AVDecoder.h b/src/QtAV/AVDecoder.h index bfcfc22f..4524579c 100644 --- a/src/QtAV/AVDecoder.h +++ b/src/QtAV/AVDecoder.h @@ -42,6 +42,7 @@ public: AVCodecContext* codecContext() const; /*not available if AVCodecContext == 0*/ bool isAvailable() const; + virtual bool prepare(); //if resampler or image converter set, call it virtual bool decode(const QByteArray& encoded) = 0; //decode AVPacket? QByteArray data() const; //decoded data diff --git a/src/QtAV/AudioDecoder.h b/src/QtAV/AudioDecoder.h index 9cbd8d37..1bf940f3 100644 --- a/src/QtAV/AudioDecoder.h +++ b/src/QtAV/AudioDecoder.h @@ -32,6 +32,7 @@ class Q_EXPORT AudioDecoder : public AVDecoder DPTR_DECLARE_PRIVATE(AudioDecoder) public: AudioDecoder(); + virtual bool prepare(); virtual bool decode(const QByteArray &encoded); }; diff --git a/src/QtAV/AudioResampler.h b/src/QtAV/AudioResampler.h new file mode 100644 index 00000000..6eb65979 --- /dev/null +++ b/src/QtAV/AudioResampler.h @@ -0,0 +1,69 @@ +/****************************************************************************** + QtAV: Media play library based on Qt and FFmpeg + Copyright (C) 2013 Wang Bin + +* 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 +******************************************************************************/ + +#ifndef QTAV_AUDIORESAMPLER_H +#define QTAV_AUDIORESAMPLER_H + +#include +#include + +namespace QtAV { + +typedef int AudioResamplerId; +class AudioResampler; +FACTORY_DECLARE(AudioResampler) + +class AudioResamplerPrivate; +class Q_EXPORT AudioResampler //export is required for users who want add their own subclass outside QtAV +{ + DPTR_DECLARE_PRIVATE(AudioResampler) +public: + AudioResampler(); + virtual ~AudioResampler(); + + QByteArray outData() const; + /* check whether the parameters are supported. If not, you should use ff*/ + virtual bool prepare(); //call after all parameters are setted + virtual bool convert(const quint8** data); + //speed: >0, default is 1 + void setSpeed(qreal speed); //out_sample_rate = out_sample_rate/speed + qreal speed() const; + void setInSampes(int samples); + //channel count can be computed by av_get_channel_layout_nb_channels(chl) + void setInSampleRate(int isr); + void setOutSampleRate(int osr); //default is in + //TODO: enum + void setInSampleFormat(int isf); + void setOutSampleFormat(int osf); //set by user. default is in + //TODO: enum + void setInChannelLayout(int icl); + void setOutChannelLayout(int ocl); //default is in + void setInChannels(int channels); + void setOutChannels(int channels); + //Are getter functions required? +protected: + AudioResampler(AudioResamplerPrivate& d); + DPTR_DECLARE(AudioResampler) +}; + +} //namespace QtAV + +#endif // QTAV_AUDIORESAMPLER_H diff --git a/src/QtAV/AudioResamplerTypes.h b/src/QtAV/AudioResamplerTypes.h new file mode 100644 index 00000000..c7b307dd --- /dev/null +++ b/src/QtAV/AudioResamplerTypes.h @@ -0,0 +1,34 @@ +#ifndef QTAV_AUDIORESAMPLERTYPES_H +#define QTAV_AUDIORESAMPLERTYPES_H +/****************************************************************************** + VideoRendererTypes: type id and manually id register function + Copyright (C) 2013 Wang Bin + +* 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 + +namespace QtAV { + +static AudioResamplerId AudioResamplerId_FF = 0; + +Q_EXPORT void AudioResampler_RegisterAll(); + +} //namespace QtAV + +#endif // QTAV_AUDIORESAMPLERTYPES_H diff --git a/src/QtAV/private/AVDecoder_p.h b/src/QtAV/private/AVDecoder_p.h index becd5f38..6107eb89 100644 --- a/src/QtAV/private/AVDecoder_p.h +++ b/src/QtAV/private/AVDecoder_p.h @@ -31,7 +31,11 @@ namespace QtAV { class Q_EXPORT AVDecoderPrivate : public DPtrPrivate { public: - AVDecoderPrivate():codec_ctx(0),frame(0) { + AVDecoderPrivate(): + codec_ctx(0) + , frame(0) + , got_frame_ptr(0) + { frame = avcodec_alloc_frame(); } virtual ~AVDecoderPrivate() { diff --git a/src/QtAV/private/AudioResampler_p.h b/src/QtAV/private/AudioResampler_p.h new file mode 100644 index 00000000..de17f5a6 --- /dev/null +++ b/src/QtAV/private/AudioResampler_p.h @@ -0,0 +1,64 @@ +/****************************************************************************** + QtAV: Media play library based on Qt and FFmpeg + Copyright (C) 2013 Wang Bin + +* 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 +******************************************************************************/ + +#ifndef QTAV_AUDIORESAMPLER_P_H +#define QTAV_AUDIORESAMPLER_P_H + + +#include +#include + +namespace QtAV { + +class AudioResampler; +class AudioResamplerPrivate : public DPtrPrivate +{ +public: + AudioResamplerPrivate(): + in_channel_layout(0) + , out_channel_layout(0) + , in_nb_channels(0) + , out_nb_channels(0) + , in_samples(0) + , in_sample_rate(0) + , out_sample_rate(0) + , in_sample_format(AV_SAMPLE_FMT_NONE) + , out_sample_format(AV_SAMPLE_FMT_FLT) + , in_planes(0) + , out_planes(0) + , speed(1.0) + { + + } + + int in_channel_layout, out_channel_layout; + int in_nb_channels, out_nb_channels; + int in_samples; + int in_sample_rate, out_sample_rate; + int in_sample_format, out_sample_format; //AVSampleFormat + int in_planes, out_planes; + qreal speed; + QByteArray data_out; +}; + +} //namespace QtAV + +#endif // QTAV_AUDIORESAMPLER_P_H diff --git a/src/libQtAV.pro b/src/libQtAV.pro index bd733f0d..a86cf9b9 100644 --- a/src/libQtAV.pro +++ b/src/libQtAV.pro @@ -15,8 +15,7 @@ PROJECTROOT = $$PWD/.. preparePaths($$OUT_PWD/../out) -RESOURCES += \ - ../i18n/QtAV.qrc +RESOURCES += ../i18n/QtAV.qrc win32 { RC_FILE = $${PROJECTROOT}/res/QtAV.rc @@ -149,7 +148,6 @@ SDK_HEADERS *= \ QtAV/dptr.h \ QtAV/QtAV_Global.h \ QtAV/AudioResampler.h \ - QtAV/private/AudioResampler_p.h \ QtAV/AudioDecoder.h \ QtAV/AudioOutput.h \ QtAV/AVDecoder.h \ @@ -189,6 +187,7 @@ HEADERS *= \ QtAV/singleton.h \ QtAV/factory.h \ QtAV/private/AudioOutput_p.h \ + QtAV/private/AudioResampler_p.h \ QtAV/private/AVThread_p.h \ QtAV/private/AVDecoder_p.h \ QtAV/private/AVOutput_p.h \ @@ -201,7 +200,8 @@ HEADERS *= \ QtAV/private/Direct2DRenderer_p.h \ QtAV/private/GLWidgetRenderer_p.h \ QtAV/private/GDIRenderer_p.h \ - QtAV/private/XVRenderer_p.h + QtAV/private/XVRenderer_p.h \ + QtAV/AudioResamplerTypes.h SDK_INCLUDE_FOLDER = QtAV