You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

273 lines
6.8 KiB

/*****************************************************************************
* Copyright (C) 2024 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_access.h>
#include <vlc_stream.h>
#include "vlcaccess_image_provider.hpp"
#include "util/asynctask.hpp"
#include "qt.hpp"
#include <QImageReader>
#include <QFile>
#include <QQmlFile>
#include <QUrlQuery>
#define PATH_KEY "uri"
namespace {
class ImageReader : public AsyncTask<QImage>
{
public:
/**
* @brief ImageReader
* @param device i/o source to read from
*
* @param requestedSize only taken as hint, the Image is resized with PreserveAspectCrop
*
* @param radius
*/
ImageReader(std::unique_ptr<QIODevice> device, QSize requestedSize, VLCAccessImageProvider::ImagePostProcessCb postProcessCb)
: device(std::move(device))
, requestedSize {requestedSize}
, postProcessCb {postProcessCb}
{
}
QString errorString() const {
return errorStr;
}
QImage execute() override
{
QImageReader reader;
reader.setDevice(device.get());
const QSize sourceSize = reader.size();
if (requestedSize.isValid())
reader.setScaledSize(sourceSize.scaled(requestedSize, Qt::KeepAspectRatioByExpanding));
auto img = reader.read();
if (img.isNull()) {
errorStr = reader.errorString();
}
if (!img.isNull() && postProcessCb)
img = postProcessCb(img, requestedSize);
return img;
}
private:
std::unique_ptr<QIODevice> device;
QSize requestedSize;
QString errorStr;
VLCAccessImageProvider::ImagePostProcessCb postProcessCb;
};
class VLCAccessImageResponse : public QQuickImageResponse
{
public:
VLCAccessImageResponse(const QUrl& url, const QSize &requestedSize, VLCAccessImageProvider::ImagePostProcessCb postProcessCb = nullptr)
: imagePostProcessCb(postProcessCb)
{
std::unique_ptr<QIODevice> device;
if (url.scheme().compare(QStringLiteral("qrc"), Qt::CaseInsensitive) == 0)
{
QString qrcPath = QQmlFile::urlToLocalFileOrQrc(url);
device = std::make_unique<QFile>(qrcPath);
}
else
{
QUrl fileUrl = url;
if (fileUrl.scheme().isEmpty())
fileUrl.setScheme("file");
device = std::make_unique<VLCIODevice>(fileUrl.toString(QUrl::FullyEncoded));
}
reader.reset(new ImageReader(std::move(device), requestedSize, postProcessCb));
connect(reader.get(), &ImageReader::result, this, &VLCAccessImageResponse::handleImageRead);
reader->start(*QThreadPool::globalInstance());
}
QQuickTextureFactory *textureFactory() const override
{
return result.isNull() ? nullptr : QQuickTextureFactory::textureFactoryForImage(result);
}
QString errorString() const override
{
return errorStr;
}
private:
void handleImageRead()
{
result = reader->takeResult();
errorStr = reader->errorString();
reader.reset();
emit finished();
}
VLCAccessImageProvider::ImagePostProcessCb imagePostProcessCb;
QImage result;
TaskHandle<ImageReader> reader;
QString errorStr;
};
} // anonymous namespace
//// VLCAccessImageProvider
VLCAccessImageProvider::VLCAccessImageProvider(VLCAccessImageProvider::ImagePostProcessCb cb)
: QQuickAsyncImageProvider()
, postProcessCb(cb)
{
}
QQuickImageResponse* VLCAccessImageProvider::requestImageResponse(const QString& id, const QSize& requestedSize)
{
QUrl url {id};
QUrlQuery query {url};
if (!query.hasQueryItem(PATH_KEY))
return nullptr;
QString vlcurl = query.queryItemValue(PATH_KEY, QUrl::FullyDecoded);
return new VLCAccessImageResponse(QUrl::fromEncoded(vlcurl.toUtf8()), requestedSize, postProcessCb);
}
QString VLCAccessImageProvider::wrapUri(QString path)
{
QUrlQuery query;
query.addQueryItem(PATH_KEY, path);
return QStringLiteral("image://vlcaccess/?") + query.toString(QUrl::FullyEncoded);
}
//// VLCImageAccess
VLCAccessImage::VLCAccessImage(QObject* parent)
: QObject(parent)
{}
QString VLCAccessImage::uri(QString path)
{
return VLCAccessImageProvider::wrapUri(path);
}
//// VLCIODevice
VLCIODevice::VLCIODevice(const QString& filename, QObject* parent)
: QIODevice(parent)
, m_filename(filename)
{
}
VLCIODevice::~VLCIODevice()
{
close();
}
bool VLCIODevice::open(OpenMode mode)
{
//we only support reading
if (mode & QIODevice::OpenModeFlag::WriteOnly)
return false;
m_stream = vlc_access_NewMRL(nullptr, qtu(m_filename));
if (m_stream == nullptr)
return false;
return QIODevice::open(mode);
}
bool VLCIODevice::isSequential() const
{
assert(m_stream);
//some access (like http) will perform really poorly with the way
//Qt uses the QIODevice (lots of seeks)
//return !vlc_stream_CanSeek(m_stream);
return true;
}
void VLCIODevice::close()
{
if (!m_stream)
return;
vlc_stream_Delete(m_stream);
m_stream = nullptr;
}
qint64 VLCIODevice::pos() const
{
assert(m_stream);
return vlc_stream_Tell(m_stream);
}
qint64 VLCIODevice::size() const
{
assert(m_stream);
uint64_t streamSize;
bool ret = vlc_stream_GetSize(m_stream, &streamSize);
if (!ret)
return -1;
return static_cast<qint64>(streamSize);
}
bool VLCIODevice::seek(qint64 pos)
{
assert(m_stream);
QIODevice::seek(pos);
if (pos < 0)
return false;
return vlc_stream_Seek(m_stream, pos) == VLC_SUCCESS;
}
bool VLCIODevice::atEnd() const
{
assert(m_stream);
return vlc_stream_Eof(m_stream);
}
bool VLCIODevice::reset()
{
assert(m_stream);
return vlc_stream_Seek(m_stream, 0) == VLC_SUCCESS;
}
qint64 VLCIODevice::readData(char* data, qint64 maxlen)
{
assert(m_stream);
return vlc_stream_Read(m_stream, data, maxlen);
}
qint64 VLCIODevice::writeData(const char*, qint64)
{
assert(m_stream);
return -1;
}