Commit 26bcbb6f authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Extract frame: process in another frame so we don't block the UI, make sure effects are applied

Related to #1491
parent 4ad3de1b
......@@ -1115,6 +1115,41 @@ std::pair<std::shared_ptr<Mlt::Producer>, bool> ProjectClip::giveMasterAndGetTim
return {std::shared_ptr<Mlt::Producer>(ClipController::mediaUnavailable->cut()), false};
}
void ProjectClip::cloneProducerToFile(const QString &path)
{
Mlt::Consumer c(pCore->getCurrentProfile()->profile(), "xml", path.toUtf8().constData());
Mlt::Service s(m_masterProducer->get_service());
int ignore = s.get_int("ignore_points");
if (ignore) {
s.set("ignore_points", 0);
}
c.connect(s);
c.set("time_format", "frames");
c.set("no_meta", 1);
c.set("no_root", 1);
c.set("no_profile", 1);
c.set("root", "/");
c.set("store", "kdenlive");
c.run();
if (ignore) {
s.set("ignore_points", ignore);
}
if (m_usesProxy) {
QFile file(path);
if (file.open(QIODevice::ReadOnly)) {
QTextStream in(&file);
QString content = in.readAll();
file.close();
content.replace(getProducerProperty(QStringLiteral("resource")), getProducerProperty(QStringLiteral("kdenlive:originalurl")));
if (file.open(QIODevice::WriteOnly)) {
QTextStream out(&file);
out << content;
file.close();
}
}
}
}
std::shared_ptr<Mlt::Producer> ProjectClip::cloneProducer(bool removeEffects)
{
Mlt::Consumer c(pCore->getCurrentProfile()->profile(), "xml", "string");
......
......@@ -200,6 +200,7 @@ public:
std::pair<std::shared_ptr<Mlt::Producer>, bool> giveMasterAndGetTimelineProducer(int clipId, std::shared_ptr<Mlt::Producer> master, PlaylistState::ClipState state, int tid, bool secondPlaylist = false);
std::shared_ptr<Mlt::Producer> cloneProducer(bool removeEffects = false);
void cloneProducerToFile(const QString &path);
static std::shared_ptr<Mlt::Producer> cloneProducer(const std::shared_ptr<Mlt::Producer> &producer);
std::shared_ptr<Mlt::Producer> softClone(const char *list);
/** @brief Returns a clone of the producer, useful for movit clip jobs
......
......@@ -58,6 +58,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QSlider>
#include <QToolButton>
#include <QVBoxLayout>
#include <QtConcurrent>
#include <utility>
......@@ -1162,27 +1163,33 @@ void Monitor::slotExtractCurrentFrame(QString frameName, bool addToProject)
if (dlg->exec() == QDialog::Accepted) {
QString selectedFile = fileWidget->selectedFile();
if (!selectedFile.isEmpty()) {
// Disable monitor preview scaling if any
int previewScale = KdenliveSettings::previewScaling();
if (previewScale > 0) {
KdenliveSettings::setPreviewScaling(0);
m_glMonitor->updateScaling();
if (b != nullptr) {
KdenliveSettings::setExportframe_usingsourceres(b->isChecked());
}
// Create QImage with frame
QImage frame;
KRecentDirs::add(QStringLiteral(":KdenliveFramesFolder"), QUrl::fromLocalFile(selectedFile).adjusted(QUrl::RemoveFilename).toLocalFile());
// check if we are using a proxy
if ((m_controller != nullptr) && !m_controller->getProducerProperty(QStringLiteral("kdenlive:proxy")).isEmpty() &&
m_controller->getProducerProperty(QStringLiteral("kdenlive:proxy")) != QLatin1String("-")) {
// using proxy, use original clip url to get frame
frame = m_glMonitor->getControllerProxy()->extractFrame(m_glMonitor->getCurrentPos(),
m_controller->getProducerProperty(QStringLiteral("kdenlive:originalurl")), -1, -1,
b != nullptr ? b->isChecked() : false);
if (previewScale > 0) {
KdenliveSettings::setPreviewScaling(previewScale);
m_glMonitor->updateScaling();
QTemporaryFile src(QDir::temp().absoluteFilePath(QString("XXXXXX.mlt")));
if (src.open()) {
src.setAutoRemove(false);
m_controller->cloneProducerToFile(src.fileName());
const QStringList pathInfo = {src.fileName(), selectedFile, pCore->bin()->getCurrentFolder()};
QtConcurrent::run(m_glMonitor->getControllerProxy(), &MonitorProxy::extractFrameToFile, m_glMonitor->getCurrentPos(), pathInfo,
addToProject, b != nullptr ? b->isChecked() : false);
}
return;
} else {
if (m_id == Kdenlive::ProjectMonitor) {
// Create QImage with frame
QImage frame;
// Disable monitor preview scaling if any
int previewScale = KdenliveSettings::previewScaling();
if (previewScale > 0) {
KdenliveSettings::setPreviewScaling(0);
m_glMonitor->updateScaling();
}
// Check if we have proxied clips at position
QStringList proxiedClips = pCore->window()->getCurrentTimeline()->model()->getProxiesAt(m_glMonitor->getCurrentPos());
// Temporarily disable proxy on those clips
......@@ -1226,24 +1233,17 @@ void Monitor::slotExtractCurrentFrame(QString frameName, bool addToProject)
}
return;
} else {
frame =
m_glMonitor->getControllerProxy()->extractFrame(m_glMonitor->getCurrentPos(), QString(), -1, -1, b != nullptr ? b->isChecked() : false);
if (previewScale > 0) {
KdenliveSettings::setPreviewScaling(previewScale);
m_glMonitor->updateScaling();
QTemporaryFile src(QDir::temp().absoluteFilePath(QString("XXXXXX.mlt")));
if (src.open()) {
src.setAutoRemove(false);
m_controller->cloneProducerToFile(src.fileName());
const QStringList pathInfo = {src.fileName(), selectedFile, pCore->bin()->getCurrentFolder()};
QtConcurrent::run(m_glMonitor->getControllerProxy(), &MonitorProxy::extractFrameToFile, m_glMonitor->getCurrentPos(), pathInfo,
addToProject, b != nullptr ? b->isChecked() : false);
}
return;
}
}
frame.save(selectedFile);
if (b != nullptr) {
KdenliveSettings::setExportframe_usingsourceres(b->isChecked());
}
KRecentDirs::add(QStringLiteral(":KdenliveFramesFolder"), QUrl::fromLocalFile(selectedFile).adjusted(QUrl::RemoveFilename).toLocalFile());
if (addToProject) {
QString folderInfo = pCore->bin()->getCurrentFolder();
pCore->bin()->droppedUrls(QList<QUrl>{QUrl::fromLocalFile(selectedFile)}, folderInfo);
}
}
}
}
......
......@@ -217,30 +217,70 @@ QPoint MonitorProxy::zone() const
return {m_zoneIn, m_zoneOut};
}
QImage MonitorProxy::extractFrame(int frame_position, const QString &path, int width, int height, bool useSourceProfile)
void MonitorProxy::extractFrameToFile(int frame_position, const QStringList &pathInfo, bool addToProject, bool useSourceProfile)
{
if (width == -1) {
width = pCore->getCurrentProfile()->width();
height = pCore->getCurrentProfile()->height();
} else if (width % 2 == 1) {
width++;
const QString path = pathInfo.at(0);
const QString destPath = pathInfo.at(1);
const QString folderInfo = pathInfo.at(2);
QSize size = pCore->getCurrentFrameSize();
QImage img;
int height = size.height();
int width = size.width();
if (!useSourceProfile) {
Mlt::Frame *frame = q->m_producer->get_frame();
QImage img = KThumb::getFrame(frame, width, height);
delete frame;
img.save(destPath);
if (addToProject) {
QMetaObject::invokeMethod(pCore->bin(), "droppedUrls", Q_ARG(const QList<QUrl> &, {QUrl::fromLocalFile(destPath)}),
Q_ARG(const QString &, folderInfo));
}
return;
}
if (!path.isEmpty()) {
QScopedPointer<Mlt::Profile> tmpProfile(new Mlt::Profile());
QScopedPointer<Mlt::Producer> producer(new Mlt::Producer(*tmpProfile, path.toUtf8().constData()));
QScopedPointer<Mlt::Producer> producer;
QScopedPointer<Mlt::Profile> tmpProfile;
if (useSourceProfile) {
tmpProfile.reset(new Mlt::Profile());
producer.reset(new Mlt::Producer(*tmpProfile, path.toUtf8().constData()));
} else {
producer.reset(new Mlt::Producer(pCore->getCurrentProfile()->profile(), path.toUtf8().constData()));
}
if (producer && producer->is_valid()) {
tmpProfile->from_producer(*producer);
width = tmpProfile->width();
height = tmpProfile->height();
double projectFps = pCore->getCurrentFps();
double currentFps = tmpProfile->fps();
if (!qFuzzyCompare(projectFps, currentFps)) {
frame_position = int(frame_position * currentFps / projectFps);
if (useSourceProfile) {
tmpProfile->from_producer(*producer);
width = tmpProfile->width();
height = tmpProfile->height();
double projectFps = pCore->getCurrentFps();
double currentFps = tmpProfile->fps();
if (!qFuzzyCompare(projectFps, currentFps)) {
frame_position = int(frame_position * currentFps / projectFps);
}
}
QImage img = KThumb::getFrame(producer.data(), frame_position, width, height);
return img;
img.save(destPath);
if (addToProject) {
QMetaObject::invokeMethod(pCore->bin(), "droppedUrls", Q_ARG(const QList<QUrl> &, {QUrl::fromLocalFile(destPath)}),
Q_ARG(const QString &, folderInfo));
}
} else {
qDebug() << "::: INVALID PRODUCER: " << path;
}
if (QDir::temp().exists(path)) {
// This was a temporary playlist file, remove
QFile::remove(path);
}
}
}
QImage MonitorProxy::extractFrame(const QString &path, int width, int height, bool useSourceProfile)
{
if (width == -1) {
width = pCore->getCurrentProfile()->width();
height = pCore->getCurrentProfile()->height();
} else if (width % 2 == 1) {
width++;
}
if ((q->m_producer == nullptr) || !path.isEmpty()) {
QImage pix(width, height, QImage::Format_RGB32);
......
......@@ -94,7 +94,6 @@ public:
void setZone(QPoint zone, bool sendUpdate = true);
void resetZone();
QPoint zone() const;
QImage extractFrame(int frame_position, const QString &path = QString(), int width = -1, int height = -1, bool useSourceProfile = false);
Q_INVOKABLE QString toTimecode(int frames) const;
Q_INVOKABLE void startZoneMove();
Q_INVOKABLE void endZoneMove();
......@@ -105,6 +104,7 @@ public:
Q_INVOKABLE bool seekOnDrop() const;
Q_INVOKABLE void addEffect(const QString &effectData, const QString &effectSource);
QPoint profile();
QImage extractFrame(const QString &path = QString(), int width = -1, int height = -1, bool useSourceProfile = false);
void setClipProperties(int clipId, ClipType::ProducerType type, bool hasAV, const QString &clipName);
void setAudioThumb(const QList <int> &streamIndexes = QList <int>(), const QList <int> &channels = QList <int>());
void setAudioStream(const QString &name);
......@@ -185,4 +185,5 @@ private:
public slots:
void updateClipBounds(const QVector <QPoint>&bounds);
void extractFrameToFile(int frame_position, const QStringList &pathInfo, bool addToProject = false, bool useSourceProfile = false);
};
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment