first commit
This commit is contained in:
238
project/fm_viewer/fav/AudioFrame.cpp
Normal file
238
project/fm_viewer/fav/AudioFrame.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
/******************************************************************************
|
||||
QtAV: Multimedia framework based on Qt and FFmpeg
|
||||
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
|
||||
|
||||
* 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 "AudioFrame.h"
|
||||
#include "Frame_p.h"
|
||||
#include "AudioResampler.h"
|
||||
#include "AVCompat.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace FAV {
|
||||
namespace{
|
||||
static const struct RegisterMetaTypes
|
||||
{
|
||||
inline RegisterMetaTypes() {
|
||||
qRegisterMetaType<FAV::AudioFrame>("FAV::AudioFrame");
|
||||
}
|
||||
} _registerMetaTypes;
|
||||
}
|
||||
|
||||
class AudioFramePrivate : public FramePrivate
|
||||
{
|
||||
public:
|
||||
AudioFramePrivate(const AudioFormat& fmt)
|
||||
: FramePrivate()
|
||||
, format(fmt)
|
||||
, samples_per_ch(0)
|
||||
, conv(0)
|
||||
{
|
||||
if (!format.isValid())
|
||||
return;
|
||||
const int nb_planes(format.planeCount());
|
||||
planes.reserve(nb_planes);
|
||||
planes.resize(nb_planes);
|
||||
line_sizes.reserve(nb_planes);
|
||||
line_sizes.resize(nb_planes);
|
||||
}
|
||||
|
||||
AudioFormat format;
|
||||
int samples_per_ch;
|
||||
AudioResampler *conv;
|
||||
};
|
||||
|
||||
/*!
|
||||
Constructs a shallow copy of \a other. Since AudioFrame is
|
||||
explicitly shared, these two instances will reflect the same frame.
|
||||
|
||||
*/
|
||||
AudioFrame::AudioFrame(const AudioFrame &other)
|
||||
: Frame(other)
|
||||
{
|
||||
}
|
||||
|
||||
AudioFrame::AudioFrame(const AudioFormat &format, const QByteArray& data)
|
||||
: Frame(new AudioFramePrivate(format))
|
||||
{
|
||||
if (data.isEmpty())
|
||||
return;
|
||||
Q_D(AudioFrame);
|
||||
d->format = format;
|
||||
d->data = data;
|
||||
if (!d->format.isValid())
|
||||
return;
|
||||
if (d->data.isEmpty())
|
||||
return;
|
||||
d->samples_per_ch = data.size() / d->format.channels() / d->format.bytesPerSample();
|
||||
const int nb_planes(d->format.planeCount());
|
||||
const int bpl(d->data.size()/nb_planes);
|
||||
for (int i = 0; i < nb_planes; ++i) {
|
||||
setBytesPerLine(bpl, i);
|
||||
setBits((uchar*)d->data.constData() + i*bpl, i);
|
||||
}
|
||||
//init();
|
||||
}
|
||||
|
||||
/*!
|
||||
Assigns the contents of \a other to this video frame. Since AudioFrame is
|
||||
explicitly shared, these two instances will reflect the same frame.
|
||||
|
||||
*/
|
||||
AudioFrame &AudioFrame::operator =(const AudioFrame &other)
|
||||
{
|
||||
d_ptr = other.d_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AudioFrame::~AudioFrame()
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioFrame::isValid() const
|
||||
{
|
||||
Q_D(const AudioFrame);
|
||||
return d->samples_per_ch > 0 && d->format.isValid();
|
||||
}
|
||||
|
||||
QByteArray AudioFrame::data()
|
||||
{
|
||||
if (!isValid())
|
||||
return QByteArray();
|
||||
Q_D(AudioFrame);
|
||||
if (d->data.isEmpty()) {
|
||||
AudioFrame a(clone());
|
||||
d->data = a.data();
|
||||
}
|
||||
return d->data;
|
||||
}
|
||||
|
||||
int AudioFrame::channelCount() const
|
||||
{
|
||||
Q_D(const AudioFrame);
|
||||
if (!d->format.isValid())
|
||||
return 0;
|
||||
return d->format.channels();
|
||||
}
|
||||
|
||||
AudioFrame AudioFrame::clone() const
|
||||
{
|
||||
Q_D(const AudioFrame);
|
||||
if (d->format.sampleFormatFFmpeg() == AV_SAMPLE_FMT_NONE
|
||||
|| d->format.channels() <= 0)
|
||||
return AudioFrame();
|
||||
if (d->samples_per_ch <= 0 || bytesPerLine(0) <= 0)
|
||||
return AudioFrame(format());
|
||||
QByteArray buf(bytesPerLine()*planeCount(), 0);
|
||||
char *dst = buf.data(); //must before buf is shared, otherwise data will be detached.
|
||||
for (int i = 0; i < planeCount(); ++i) {
|
||||
const int plane_size = bytesPerLine(i);
|
||||
memcpy(dst, constBits(i), plane_size);
|
||||
dst += plane_size;
|
||||
}
|
||||
AudioFrame f(d->format, buf);
|
||||
f.setSamplesPerChannel(samplesPerChannel());
|
||||
f.setPTS(pts()); // timestamp()
|
||||
f.setDuration(duration());
|
||||
// meta data?
|
||||
return f;
|
||||
}
|
||||
|
||||
AudioFormat AudioFrame::format() const
|
||||
{
|
||||
return d_func()->format;
|
||||
}
|
||||
|
||||
void AudioFrame::setSamplesPerChannel(int samples)
|
||||
{
|
||||
Q_D(AudioFrame);
|
||||
if (!d->format.isValid()) {
|
||||
qWarning() << "can not set spc for an invalid format: " << d->format;
|
||||
return;
|
||||
}
|
||||
d->samples_per_ch = samples;
|
||||
const int nb_planes = d->format.planeCount();
|
||||
const int bpl(d->line_sizes[0] > 0 ? d->line_sizes[0] : d->samples_per_ch*d->format.bytesPerSample() * (d->format.isPlanar() ? 1 : d->format.channels()));
|
||||
for (int i = 0; i < nb_planes; ++i) {
|
||||
setBytesPerLine(bpl, i);
|
||||
}
|
||||
if (d->data.isEmpty())
|
||||
return;
|
||||
if (!constBits(0)) {
|
||||
setBits((quint8*)d->data.constData(), 0);
|
||||
}
|
||||
for (int i = 1; i < nb_planes; ++i) {
|
||||
if (!constBits(i)) {
|
||||
setBits((uchar*)constBits(i-1) + bpl, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int AudioFrame::samplesPerChannel() const
|
||||
{
|
||||
return d_func()->samples_per_ch;
|
||||
}
|
||||
|
||||
void AudioFrame::setAudioResampler(AudioResampler *conv)
|
||||
{
|
||||
d_func()->conv = conv;
|
||||
}
|
||||
|
||||
qint64 AudioFrame::duration() const
|
||||
{
|
||||
Q_D(const AudioFrame);
|
||||
return d->format.durationForBytes(d->data.size());
|
||||
}
|
||||
|
||||
AudioFrame AudioFrame::to(const AudioFormat &fmt) const
|
||||
{
|
||||
if (!isValid() || !constBits(0))
|
||||
return AudioFrame();
|
||||
//if (fmt == format())
|
||||
// return clone(); //FIXME: clone a frame from ffmpeg is not enough?
|
||||
Q_D(const AudioFrame);
|
||||
// TODO: use a pool
|
||||
AudioResampler *conv = d->conv;
|
||||
QScopedPointer<AudioResampler> c;
|
||||
if (!conv) {
|
||||
conv = AudioResampler::create(AudioResamplerId_FF);
|
||||
if (!conv)
|
||||
conv = AudioResampler::create(AudioResamplerId_Libav);
|
||||
if (!conv) {
|
||||
qWarning("no audio resampler is available");
|
||||
return AudioFrame();
|
||||
}
|
||||
c.reset(conv);
|
||||
}
|
||||
conv->setInAudioFormat(format());
|
||||
conv->setOutAudioFormat(fmt);
|
||||
//conv->prepare(); // already called in setIn/OutFormat
|
||||
conv->setInSampesPerChannel(samplesPerChannel()); //TODO
|
||||
if (!conv->convert((const quint8**)d->planes.constData())) {
|
||||
qWarning() << "AudioFrame::to error: " << format() << "=>" << fmt;
|
||||
return AudioFrame();
|
||||
}
|
||||
AudioFrame f(fmt, conv->outData());
|
||||
f.setSamplesPerChannel(conv->outSamplesPerChannel());
|
||||
f.setPTS(pts()); // (timestamp()
|
||||
f.setDuration(duration());
|
||||
f.d_ptr->metadata = d->metadata; // need metadata?
|
||||
return f;
|
||||
}
|
||||
} //namespace FAV
|
||||
Reference in New Issue
Block a user