/***************************************************************************
krender.cpp - description
-------------------
begin : Fri Nov 22 2002
copyright : (C) 2002 by Jason Wood
email : jasonwood@blueyonder.co.uk
copyright : (C) 2005 Lucio Flavio Correa
email : lucio.correa@gmail.com
copyright : (C) Marco Gittler
email : g.marco@freenet.de
copyright : (C) 2006 Jean-Baptiste Mardelle
email : jb@kdenlive.org
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "renderer.h"
#include "kdenlivesettings.h"
#include "doc/kthumb.h"
#include "definitions.h"
#include "project/dialogs/slideshowclip.h"
#include "dialogs/profilesdialog.h"
#include "mltcontroller/bincontroller.h"
#include "bin/projectclip.h"
#include "timeline/clip.h"
#include "monitor/glwidget.h"
#include "mltcontroller/clipcontroller.h"
#include "timeline/transitionhandler.h"
#include "core.h"
#include
#include "kdenlive_debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SEEK_INACTIVE (-1)
Render::Render(Kdenlive::MonitorId rendererName, BinController *binController, GLWidget *qmlView, QWidget *parent) :
AbstractRender(rendererName, parent),
byPassSeek(false),
requestedSeekPosition(SEEK_INACTIVE),
showFrameSemaphore(1),
externalConsumer(false),
m_name(rendererName),
m_mltConsumer(nullptr),
m_mltProducer(nullptr),
m_showFrameEvent(nullptr),
m_pauseEvent(nullptr),
m_binController(binController),
m_qmlView(qmlView),
m_isZoneMode(false),
m_isLoopMode(false),
m_blackClip(nullptr),
m_isActive(false),
m_isRefreshing(false)
{
qRegisterMetaType ("stringMap");
analyseAudio = KdenliveSettings::monitor_audio();
//buildConsumer();
if (m_qmlView) {
m_blackClip = new Mlt::Producer(*m_qmlView->profile(), "colour:black");
m_blackClip->set("id", "black");
m_blackClip->set("mlt_type", "producer");
m_blackClip->set("aspect_ratio", 1);
m_blackClip->set("set.test_audio", 0);
m_mltProducer = m_blackClip->cut(0, 1);
m_qmlView->setProducer(m_mltProducer);
m_mltConsumer = qmlView->consumer();
}
/*m_mltConsumer->connect(*m_mltProducer);
m_mltProducer->set_speed(0.0);*/
m_refreshTimer.setSingleShot(true);
m_refreshTimer.setInterval(50);
connect(&m_refreshTimer, &QTimer::timeout, this, &Render::refresh);
connect(this, &Render::checkSeeking, this, &Render::slotCheckSeeking);
if (m_name == Kdenlive::ProjectMonitor) {
connect(m_binController, &BinController::prepareTimelineReplacement, this, &Render::prepareTimelineReplacement, Qt::DirectConnection);
connect(m_binController, &BinController::replaceTimelineProducer, this, &Render::replaceTimelineProducer, Qt::DirectConnection);
connect(m_binController, &BinController::updateTimelineProducer, this, &Render::updateTimelineProducer);
connect(m_binController, &BinController::setDocumentNotes, this, &Render::setDocumentNotes);
}
}
Render::~Render()
{
closeMlt();
}
void Render::closeMlt()
{
delete m_showFrameEvent;
delete m_pauseEvent;
delete m_mltConsumer;
delete m_mltProducer;
delete m_blackClip;
}
void Render::slotSwitchFullscreen()
{
if (m_mltConsumer) {
m_mltConsumer->set("full_screen", 1);
}
}
void Render::prepareProfileReset(double fps)
{
m_refreshTimer.stop();
m_fps = fps;
}
void Render::finishProfileReset()
{
delete m_blackClip;
m_blackClip = new Mlt::Producer(*m_qmlView->profile(), "colour:black");
m_blackClip->set("id", "black");
m_blackClip->set("mlt_type", "producer");
m_blackClip->set("aspect_ratio", 1);
m_blackClip->set("set.test_audio", 0);
}
void Render::seek(const GenTime &time)
{
if (!m_mltProducer || !m_isActive) {
return;
}
int pos = time.frames(m_fps);
seek(pos);
}
void Render::silentSeek(int time)
{
if (m_isActive) {
seek(time);
return;
}
m_mltProducer->seek(time);
m_mltConsumer->set("refresh", 1);
}
void Render::seek(int time)
{
resetZoneMode();
time = qBound(0, time, m_mltProducer->get_length() - 1);
if (requestedSeekPosition == SEEK_INACTIVE) {
requestedSeekPosition = time;
if (m_mltProducer->get_speed() != 0) {
m_mltConsumer->purge();
}
m_mltProducer->seek(time);
if (!externalConsumer) {
m_isRefreshing = true;
if (m_mltConsumer->is_stopped()) {
m_mltConsumer->start();
}
m_mltConsumer->set("refresh", 1);
}
} else {
requestedSeekPosition = time;
}
}
int Render::frameRenderWidth() const
{
return m_qmlView->profile()->width();
}
int Render::renderWidth() const
{
return (int)(m_qmlView->profile()->height() * m_qmlView->profile()->dar() + 0.5);
}
int Render::renderHeight() const
{
return m_qmlView->profile()->height();
}
QImage Render::extractFrame(int frame_position, const QString &path, int width, int height)
{
if (width == -1) {
width = frameRenderWidth();
height = renderHeight();
} else if (width % 2 == 1) {
width++;
}
if (!path.isEmpty()) {
QScopedPointer producer(new Mlt::Producer(*m_qmlView->profile(), path.toUtf8().constData()));
if (producer && producer->is_valid()) {
QImage img = KThumb::getFrame(producer.data(), frame_position, width, height);
return img;
}
}
if (!m_mltProducer || !path.isEmpty()) {
QImage pix(width, height, QImage::Format_RGB32);
pix.fill(Qt::black);
return pix;
}
Mlt::Frame *frame = nullptr;
QImage img;
bool profileFromSource = m_mltProducer->get_int("meta.media.width") > width;
if (KdenliveSettings::gpu_accel() && !profileFromSource) {
QString service = m_mltProducer->get("mlt_service");
QScopedPointer tmpProd(new Mlt::Producer(*m_qmlView->profile(), service.toUtf8().constData(), m_mltProducer->get("resource")));
Mlt::Filter scaler(*m_qmlView->profile(), "swscale");
Mlt::Filter converter(*m_qmlView->profile(), "avcolor_space");
tmpProd->attach(scaler);
tmpProd->attach(converter);
tmpProd->seek(m_mltProducer->position());
frame = tmpProd->get_frame();
img = KThumb::getFrame(frame, width, height);
delete frame;
} else if (profileFromSource) {
// Our source clip's resolution is higher than current profile, export at full res
QScopedPointer tmpProfile(new Mlt::Profile());
QString service = m_mltProducer->get("mlt_service");
QScopedPointer tmpProd(new Mlt::Producer(*tmpProfile, service.toUtf8().constData(), m_mltProducer->get("resource")));
tmpProfile->from_producer(*tmpProd);
width = tmpProfile->width();
height = tmpProfile->height();
if (tmpProd && tmpProd->is_valid()) {
Mlt::Filter scaler(*tmpProfile, "swscale");
Mlt::Filter converter(*tmpProfile, "avcolor_space");
tmpProd->attach(scaler);
tmpProd->attach(converter);
//Clip clp2(*m_mltProducer);
Clip(*tmpProd).addEffects(*m_mltProducer);
tmpProd->seek(m_mltProducer->position());
frame = tmpProd->get_frame();
img = KThumb::getFrame(frame, width, height);
delete frame;
}
} else {
frame = m_mltProducer->get_frame();
img = KThumb::getFrame(frame, width, height);
delete frame;
}
return img;
}
int Render::getLength()
{
if (m_mltProducer) {
return mlt_producer_get_playtime(m_mltProducer->get_producer());
}
return 0;
}
bool Render::isValid(const QUrl &url)
{
Mlt::Producer producer(*m_qmlView->profile(), url.toLocalFile().toUtf8().constData());
if (producer.is_blank()) {
return false;
}
return true;
}
double Render::dar() const
{
return m_qmlView->profile()->dar();
}
double Render::sar() const
{
return m_qmlView->profile()->sar();
}
#if 0
/** Create the producer from the MLT XML QDomDocument */
void Render::initSceneList()
{
//qCDebug(KDENLIVE_LOG) << "-------- INIT SCENE LIST ------_";
QDomDocument doc;
QDomElement mlt = doc.createElement("mlt");
doc.appendChild(mlt);
QDomElement prod = doc.createElement("producer");
prod.setAttribute("resource", "colour");
prod.setAttribute("colour", "red");
prod.setAttribute("id", "black");
prod.setAttribute("in", "0");
prod.setAttribute("out", "0");
QDomElement tractor = doc.createElement("tractor");
QDomElement multitrack = doc.createElement("multitrack");
QDomElement playlist1 = doc.createElement("playlist");
playlist1.appendChild(prod);
multitrack.appendChild(playlist1);
QDomElement playlist2 = doc.createElement("playlist");
multitrack.appendChild(playlist2);
QDomElement playlist3 = doc.createElement("playlist");
multitrack.appendChild(playlist3);
QDomElement playlist4 = doc.createElement("playlist");
multitrack.appendChild(playlist4);
QDomElement playlist5 = doc.createElement("playlist");
multitrack.appendChild(playlist5);
tractor.appendChild(multitrack);
mlt.appendChild(tractor);
// //qCDebug(KDENLIVE_LOG)<");*/
setSceneList(doc, 0);
}
#endif
void Render::loadUrl(const QString &url)
{
Mlt::Producer *producer = new Mlt::Producer(*m_qmlView->profile(), url.toUtf8().constData());
setProducer(producer, 0, true);
}
bool Render::updateProducer(Mlt::Producer *producer)
{
if (m_mltProducer) {
if (strcmp(m_mltProducer->get("resource"), "") == 0) {
// We need to make some cleanup
Mlt::Tractor trac(*m_mltProducer);
for (int i = 0; i < trac.count(); i++) {
trac.set_track(*m_blackClip, i);
}
}
delete m_mltProducer;
m_mltProducer = nullptr;
}
if (m_mltConsumer) {
if (!m_mltConsumer->is_stopped()) {
m_mltConsumer->stop();
}
}
if (!producer || !producer->is_valid()) {
return false;
}
m_fps = producer->get_fps();
m_mltProducer = producer;
if (m_qmlView) {
m_qmlView->setProducer(producer);
m_mltConsumer = m_qmlView->consumer();
}
return true;
}
bool Render::setProducer(Mlt::Producer *producer, int position, bool isActive)
{
m_refreshTimer.stop();
requestedSeekPosition = SEEK_INACTIVE;
QMutexLocker locker(&m_mutex);
QString currentId;
int consumerPosition = 0;
if (!producer && m_mltProducer && m_mltProducer->parent().get("id") == QLatin1String("black")) {
// Black clip already displayed no need to refresh
return true;
}
if (m_mltProducer) {
currentId = m_mltProducer->get("id");
m_mltProducer->set_speed(0);
if (QString(m_mltProducer->get("resource")) == QLatin1String("")) {
// We need to make some cleanup
Mlt::Tractor trac(*m_mltProducer);
for (int i = 0; i < trac.count(); i++) {
trac.set_track(*m_blackClip, i);
}
}
delete m_mltProducer;
m_mltProducer = nullptr;
}
if (m_mltConsumer) {
if (!m_mltConsumer->is_stopped()) {
isActive = true;
m_mltConsumer->stop();
}
consumerPosition = m_mltConsumer->position();
}
blockSignals(true);
if (!producer || !producer->is_valid()) {
producer = m_blackClip->cut(0, 1);
}
emit stopped();
if (position == -1 && producer->get("id") == currentId) {
position = consumerPosition;
}
if (position != -1) {
producer->seek(position);
}
m_fps = producer->get_fps();
blockSignals(false);
m_mltProducer = producer;
m_mltProducer->set_speed(0);
if (m_qmlView) {
m_qmlView->setProducer(producer);
m_mltConsumer = m_qmlView->consumer();
//m_mltConsumer->set("refresh", 1);
}
//m_mltConsumer->connect(*producer);
if (isActive) {
startConsumer();
}
emit durationChanged(m_mltProducer->get_length() - 1, m_mltProducer->get_in());
position = m_mltProducer->position();
emit rendererPosition(position);
return true;
}
void Render::startConsumer()
{
if (m_mltConsumer->is_stopped() && m_mltConsumer->start() == -1) {
// ARGH CONSUMER BROKEN!!!!
KMessageBox::error(qApp->activeWindow(), i18n("Could not create the video preview window.\nThere is something wrong with your Kdenlive install or your driver settings, please fix it."));
if (m_showFrameEvent) {
delete m_showFrameEvent;
}
m_showFrameEvent = nullptr;
if (m_pauseEvent) {
delete m_pauseEvent;
}
m_pauseEvent = nullptr;
delete m_mltConsumer;
m_mltConsumer = nullptr;
return;
}
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
m_isActive = true;
}
int Render::setSceneList(const QDomDocument &list, int position)
{
return setSceneList(list.toString(), position);
}
int Render::setSceneList(QString playlist, int position)
{
requestedSeekPosition = SEEK_INACTIVE;
m_refreshTimer.stop();
QMutexLocker locker(&m_mutex);
//if (m_winid == -1) return -1;
int error = 0;
//qCDebug(KDENLIVE_LOG) << "////// RENDER, SET SCENE LIST:\n" << playlist <<"\n..........:::.";
// Remove previous profile info
QDomDocument doc;
doc.setContent(playlist);
QDomElement profile = doc.documentElement().firstChildElement(QStringLiteral("profile"));
doc.documentElement().removeChild(profile);
playlist = doc.toString();
if (m_mltConsumer) {
if (!m_mltConsumer->is_stopped()) {
m_mltConsumer->stop();
}
} else {
qCWarning(KDENLIVE_LOG) << "/////// ERROR, TRYING TO USE nullptr MLT CONSUMER";
error = -1;
}
if (m_mltProducer) {
m_mltProducer->set_speed(0);
qDeleteAll(m_slowmotionProducers);
m_slowmotionProducers.clear();
delete m_mltProducer;
m_mltProducer = nullptr;
emit stopped();
}
m_binController->destroyBin();
blockSignals(true);
m_locale = QLocale();
m_locale.setNumberOptions(QLocale::OmitGroupSeparator);
m_mltProducer = new Mlt::Producer(*m_qmlView->profile(), "xml-string", playlist.toUtf8().constData());
//m_mltProducer = new Mlt::Producer(*m_qmlView->profile(), "xml-nogl-string", playlist.toUtf8().constData());
if (!m_mltProducer || !m_mltProducer->is_valid()) {
qCDebug(KDENLIVE_LOG) << " WARNING - - - - -INVALID PLAYLIST: " << playlist.toUtf8().constData();
m_mltProducer = m_blackClip->cut(0, 1);
error = -1;
}
m_mltProducer->set("eof", "pause");
checkMaxThreads();
m_mltProducer->optimise();
m_fps = m_mltProducer->get_fps();
if (position != 0) {
// Seek to correct place after opening project.
m_mltProducer->seek(position);
}
// init MLT's document root, useful to find full urls
m_binController->setDocumentRoot(doc.documentElement().attribute(QStringLiteral("root")));
// Fill Bin's playlist
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
}
blockSignals(false);
Mlt::Tractor tractor(service);
Mlt::Properties retainList((mlt_properties) tractor.get_data("xml_retain"));
if (retainList.is_valid() && retainList.get_data(m_binController->binPlaylistId().toUtf8().constData())) {
Mlt::Playlist playlist((mlt_playlist) retainList.get_data(m_binController->binPlaylistId().toUtf8().constData()));
if (playlist.is_valid() && playlist.type() == playlist_type) {
// Load bin clips
m_binController->initializeBin(playlist);
}
}
// No Playlist found, create new one
if (m_qmlView) {
m_binController->createIfNeeded(m_qmlView->profile());
QString retain = QStringLiteral("xml_retain %1").arg(m_binController->binPlaylistId());
tractor.set(retain.toUtf8().constData(), m_binController->service(), 0);
//if (!m_binController->hasClip("black")) m_binController->addClipToBin("black", *m_blackClip);
m_qmlView->setProducer(m_mltProducer);
m_mltConsumer = m_qmlView->consumer();
}
//qCDebug(KDENLIVE_LOG) << "// NEW SCENE LIST DURATION SET TO: " << m_mltProducer->get_playtime();
//m_mltConsumer->connect(*m_mltProducer);
m_mltProducer->set_speed(0);
fillSlowMotionProducers();
emit durationChanged(m_mltProducer->get_playtime() - 1);
// Fill bin
const QStringList ids = m_binController->getClipIds();
for (const QString &id : ids) {
if (id == QLatin1String("black")) {
continue;
}
// pass basic info, the others (folder, etc) will be taken from the producer itself
requestClipInfo info;
info.imageHeight = 0;
info.clipId = id;
info.replaceProducer = true;
emit gotFileProperties(info, m_binController->getController(id));
}
////qCDebug(KDENLIVE_LOG)<<"// SETSCN LST, POS: "<parent().get_service());
if (service.type() != tractor_type) {
qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM" << m_mltProducer->parent().get("mlt_service");
return;
}
Mlt::Tractor tractor(service);
int mltMaxThreads = mlt_service_cache_get_size(service.get_service(), "producer_avformat");
int requestedThreads = tractor.count() + m_qmlView->realTime() + 2;
if (requestedThreads > mltMaxThreads) {
mlt_service_cache_set_size(service.get_service(), "producer_avformat", requestedThreads);
//qCDebug(KDENLIVE_LOG)<<"// MLT threads updated to: "<profile(), "xml:kdenlive_playlist");
if (!root.isEmpty()) {
xmlConsumer.set("root", root.toUtf8().constData());
}
//qCDebug(KDENLIVE_LOG)<<" ++ + READY TO SAVE: "<profile()->width()<<" / "<profile()->description();
if (!xmlConsumer.is_valid()) {
return QString();
}
m_mltProducer->optimise();
xmlConsumer.set("terminate_on_pause", 1);
xmlConsumer.set("store", "kdenlive");
// Disabling meta creates cleaner files, but then we don't have access to metadata on the fly (meta channels, etc)
// And we must use "avformat" instead of "avformat-novalidate" on project loading which causes a big delay on project opening
//xmlConsumer.set("no_meta", 1);
Mlt::Producer prod(m_mltProducer->get_producer());
if (!prod.is_valid()) {
return QString();
}
xmlConsumer.connect(prod);
xmlConsumer.run();
playlist = QString::fromUtf8(xmlConsumer.get("kdenlive_playlist"));
return playlist;
}
void Render::saveZone(const QString &projectFolder, QPoint zone)
{
QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
if (clipFolder.isEmpty()) {
clipFolder = QDir::homePath();
}
QString url = QFileDialog::getSaveFileName(qApp->activeWindow(), i18n("Save Zone"), clipFolder, i18n("MLT playlist (*.mlt)"));
if (url.isEmpty()) {
return;
}
Mlt::Consumer xmlConsumer(*m_qmlView->profile(), ("xml:" + url).toUtf8().constData());
xmlConsumer.set("terminate_on_pause", 1);
m_mltProducer->optimise();
qCDebug(KDENLIVE_LOG) << " - - -- - SAVE ZONE SERVICE: " << m_mltProducer->get("mlt_type");
if (QString(m_mltProducer->get("mlt_type")) != QLatin1String("producer")) {
// TODO: broken
QString scene = sceneList(projectFolder);
Mlt::Producer duplicate(*m_mltProducer->profile(), "xml-string", scene.toUtf8().constData());
duplicate.set_in_and_out(zone.x(), zone.y());
qCDebug(KDENLIVE_LOG) << "/// CUT: " << zone.x() << "x" << zone.y() << " / " << duplicate.get_length();
xmlConsumer.connect(duplicate);
xmlConsumer.run();
} else {
Mlt::Producer prod(m_mltProducer->get_producer());
Mlt::Producer *prod2 = prod.cut(zone.x(), zone.y());
Mlt::Playlist list(*m_mltProducer->profile());
list.insert_at(0, *prod2, 0);
//list.set("title", desc.toUtf8().constData());
xmlConsumer.connect(list);
xmlConsumer.run();
delete prod2;
}
}
double Render::fps() const
{
return m_fps;
}
int Render::volume() const
{
if (!m_mltConsumer || !m_mltProducer) {
return -1;
}
if (m_mltConsumer->get("mlt_service") == QStringLiteral("multi")) {
return ((int) 100 * m_mltConsumer->get_double("0.volume"));
}
return ((int) 100 * m_mltConsumer->get_double("volume"));
}
void Render::start()
{
m_refreshTimer.stop();
QMutexLocker locker(&m_mutex);
/*if (m_winid == -1) {
//qCDebug(KDENLIVE_LOG) << "----- BROKEN MONITOR: " << m_name << ", RESTART";
return;
}*/
if (!m_mltConsumer) {
//qCDebug(KDENLIVE_LOG)<<" / - - - STARTED BEFORE CONSUMER!!!";
return;
}
if (m_mltConsumer->is_stopped()) {
if (m_mltConsumer->start() == -1) {
//KMessageBox::error(qApp->activeWindow(), i18n("Could not create the video preview window.\nThere is something wrong with your Kdenlive install or your driver settings, please fix it."));
qCWarning(KDENLIVE_LOG) << "/ / / / CANNOT START MONITOR";
} else {
m_mltConsumer->purge();
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
}
}
}
void Render::stop()
{
requestedSeekPosition = SEEK_INACTIVE;
m_refreshTimer.stop();
QMutexLocker locker(&m_mutex);
m_isActive = false;
if (m_mltProducer) {
if (m_isZoneMode) {
resetZoneMode();
}
m_mltProducer->set_speed(0.0);
}
if (m_mltConsumer) {
m_mltConsumer->purge();
if (!m_mltConsumer->is_stopped()) {
m_mltConsumer->stop();
}
}
m_isRefreshing = false;
}
void Render::stop(const GenTime &startTime)
{
requestedSeekPosition = SEEK_INACTIVE;
m_refreshTimer.stop();
QMutexLocker locker(&m_mutex);
m_isActive = false;
if (m_mltProducer) {
if (m_isZoneMode) {
resetZoneMode();
}
m_mltProducer->set_speed(0.0);
m_mltProducer->seek((int) startTime.frames(m_fps));
}
if (m_mltConsumer) {
m_mltConsumer->purge();
}
m_isRefreshing = false;
}
/*void Render::pause()
{
requestedSeekPosition = SEEK_INACTIVE;
if (!m_mltProducer || !m_mltConsumer || !m_isActive)
return;
m_mltProducer->set_speed(0.0);
//if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
//m_mltProducer->seek(m_mltConsumer->position());
}*/
void Render::setActiveMonitor()
{
if (!m_isActive) {
emit activateMonitor(m_name);
}
}
void Render::switchPlay(bool play, double speed)
{
QMutexLocker locker(&m_mutex);
requestedSeekPosition = SEEK_INACTIVE;
if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
qWarning("no producer/consumer!");
return;
}
if (m_isZoneMode) {
resetZoneMode();
}
if (play) {
double currentSpeed = m_mltProducer->get_speed();
if (m_name == Kdenlive::ClipMonitor && m_mltConsumer->position() == m_mltProducer->get_out() && speed > 0) {
m_mltProducer->seek(0);
}
m_mltProducer->set_speed(speed);
m_mltConsumer->start();
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
} else {
m_mltProducer->set_speed(0);
m_mltProducer->seek(m_mltConsumer->position() + 1);
m_mltConsumer->purge();
m_mltConsumer->start();
}
}
void Render::play(double speed)
{
requestedSeekPosition = SEEK_INACTIVE;
if (!m_mltProducer || !m_isActive) {
return;
}
double current_speed = m_mltProducer->get_speed();
if (current_speed == speed) {
return;
}
if (m_isZoneMode) {
resetZoneMode();
}
if (speed != 0 && m_mltConsumer->get_int("real_time") != m_qmlView->realTime()) {
m_mltConsumer->set("real_time", m_qmlView->realTime());
m_mltConsumer->set("buffer", 25);
m_mltConsumer->set("prefill", 1);
// Changes to real_time require a consumer restart if running.
if (!m_mltConsumer->is_stopped()) {
m_mltConsumer->stop();
}
}
if (current_speed == 0) {
m_mltConsumer->start();
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
}
m_mltProducer->set_speed(speed);
}
void Render::play(const GenTime &startTime)
{
requestedSeekPosition = SEEK_INACTIVE;
if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
return;
}
m_mltProducer->seek((int)(startTime.frames(m_fps)));
m_mltProducer->set_speed(1.0);
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
}
void Render::loopZone(const GenTime &startTime, const GenTime &stopTime)
{
requestedSeekPosition = SEEK_INACTIVE;
if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
return;
}
//m_mltProducer->set("eof", "loop");
m_isLoopMode = true;
m_loopStart = startTime;
playZone(startTime, stopTime);
}
bool Render::playZone(const GenTime &startTime, const GenTime &stopTime)
{
requestedSeekPosition = SEEK_INACTIVE;
if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
return false;
}
m_mltProducer->seek((int)(startTime.frames(m_fps)));
m_mltProducer->set_speed(0);
m_mltConsumer->purge();
m_mltProducer->set("out", (int)(stopTime.frames(m_fps)));
m_mltProducer->set_speed(1.0);
if (m_mltConsumer->is_stopped()) {
m_mltConsumer->start();
}
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
m_isZoneMode = true;
return true;
}
void Render::resetZoneMode()
{
if (!m_isZoneMode && !m_isLoopMode) {
return;
}
m_mltProducer->set("out", m_mltProducer->get_length());
m_isZoneMode = false;
m_isLoopMode = false;
}
void Render::seekToFrame(int pos)
{
if (!m_mltProducer || !m_isActive) {
return;
}
pos = qBound(0, pos - m_mltProducer->get_in(), m_mltProducer->get_length() - 1);
seek(pos);
}
void Render::seekToFrameDiff(int diff)
{
if (byPassSeek) {
emit renderSeek(diff);
return;
}
if (!m_mltProducer || !m_isActive) {
return;
}
if (requestedSeekPosition == SEEK_INACTIVE) {
seek(seekFramePosition() + diff);
} else {
seek(requestedSeekPosition + diff);
}
}
void Render::doRefresh()
{
if (m_mltProducer && (playSpeed() == 0) && m_isActive) {
if (m_isRefreshing) {
m_refreshTimer.start();
} else {
refresh();
}
}
}
void Render::refresh()
{
m_refreshTimer.stop();
if (!m_mltProducer || !m_isActive) {
return;
}
QMutexLocker locker(&m_mutex);
if (m_mltConsumer) {
m_isRefreshing = true;
if (m_mltConsumer->is_stopped()) {
m_mltConsumer->start();
}
m_mltConsumer->purge();
m_mltConsumer->set("refresh", 1);
}
}
void Render::setDropFrames(bool drop)
{
QMutexLocker locker(&m_mutex);
if (m_mltConsumer) {
int dropFrames = m_qmlView->realTime();
if (drop == false) {
dropFrames = -dropFrames;
}
//m_mltConsumer->stop();
m_mltConsumer->set("real_time", dropFrames);
if (m_mltConsumer->start() == -1) {
qCWarning(KDENLIVE_LOG) << "ERROR, Cannot start monitor";
}
}
}
void Render::setConsumerProperty(const QString &name, const QString &value)
{
QMutexLocker locker(&m_mutex);
if (m_mltConsumer) {
//m_mltConsumer->stop();
m_mltConsumer->set(name.toUtf8().constData(), value.toUtf8().constData());
if (m_isActive && m_mltConsumer->start() == -1) {
qCWarning(KDENLIVE_LOG) << "ERROR, Cannot start monitor";
}
}
}
bool Render::isPlaying() const
{
if (!m_mltConsumer || m_mltConsumer->is_stopped()) {
return false;
}
return playSpeed() != 0;
}
double Render::playSpeed() const
{
if (m_mltProducer) {
return m_mltProducer->get_speed();
}
return 0.0;
}
GenTime Render::seekPosition() const
{
if (m_mltConsumer) {
return GenTime((int) m_mltConsumer->position(), m_fps);
} else {
return GenTime();
}
}
int Render::seekFramePosition() const
{
if (m_mltProducer && m_mltProducer->get_speed() == 0) {
return (int) m_mltProducer->position();
}
if (m_mltConsumer) {
return (int) m_mltConsumer->position();
}
return 0;
}
void Render::emitFrameUpdated(Mlt::Frame &frame)
{
Q_UNUSED(frame)
return;
/*TODO: fix movit crash
mlt_image_format format = mlt_image_rgb24;
int width = 0;
int height = 0;
//frame.set("rescale.interp", "bilinear");
//frame.set("deinterlace_method", "onefield");
//frame.set("top_field_first", -1);
const uchar* image = frame.get_image(format, width, height);
QImage qimage(width, height, QImage::Format_RGB888); //Format_ARGB32_Premultiplied);
memcpy(qimage.scanLine(0), image, width * height * 3);
emit frameUpdated(qimage);
*/
}
int Render::getCurrentSeekPosition() const
{
if (requestedSeekPosition != SEEK_INACTIVE) {
return requestedSeekPosition;
}
return (int) m_mltConsumer->position();
}
bool Render::checkFrameNumber(int pos)
{
if (pos == requestedSeekPosition) {
requestedSeekPosition = SEEK_INACTIVE;
}
const double speed = m_mltProducer->get_speed();
if (requestedSeekPosition != SEEK_INACTIVE) {
m_mltProducer->set_speed(0);
m_mltProducer->seek(requestedSeekPosition);
if (speed == 0) {
m_mltConsumer->set("refresh", 1);
} else {
m_mltProducer->set_speed(speed);
}
} else if (speed < 0){
m_isRefreshing = false;
if (pos <= 0) {
m_mltProducer->set_speed(0);
return false;
}
} else {
m_isRefreshing = false;
if (m_isZoneMode) {
if (pos >= m_mltProducer->get_int("out") - 1) {
if (m_isLoopMode) {
m_mltConsumer->purge();
m_mltProducer->seek((int)(m_loopStart.frames(m_fps)));
m_mltProducer->set_speed(1.0);
m_mltConsumer->set("refresh", 1);
} else {
if (speed == 0) {
return false;
}
}
}
}
}
return true;
}
void Render::slotCheckSeeking()
{
if (requestedSeekPosition != SEEK_INACTIVE) {
m_mltProducer->seek(requestedSeekPosition);
requestedSeekPosition = SEEK_INACTIVE;
}
}
void Render::showAudio(Mlt::Frame &frame)
{
if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
return;
}
mlt_audio_format audio_format = mlt_audio_s16;
//FIXME: should not be hardcoded..
int freq = 48000;
int num_channels = 2;
int samples = 0;
qint16 *data = (qint16 *)frame.get_audio(audio_format, freq, num_channels, samples);
if (!data) {
return;
}
// Data format: [ c00 c10 c01 c11 c02 c12 c03 c13 ... c0{samples-1} c1{samples-1} for 2 channels.
// So the vector is of size samples*channels.
audioShortVector sampleVector(samples * num_channels);
memcpy(sampleVector.data(), data, samples * num_channels * sizeof(qint16));
if (samples > 0) {
emit audioSamplesSignal(sampleVector, freq, num_channels, samples);
}
}
/*
* MLT playlist direct manipulation.
*/
void Render::mltCheckLength(Mlt::Tractor *tractor)
{
int trackNb = tractor->count();
int duration = 0;
if (m_isZoneMode) {
resetZoneMode();
}
if (trackNb == 1) {
QScopedPointer trackProducer(tractor->track(0));
duration = trackProducer->get_playtime();
m_mltProducer->set("out", duration);
emit durationChanged(duration);
return;
}
while (trackNb > 1) {
QScopedPointer trackProducer(tractor->track(trackNb - 1));
int trackDuration = trackProducer->get_playtime();
if (trackDuration > duration) {
duration = trackDuration;
}
trackNb--;
}
QScopedPointer blackTrackProducer(tractor->track(0));
if (blackTrackProducer->get_playtime() - 1 != duration) {
Mlt::Playlist blackTrackPlaylist((mlt_playlist) blackTrackProducer->get_service());
QScopedPointer blackclip(blackTrackPlaylist.get_clip(0));
if (!blackclip || blackclip->is_blank() || blackTrackPlaylist.count() != 1) {
blackTrackPlaylist.clear();
m_blackClip->set("length", duration + 1);
m_blackClip->set("out", duration);
Mlt::Producer *black2 = m_blackClip->cut(0, duration);
blackTrackPlaylist.insert_at(0, black2, 1);
delete black2;
} else {
if (duration > blackclip->parent().get_length()) {
blackclip->parent().set("length", duration + 1);
blackclip->parent().set("out", duration);
blackclip->set("length", duration + 1);
}
blackTrackPlaylist.resize_clip(0, 0, duration);
}
if (m_mltConsumer->position() > duration) {
m_mltConsumer->purge();
m_mltProducer->seek(duration);
}
m_mltProducer->set("out", duration);
emit durationChanged(duration);
}
}
Mlt::Producer *Render::getTrackProducer(const QString &id, int track, bool, bool)
{
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
return nullptr;
}
Mlt::Tractor tractor(service);
// WARNING: Kdenlive's track numbering is 0 for top track, while in MLT 0 is black track and 1 is the bottom track so we MUST reverse track number
// TODO: memleak
Mlt::Producer destTrackProducer(tractor.track(tractor.count() - track - 1));
Mlt::Playlist destTrackPlaylist((mlt_playlist) destTrackProducer.get_service());
return getProducerForTrack(destTrackPlaylist, id);
}
Mlt::Producer *Render::getProducerForTrack(Mlt::Playlist &trackPlaylist, const QString &clipId)
{
//TODO: find a better way to check if a producer is already inserted in a track ?
QString trackName = trackPlaylist.get("id");
QString clipIdWithTrack = clipId + QLatin1Char('_') + trackName;
Mlt::Producer *prod = nullptr;
for (int i = 0; i < trackPlaylist.count(); i++) {
if (trackPlaylist.is_blank(i)) {
continue;
}
QScopedPointer p(trackPlaylist.get_clip(i));
QString id = p->parent().get("id");
if (id == clipIdWithTrack) {
// This producer already exists in the track, reuse it
prod = &p->parent();
break;
}
}
if (prod == nullptr) {
prod = m_binController->getBinProducer(clipId);
}
return prod;
}
Mlt::Tractor *Render::lockService()
{
// we are going to replace some clips, purge consumer
if (!m_mltProducer) {
return nullptr;
}
QMutexLocker locker(&m_mutex);
if (m_mltConsumer) {
m_mltConsumer->purge();
}
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
return nullptr;
}
service.lock();
return new Mlt::Tractor(service);
}
void Render::unlockService(Mlt::Tractor *tractor)
{
if (tractor) {
delete tractor;
}
if (!m_mltProducer) {
return;
}
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
return;
}
service.unlock();
}
void Render::mltInsertSpace(const QMap &trackClipStartList, const QMap &trackTransitionStartList, int track, const GenTime &duration, const GenTime &timeOffset)
{
if (!m_mltProducer) {
//qCDebug(KDENLIVE_LOG) << "PLAYLIST NOT INITIALISED //////";
return;
}
Mlt::Producer parentProd(m_mltProducer->parent());
if (parentProd.get_producer() == nullptr) {
//qCDebug(KDENLIVE_LOG) << "PLAYLIST BROKEN, CANNOT INSERT CLIP //////";
return;
}
////qCDebug(KDENLIVE_LOG)<<"// CLP STRT LST: "< 0) {
trackPlaylist.insert_blank(clipIndex, diff - 1);
} else {
if (!trackPlaylist.is_blank(clipIndex)) {
clipIndex --;
}
if (!trackPlaylist.is_blank(clipIndex)) {
//qCDebug(KDENLIVE_LOG) << "//// ERROR TRYING TO DELETE SPACE FROM " << insertPos;
}
int position = trackPlaylist.clip_start(clipIndex);
int blankDuration = trackPlaylist.clip_length(clipIndex);
if (blankDuration + diff == 0) {
trackPlaylist.remove(clipIndex);
} else {
trackPlaylist.remove_region(position, -diff);
}
}
trackPlaylist.consolidate_blanks(0);
}
// now move transitions
mlt_service serv = m_mltProducer->parent().get_service();
mlt_service nextservice = mlt_service_get_producer(serv);
mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
QString mlt_type = mlt_properties_get(properties, "mlt_type");
QString resource = mlt_properties_get(properties, "mlt_service");
while (mlt_type == QLatin1String("transition")) {
mlt_transition tr = (mlt_transition) nextservice;
int currentTrack = mlt_transition_get_b_track(tr);
int currentIn = (int) mlt_transition_get_in(tr);
int currentOut = (int) mlt_transition_get_out(tr);
insertPos = trackTransitionStartList.value(track);
if (insertPos != -1) {
insertPos += offset;
if (track == currentTrack && currentOut > insertPos && resource != QLatin1String("mix")) {
mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
}
}
nextservice = mlt_service_producer(nextservice);
if (nextservice == nullptr) {
break;
}
properties = MLT_SERVICE_PROPERTIES(nextservice);
mlt_type = mlt_properties_get(properties, "mlt_type");
resource = mlt_properties_get(properties, "mlt_service");
}
} else {
for (int trackNb = tractor.count() - 1; trackNb >= 1; --trackNb) {
Mlt::Producer trackProducer(tractor.track(trackNb));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
//int clipNb = trackPlaylist.count();
insertPos = trackClipStartList.value(trackNb);
if (insertPos != -1) {
insertPos += offset;
/* //qCDebug(KDENLIVE_LOG)<<"-------------\nTRACK "< 0) {
trackPlaylist.insert_blank(clipIndex, diff - 1);
} else {
if (!trackPlaylist.is_blank(clipIndex)) {
clipIndex --;
}
if (!trackPlaylist.is_blank(clipIndex)) {
//qCDebug(KDENLIVE_LOG) << "//// ERROR TRYING TO DELETE SPACE FROM " << insertPos;
}
int position = trackPlaylist.clip_start(clipIndex);
int blankDuration = trackPlaylist.clip_length(clipIndex);
if (diff + blankDuration == 0) {
trackPlaylist.remove(clipIndex);
} else {
trackPlaylist.remove_region(position, - diff);
}
}
trackPlaylist.consolidate_blanks(0);
}
}
// now move transitions
mlt_service serv = m_mltProducer->parent().get_service();
mlt_service nextservice = mlt_service_get_producer(serv);
mlt_properties properties = MLT_SERVICE_PROPERTIES(nextservice);
QString mlt_type = mlt_properties_get(properties, "mlt_type");
QString resource = mlt_properties_get(properties, "mlt_service");
while (mlt_type == QLatin1String("transition")) {
mlt_transition tr = (mlt_transition) nextservice;
int currentIn = (int) mlt_transition_get_in(tr);
int currentOut = (int) mlt_transition_get_out(tr);
int currentTrack = mlt_transition_get_b_track(tr);
insertPos = trackTransitionStartList.value(currentTrack);
if (insertPos != -1) {
insertPos += offset;
if (currentOut > insertPos && resource != QLatin1String("mix")) {
mlt_transition_set_in_and_out(tr, currentIn + diff, currentOut + diff);
}
}
nextservice = mlt_service_producer(nextservice);
if (nextservice == nullptr) {
break;
}
properties = MLT_SERVICE_PROPERTIES(nextservice);
mlt_type = mlt_properties_get(properties, "mlt_type");
resource = mlt_properties_get(properties, "mlt_service");
}
}
service.unlock();
mltCheckLength(&tractor);
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
}
bool Render::mltResizeClipCrop(const ItemInfo &info, GenTime newCropStart)
{
Mlt::Service service(m_mltProducer->parent().get_service());
int newCropFrame = (int) newCropStart.frames(m_fps);
Mlt::Tractor tractor(service);
Mlt::Producer trackProducer(tractor.track(info.track));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
if (trackPlaylist.is_blank_at(info.startPos.frames(m_fps))) {
//qCDebug(KDENLIVE_LOG) << "//////// ERROR RSIZING BLANK CLIP!!!!!!!!!!!";
return false;
}
service.lock();
int clipIndex = trackPlaylist.get_clip_index_at(info.startPos.frames(m_fps));
QScopedPointer clip(trackPlaylist.get_clip(clipIndex));
if (clip == nullptr) {
//qCDebug(KDENLIVE_LOG) << "//////// ERROR RSIZING nullptr CLIP!!!!!!!!!!!";
service.unlock();
return false;
}
int previousStart = clip->get_in();
int previousOut = clip->get_out();
if (previousStart == newCropFrame) {
//qCDebug(KDENLIVE_LOG) << "//////// No ReSIZING Required";
service.unlock();
return true;
}
int frameOffset = newCropFrame - previousStart;
trackPlaylist.resize_clip(clipIndex, newCropFrame, previousOut + frameOffset);
service.unlock();
m_isRefreshing = true;
m_mltConsumer->set("refresh", 1);
return true;
}
QList Render::checkTrackSequence(int track)
{
QList list;
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
return list;
}
Mlt::Tractor tractor(service);
service.lock();
Mlt::Producer trackProducer(tractor.track(track));
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
int clipNb = trackPlaylist.count();
////qCDebug(KDENLIVE_LOG) << "// PARSING SCENE TRACK: " << t << ", CLIPS: " << clipNb;
for (int i = 0; i < clipNb; ++i) {
QScopedPointer c(trackPlaylist.get_clip(i));
int pos = trackPlaylist.clip_start(i);
if (!list.contains(pos)) {
list.append(pos);
}
pos += c->get_playtime();
if (!list.contains(pos)) {
list.append(pos);
}
}
return list;
}
void Render::cloneProperties(Mlt::Properties &dest, Mlt::Properties &source)
{
int count = source.count();
int i = 0;
for (i = 0; i < count; i ++) {
char *value = source.get(i);
if (value != nullptr) {
char *name = source.get_name(i);
if (name != nullptr && name[0] != QLatin1Char('_')) {
dest.set(name, value);
}
}
}
}
void Render::fillSlowMotionProducers()
{
if (m_mltProducer == nullptr) {
return;
}
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
return;
}
Mlt::Tractor tractor(service);
int trackNb = tractor.count();
for (int t = 1; t < trackNb; ++t) {
Mlt::Producer *tt = tractor.track(t);
Mlt::Producer trackProducer(tt);
delete tt;
Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
if (!trackPlaylist.is_valid()) {
continue;
}
int clipNb = trackPlaylist.count();
for (int i = 0; i < clipNb; ++i) {
QScopedPointer c(trackPlaylist.get_clip(i));
Mlt::Producer *nprod = new Mlt::Producer(c->get_parent());
if (nprod) {
QString id = nprod->parent().get("id");
if (id.startsWith(QLatin1String("slowmotion:")) && !nprod->is_blank()) {
// this is a slowmotion producer, add it to the list
QString url = QString::fromUtf8(nprod->get("resource"));
int strobe = nprod->get_int("strobe");
if (strobe > 1) {
url.append(QStringLiteral("&strobe=") + QString::number(strobe));
}
if (!m_slowmotionProducers.contains(url)) {
m_slowmotionProducers.insert(url, nprod);
}
} else {
delete nprod;
}
}
}
}
}
//Updates all transitions
QList Render::mltInsertTrack(int ix, const QString &name, bool videoTrack)
{
QList transitionInfos;
// Track add / delete was only added recently in MLT (pre 0.9.8 release).
#if (LIBMLT_VERSION_INT < 0x0908)
Q_UNUSED(ix)
Q_UNUSED(name)
Q_UNUSED(videoTrack)
qCDebug(KDENLIVE_LOG) << "Track insertion requires a more recent MLT version";
return transitionInfos;
#else
Mlt::Service service(m_mltProducer->parent().get_service());
if (service.type() != tractor_type) {
qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
return QList ();
}
blockSignals(true);
service.lock();
Mlt::Tractor tractor(service);
// Find available track name
QStringList trackNames;
for (int i = 0; i < tractor.count(); i++) {
QScopedPointer track(tractor.track(i));
trackNames << track->get("id");
}
QString newName = QStringLiteral("playlist0");
int i = 1;
while (trackNames.contains(newName)) {
newName = QStringLiteral("playlist%1").arg(i);
i++;
}
Mlt::Playlist playlist(*service.profile());
playlist.set("kdenlive:track_name", name.toUtf8().constData());
playlist.set("id", newName.toUtf8().constData());
int ct = tractor.count();
if (ix > ct) {
//qCDebug(KDENLIVE_LOG) << "// ERROR, TRYING TO insert TRACK " << ix << ", max: " << ct;
ix = ct;
}
tractor.insert_track(playlist, ix);
Mlt::Producer newProd(tractor.track(ix));
if (!videoTrack) {
newProd.set("kdenlive:audio_track", 1);
newProd.set("hide", 1);
}
checkMaxThreads();
tractor.refresh();
Mlt::Field *field = tractor.field();
// Add audio mix transition to last track
Mlt::Transition mix(*m_qmlView->profile(), "mix");
mix.set("a_track", 0);
mix.set("b_track", ix);
mix.set("always_active", 1);
mix.set("internal_added", 237);
if (TransitionHandler::sumAudioMixAvailable()) {
mix.set("sum", 1);
} else {
mix.set("combine", 1);
}
field->plant_transition(mix, 0, ix);
service.unlock();
blockSignals(false);
return transitionInfos;
#endif
}
void Render::sendFrameUpdate()
{
if (m_mltProducer) {
Mlt::Frame *frame = m_mltProducer->get_frame();
emitFrameUpdated(*frame);
delete frame;
}
}
Mlt::Producer *Render::getProducer()
{
return m_mltProducer;
}
const QString Render::activeClipId()
{
if (m_mltProducer) {
return m_mltProducer->get("id");
}
return QString();
}
//static
bool Render::getBlackMagicDeviceList(KComboBox *devicelist, bool force)
{
if (!force && !KdenliveSettings::decklink_device_found()) {
return false;
}
Mlt::Profile profile;
Mlt::Producer bm(profile, "decklink");
int found_devices = 0;
if (bm.is_valid()) {
bm.set("list_devices", 1);
found_devices = bm.get_int("devices");
} else {
KdenliveSettings::setDecklink_device_found(false);
}
if (found_devices <= 0) {
devicelist->setEnabled(false);
return false;
}
KdenliveSettings::setDecklink_device_found(true);
for (int i = 0; i < found_devices; ++i) {
char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData());
devicelist->addItem(bm.get(tmp));
delete[] tmp;
}
return true;
}
bool Render::getBlackMagicOutputDeviceList(KComboBox *devicelist, bool force)
{
if (!force && !KdenliveSettings::decklink_device_found()) {
return false;
}
Mlt::Profile profile;
Mlt::Consumer bm(profile, "decklink");
int found_devices = 0;
if (bm.is_valid()) {
bm.set("list_devices", 1);
found_devices = bm.get_int("devices");
} else {
KdenliveSettings::setDecklink_device_found(false);
}
if (found_devices <= 0) {
devicelist->setEnabled(false);
return false;
}
KdenliveSettings::setDecklink_device_found(true);
for (int i = 0; i < found_devices; ++i) {
char *tmp = qstrdup(QStringLiteral("device.%1").arg(i).toUtf8().constData());
devicelist->addItem(bm.get(tmp));
delete[] tmp;
}
return true;
}
//static
bool Render::checkX11Grab()
{
if (KdenliveSettings::rendererpath().isEmpty() || KdenliveSettings::ffmpegpath().isEmpty()) {
return false;
}
QProcess p;
QStringList args;
args << QStringLiteral("avformat:f-list");
p.start(KdenliveSettings::rendererpath(), args);
if (!p.waitForStarted()) {
return false;
}
if (!p.waitForFinished()) {
return false;
}
QByteArray result = p.readAllStandardError();
return result.contains("x11grab");
}
double Render::getMltVersionInfo(const QString &tag)
{
double version = 0;
Mlt::Properties *metadata = pCore->getMltRepository()->metadata(producer_type, tag.toUtf8().data());
if (metadata && metadata->is_valid()) {
version = metadata->get_double("version");
}
delete metadata;
return version;
}
Mlt::Producer *Render::getBinProducer(const QString &id)
{
return m_binController->getBinProducer(id);
}
Mlt::Producer *Render::getBinVideoProducer(const QString &id)
{
return m_binController->getBinVideoProducer(id);
}
void Render::loadExtraProducer(const QString &id, Mlt::Producer *prod)
{
m_binController->loadExtraProducer(id, prod);
}
const QString Render::getBinProperty(const QString &name)
{
return m_binController->getProperty(name);
}
void Render::setVolume(double volume)
{
if (m_mltConsumer) {
if (m_mltConsumer->get("mlt_service") == QStringLiteral("multi")) {
m_mltConsumer->set("0.volume", volume);
} else {
m_mltConsumer->set("volume", volume);
}
}
}
bool Render::storeSlowmotionProducer(const QString &url, Mlt::Producer *prod, bool replace)
{
if (!m_slowmotionProducers.contains(url)) {
m_slowmotionProducers.insert(url, prod);
return true;
} else if (replace) {
Mlt::Producer *old = m_slowmotionProducers.take(url);
delete old;
m_slowmotionProducers.insert(url, prod);
return true;
}
return false;
}
Mlt::Producer *Render::getSlowmotionProducer(const QString &url)
{
return m_slowmotionProducers.value(url);
}
void Render::updateSlowMotionProducers(const QString &id, const QMap &passProperties)
{
QMapIterator i(m_slowmotionProducers);
Mlt::Producer *prod;
while (i.hasNext()) {
i.next();
prod = i.value();
QString currentId = prod->get("id");
if (currentId.startsWith("slowmotion:" + id + QLatin1Char(':'))) {
QMapIterator j(passProperties);
while (j.hasNext()) {
j.next();
prod->set(j.key().toUtf8().constData(), j.value().toUtf8().constData());
}
}
}
}
void Render::preparePreviewRendering(const QString &sceneListFile)
{
// Save temporary scenelist
Mlt::Consumer xmlConsumer(*m_qmlView->profile(), "xml", sceneListFile.toUtf8().constData());
if (!xmlConsumer.is_valid()) {
return;
}
m_mltProducer->optimise();
xmlConsumer.set("terminate_on_pause", 1);
xmlConsumer.set("no_meta", 1);
Mlt::Producer prod(m_mltProducer->get_producer());
if (!prod.is_valid()) {
return;
}
xmlConsumer.connect(prod);
xmlConsumer.run();
}