Files
fmviewer3/project/fm_viewer/fav/AudioFrame.cpp
2026-02-21 17:11:31 +09:00

239 lines
6.6 KiB
C++

/******************************************************************************
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