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

217 lines
5.9 KiB
C++

/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
Initial QAVIOContext.cpp code is from Stefan Ladage <sladage@gmail.com>
* This file is part of QtAV (from 2014)
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 "MediaIO.h"
#include "MediaIO_p.h"
#include "factory.h"
#include <QtCore/QStringList>
#include "Logger.h"
namespace FAV {
FACTORY_DEFINE(MediaIO)
extern bool RegisterMediaIOQIODevice_Man();
extern bool RegisterMediaIOQFile_Man();
extern bool RegisterMediaIOWinRT_Man();
void MediaIO::registerAll()
{
static bool done = false;
if (done)
return;
done = true;
RegisterMediaIOQIODevice_Man();
RegisterMediaIOQFile_Man();
#ifdef Q_OS_WINRT
RegisterMediaIOWinRT_Man();
#endif
}
QStringList MediaIO::builtInNames()
{
static QStringList names;
if (!names.isEmpty())
return names;
std::vector<const char*> ns(MediaIOFactory::Instance().registeredNames());
foreach (const char* n, ns) {
names.append(QLatin1String(n));
}
return names;
}
// TODO: plugin
// TODO: plugin use metadata(Qt plugin system) to avoid loading
MediaIO* MediaIO::createForProtocol(const QString &protocol)
{
std::vector<MediaIOId> ids(MediaIOFactory::Instance().registeredIds());
foreach (MediaIOId id, ids) {
MediaIO *in = MediaIO::create(id);
if (in->protocols().contains(protocol))
return in;
delete in;
}
return 0;
}
MediaIO* MediaIO::createForUrl(const QString &url)
{
const int p = url.indexOf(QLatin1String(":"));
if (p < 0)
return 0;
MediaIO *io = MediaIO::createForProtocol(url.left(p));
if (!io)
return 0;
io->setUrl(url);
return io;
}
static int av_read(void *opaque, unsigned char *buf, int buf_size)
{
MediaIO* io = static_cast<MediaIO*>(opaque);
return io->read((char*)buf, buf_size);
}
static int av_write(void *opaque, unsigned char *buf, int buf_size)
{
MediaIO* io = static_cast<MediaIO*>(opaque);
return io->write((const char*)buf, buf_size);
}
static int64_t av_seek(void *opaque, int64_t offset, int whence)
{
if (whence == SEEK_SET && offset < 0)
return -1;
MediaIO* io = static_cast<MediaIO*>(opaque);
if (!io->isSeekable()) {
qWarning("Can not seek. MediaIO[%s] is not a seekable IO", MediaIO::staticMetaObject.className());
return -1;
}
if (whence == AVSEEK_SIZE) {
// return the filesize without seeking anywhere. Supporting this is optional.
return io->size() > 0 ? io->size() : 0;
}
if (!io->seek(offset, whence))
return -1;
return io->position();
}
MediaIO::MediaIO(QObject *parent)
: QObject(parent)
{}
MediaIO::MediaIO(MediaIOPrivate &d, QObject *parent)
: QObject(parent)
, DPTR_INIT(&d)
{}
MediaIO::~MediaIO()
{
release();
}
void MediaIO::setUrl(const QString &url)
{
DPTR_D(MediaIO);
if (d.url == url)
return;
// TODO: check protocol
d.url = url;
onUrlChanged();
}
QString MediaIO::url() const
{
return d_func().url;
}
void MediaIO::onUrlChanged()
{}
bool MediaIO::setAccessMode(AccessMode value)
{
DPTR_D(MediaIO);
if (d.mode == value)
return true;
if (value == Write && !isWritable()) {
qWarning("Can not set Write access mode to this MediaIO");
return false;
}
d.mode = value;
return true;
}
MediaIO::AccessMode MediaIO::accessMode() const
{
return d_func().mode;
}
const QStringList& MediaIO::protocols() const
{
static QStringList no_protocols;
return no_protocols;
}
#define IODATA_BUFFER_SIZE 32768 // TODO: user configurable
static const int kBufferSizeDefault = 32768;
void MediaIO::setBufferSize(int value)
{
DPTR_D(MediaIO);
if (d.buffer_size == value)
return;
d.buffer_size = value;
}
int MediaIO::bufferSize() const
{
return d_func().buffer_size;
}
void* MediaIO::avioContext()
{
DPTR_D(MediaIO);
if (d.ctx)
return d.ctx;
// buffer will be released in av_probe_input_buffer2=>ffio_rewind_with_probe_data. always is? may be another context
unsigned char* buf = (unsigned char*)av_malloc(IODATA_BUFFER_SIZE);
// open for write if 1. SET 0 if open for read otherwise data ptr in av_read(data, ...) does not change
const int write_flag = (accessMode() == Write) && isWritable();
d.ctx = avio_alloc_context(buf, bufferSize() > 0 ? bufferSize() : kBufferSizeDefault, write_flag, this, &av_read, write_flag ? &av_write : NULL, &av_seek);
// if seekable==false, containers that estimate duration from pts(or bit rate) will not seek to the last frame when computing duration
// but it's still seekable if call seek outside(e.g. from demuxer)
// TODO: isVariableSize: size = -real_size
d.ctx->seekable = isSeekable() && !isVariableSize() ? AVIO_SEEKABLE_NORMAL : 0;
return d.ctx;
}
void MediaIO::release()
{
DPTR_D(MediaIO);
if (!d.ctx)
return;
// avio_close is called by avformat_close_input. here we only allocate but no open
av_freep(&d.ctx->buffer);
av_freep(&d.ctx);
}
} //namespace FAV