renderer.cpp 56.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/***************************************************************************
                        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
12
  email                : jb@kdenlive.org
13 14 15 16 17 18 19 20 21 22 23 24

***************************************************************************/

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

25 26
#include "renderer.h"
#include "kdenlivesettings.h"
Vincent Pinon's avatar
Vincent Pinon committed
27
#include "doc/kthumb.h"
28
#include "definitions.h"
Vincent Pinon's avatar
Vincent Pinon committed
29
#include "project/dialogs/slideshowclip.h"
Vincent Pinon's avatar
Vincent Pinon committed
30
#include "dialogs/profilesdialog.h"
31
#include "mltcontroller/bincontroller.h"
32
#include "bin/projectclip.h"
33
#include "timeline/clip.h"
34
#include "monitor/glwidget.h"
35
#include "mltcontroller/clipcontroller.h"
36
#include "timeline/transitionhandler.h"
37
#include "core.h"
38
#include <mlt++/Mlt.h>
39

Laurent Montel's avatar
Laurent Montel committed
40
#include "kdenlive_debug.h"
41
#include <KMessageBox>
42
#include <KLocalizedString>
Vincent Pinon's avatar
Vincent Pinon committed
43
#include <QDialog>
44
#include <QString>
45
#include <QApplication>
46
#include <QProcess>
47

48 49
#include <cstdlib>
#include <cstdarg>
50
#include <KRecentDirs>
Vincent Pinon's avatar
Vincent Pinon committed
51
#include <QPushButton>
52

53
#define SEEK_INACTIVE (-1)
54

55
Render::Render(Kdenlive::MonitorId rendererName, BinController *binController, GLWidget *qmlView, QWidget *parent) :
56
    AbstractRender(rendererName, parent),
57
    byPassSeek(false),
58
    requestedSeekPosition(SEEK_INACTIVE),
59 60
    showFrameSemaphore(1),
    externalConsumer(false),
61
    m_name(rendererName),
Laurent Montel's avatar
Laurent Montel committed
62 63 64 65
    m_mltConsumer(nullptr),
    m_mltProducer(nullptr),
    m_showFrameEvent(nullptr),
    m_pauseEvent(nullptr),
66
    m_binController(binController),
Vincent Pinon's avatar
Vincent Pinon committed
67
    m_qmlView(qmlView),
68 69
    m_isZoneMode(false),
    m_isLoopMode(false),
Laurent Montel's avatar
Laurent Montel committed
70
    m_blackClip(nullptr),
71
    m_isActive(false),
72
    m_isRefreshing(false)
73
{
74
    qRegisterMetaType<stringMap> ("stringMap");
75
    analyseAudio = KdenliveSettings::monitor_audio();
76 77
    //buildConsumer();
    if (m_qmlView) {
78 79 80
        m_blackClip = new Mlt::Producer(*m_qmlView->profile(), "colour:black");
        m_blackClip->set("id", "black");
        m_blackClip->set("mlt_type", "producer");
81 82
        m_blackClip->set("aspect_ratio", 1);
        m_blackClip->set("set.test_audio", 0);
83
        m_mltProducer = m_blackClip->cut(0, 1);
84 85 86 87 88
        m_qmlView->setProducer(m_mltProducer);
        m_mltConsumer = qmlView->consumer();
    }
    /*m_mltConsumer->connect(*m_mltProducer);
    m_mltProducer->set_speed(0.0);*/
89
    m_refreshTimer.setSingleShot(true);
90
    m_refreshTimer.setInterval(50);
Laurent Montel's avatar
Laurent Montel committed
91 92
    connect(&m_refreshTimer, &QTimer::timeout, this, &Render::refresh);
    connect(this, &Render::checkSeeking, this, &Render::slotCheckSeeking);
93
    if (m_name == Kdenlive::ProjectMonitor) {
Laurent Montel's avatar
Laurent Montel committed
94 95
        connect(m_binController, &BinController::prepareTimelineReplacement, this, &Render::prepareTimelineReplacement, Qt::DirectConnection);
        connect(m_binController, &BinController::replaceTimelineProducer, this, &Render::replaceTimelineProducer, Qt::DirectConnection);
Laurent Montel's avatar
Laurent Montel committed
96
        connect(m_binController, &BinController::updateTimelineProducer, this, &Render::updateTimelineProducer);
Laurent Montel's avatar
Laurent Montel committed
97
        connect(m_binController, &BinController::setDocumentNotes, this, &Render::setDocumentNotes);
98
    }
99 100
}

101 102
Render::~Render()
{
103 104 105
    closeMlt();
}

106
void Render::closeMlt()
107
{
Laurent Montel's avatar
Laurent Montel committed
108 109 110 111 112
    delete m_showFrameEvent;
    delete m_pauseEvent;
    delete m_mltConsumer;
    delete m_mltProducer;
    delete m_blackClip;
113 114
}

115 116
void Render::slotSwitchFullscreen()
{
Laurent Montel's avatar
Laurent Montel committed
117
    if (m_mltConsumer) {
Laurent Montel's avatar
Laurent Montel committed
118
        m_mltConsumer->set("full_screen", 1);
Laurent Montel's avatar
Laurent Montel committed
119
    }
120 121
}

122
void Render::prepareProfileReset(double fps)
123
{
124
    m_refreshTimer.stop();
125
    m_fps = fps;
126 127
}

128 129 130 131 132 133
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");
134 135
    m_blackClip->set("aspect_ratio", 1);
    m_blackClip->set("set.test_audio", 0);
136 137
}

Laurent Montel's avatar
Laurent Montel committed
138
void Render::seek(const GenTime &time)
139
{
Laurent Montel's avatar
Laurent Montel committed
140
    if (!m_mltProducer || !m_isActive) {
141
        return;
Laurent Montel's avatar
Laurent Montel committed
142
    }
143 144
    int pos = time.frames(m_fps);
    seek(pos);
145 146
}

147 148 149 150 151 152 153 154 155 156
void Render::silentSeek(int time)
{
    if (m_isActive) {
        seek(time);
        return;
    }
    m_mltProducer->seek(time);
    m_mltConsumer->set("refresh", 1);
}

157 158
void Render::seek(int time)
{
159
    resetZoneMode();
160
    time = qBound(0, time, m_mltProducer->get_length() - 1);
161
    if (requestedSeekPosition == SEEK_INACTIVE) {
Laurent Montel's avatar
Laurent Montel committed
162
        requestedSeekPosition = time;
163 164 165
        if (m_mltProducer->get_speed() != 0) {
            m_mltConsumer->purge();
        }
166
        m_mltProducer->seek(time);
167
        if (!externalConsumer) {
168
            m_isRefreshing = true;
169 170 171
            if (m_mltConsumer->is_stopped()) {
                m_mltConsumer->start();
            }
Laurent Montel's avatar
Laurent Montel committed
172 173
            m_mltConsumer->set("refresh", 1);
        }
Laurent Montel's avatar
Laurent Montel committed
174
    } else {
175 176
        requestedSeekPosition = time;
    }
177 178
}

179 180
int Render::frameRenderWidth() const
{
181
    return m_qmlView->profile()->width();
182 183
}

184 185
int Render::renderWidth() const
{
186
    return (int)(m_qmlView->profile()->height() * m_qmlView->profile()->dar() + 0.5);
187 188
}

189 190
int Render::renderHeight() const
{
191
    return m_qmlView->profile()->height();
192
}
193

Laurent Montel's avatar
Laurent Montel committed
194
QImage Render::extractFrame(int frame_position, const QString &path, int width, int height)
195
{
196
    if (width == -1) {
197
        width = frameRenderWidth();
198
        height = renderHeight();
Laurent Montel's avatar
Laurent Montel committed
199 200 201
    } else if (width % 2 == 1) {
        width++;
    }
202
    if (!path.isEmpty()) {
203 204 205 206
        QScopedPointer<Mlt::Producer> 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;
207 208
        }
    }
209

210
    if (!m_mltProducer || !path.isEmpty()) {
211
        QImage pix(width, height, QImage::Format_RGB32);
212 213
        pix.fill(Qt::black);
        return pix;
214
    }
Laurent Montel's avatar
Laurent Montel committed
215
    Mlt::Frame *frame = nullptr;
216 217 218
    QImage img;
    bool profileFromSource = m_mltProducer->get_int("meta.media.width") > width;
    if (KdenliveSettings::gpu_accel() && !profileFromSource) {
219
        QString service = m_mltProducer->get("mlt_service");
220
        QScopedPointer <Mlt::Producer> tmpProd(new Mlt::Producer(*m_qmlView->profile(), service.toUtf8().constData(), m_mltProducer->get("resource")));
221 222
        Mlt::Filter scaler(*m_qmlView->profile(), "swscale");
        Mlt::Filter converter(*m_qmlView->profile(), "avcolor_space");
223 224 225 226
        tmpProd->attach(scaler);
        tmpProd->attach(converter);
        tmpProd->seek(m_mltProducer->position());
        frame = tmpProd->get_frame();
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
        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<Mlt::Profile> tmpProfile(new Mlt::Profile());
        QString service = m_mltProducer->get("mlt_service");
        QScopedPointer<Mlt::Producer> 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;
        }
Laurent Montel's avatar
Laurent Montel committed
249
    } else {
250
        frame = m_mltProducer->get_frame();
251 252
        img = KThumb::getFrame(frame, width, height);
        delete frame;
253 254
    }
    return img;
255 256
}

257 258
int Render::getLength()
{
259

260 261
    if (m_mltProducer) {
        return mlt_producer_get_playtime(m_mltProducer->get_producer());
262 263 264 265
    }
    return 0;
}

266
bool Render::isValid(const QUrl &url)
267
{
268
    Mlt::Producer producer(*m_qmlView->profile(), url.toLocalFile().toUtf8().constData());
Laurent Montel's avatar
Laurent Montel committed
269
    if (producer.is_blank()) {
270
        return false;
Laurent Montel's avatar
Laurent Montel committed
271
    }
272 273 274 275

    return true;
}

276 277
double Render::dar() const
{
278
    return m_qmlView->profile()->dar();
279 280
}

281 282
double Render::sar() const
{
283
    return m_qmlView->profile()->sar();
284
}
285

286
#if 0
Alberto Villa's avatar
Alberto Villa committed
287
/** Create the producer from the MLT XML QDomDocument */
288 289
void Render::initSceneList()
{
Laurent Montel's avatar
Laurent Montel committed
290
    //qCDebug(KDENLIVE_LOG) << "--------  INIT SCENE LIST ------_";
291
    QDomDocument doc;
292 293
    QDomElement mlt = doc.createElement("mlt");
    doc.appendChild(mlt);
294 295 296 297 298 299 300 301
    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");
302
    QDomElement multitrack = doc.createElement("multitrack");
303 304 305 306 307 308 309 310 311 312 313 314 315

    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);
316
    mlt.appendChild(tractor);
Laurent Montel's avatar
Laurent Montel committed
317
    // //qCDebug(KDENLIVE_LOG)<<doc.toString();
318
    /*
319
       QString tmp = QString("<mlt><producer resource=\"colour\" colour=\"red\" id=\"red\" /><tractor><multitrack><playlist></playlist><playlist></playlist><playlist /><playlist /><playlist></playlist></multitrack></tractor></mlt>");*/
320 321
    setSceneList(doc, 0);
}
322
#endif
323

324 325
void Render::loadUrl(const QString &url)
{
326
    Mlt::Producer *producer = new Mlt::Producer(*m_qmlView->profile(), url.toUtf8().constData());
327
    setProducer(producer, 0, true);
328 329
}

Vincent Pinon's avatar
Vincent Pinon committed
330
bool Render::updateProducer(Mlt::Producer *producer)
331 332 333 334 335 336 337 338 339 340
{
    if (m_mltProducer) {
        if (strcmp(m_mltProducer->get("resource"), "<tractor>") == 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;
Laurent Montel's avatar
Laurent Montel committed
341
        m_mltProducer = nullptr;
342 343 344 345 346 347 348
    }
    if (m_mltConsumer) {
        if (!m_mltConsumer->is_stopped()) {
            m_mltConsumer->stop();
        }
    }
    if (!producer || !producer->is_valid()) {
Vincent Pinon's avatar
Vincent Pinon committed
349
        return false;
350 351 352 353
    }
    m_fps = producer->get_fps();
    m_mltProducer = producer;
    if (m_qmlView) {
Vincent Pinon's avatar
Vincent Pinon committed
354
        m_qmlView->setProducer(producer);
355 356
        m_mltConsumer = m_qmlView->consumer();
    }
Vincent Pinon's avatar
Vincent Pinon committed
357
    return true;
358 359
}

Vincent Pinon's avatar
Vincent Pinon committed
360
bool Render::setProducer(Mlt::Producer *producer, int position, bool isActive)
361
{
362
    m_refreshTimer.stop();
363
    requestedSeekPosition = SEEK_INACTIVE;
364
    QMutexLocker locker(&m_mutex);
365 366
    QString currentId;
    int consumerPosition = 0;
367 368 369 370
    if (!producer && m_mltProducer && m_mltProducer->parent().get("id") == QLatin1String("black")) {
        // Black clip already displayed no need to refresh
        return true;
    }
371 372 373
    if (m_mltProducer) {
        currentId = m_mltProducer->get("id");
        m_mltProducer->set_speed(0);
374
        if (QString(m_mltProducer->get("resource")) == QLatin1String("<tractor>")) {
375 376 377 378 379 380
            // 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);
            }
        }
381
        delete m_mltProducer;
Laurent Montel's avatar
Laurent Montel committed
382
        m_mltProducer = nullptr;
383
    }
384
    if (m_mltConsumer) {
385
        if (!m_mltConsumer->is_stopped()) {
Vincent Pinon's avatar
Vincent Pinon committed
386
            isActive = true;
387
            m_mltConsumer->stop();
388
        }
389
        consumerPosition = m_mltConsumer->position();
390
    }
391
    blockSignals(true);
392
    if (!producer || !producer->is_valid()) {
Laurent Montel's avatar
Laurent Montel committed
393
        producer = m_blackClip->cut(0, 1);
394
    }
395

396
    emit stopped();
Laurent Montel's avatar
Laurent Montel committed
397 398 399 400 401 402
    if (position == -1 && producer->get("id") == currentId) {
        position = consumerPosition;
    }
    if (position != -1) {
        producer->seek(position);
    }
403
    m_fps = producer->get_fps();
404

405
    blockSignals(false);
406
    m_mltProducer = producer;
407
    m_mltProducer->set_speed(0);
408 409 410
    if (m_qmlView) {
        m_qmlView->setProducer(producer);
        m_mltConsumer = m_qmlView->consumer();
411
        //m_mltConsumer->set("refresh", 1);
412 413
    }
    //m_mltConsumer->connect(*producer);
414 415
    if (isActive) {
        startConsumer();
416
    }
417
    emit durationChanged(m_mltProducer->get_length() - 1, m_mltProducer->get_in());
418 419
    position = m_mltProducer->position();
    emit rendererPosition(position);
Vincent Pinon's avatar
Vincent Pinon committed
420
    return true;
421 422
}

Laurent Montel's avatar
Laurent Montel committed
423 424
void Render::startConsumer()
{
Laurent Montel's avatar
Laurent Montel committed
425
    if (m_mltConsumer->is_stopped() && m_mltConsumer->start() == -1) {
426 427
        // 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."));
Laurent Montel's avatar
Laurent Montel committed
428 429 430
        if (m_showFrameEvent) {
            delete m_showFrameEvent;
        }
Laurent Montel's avatar
Laurent Montel committed
431
        m_showFrameEvent = nullptr;
Laurent Montel's avatar
Laurent Montel committed
432 433 434
        if (m_pauseEvent) {
            delete m_pauseEvent;
        }
Laurent Montel's avatar
Laurent Montel committed
435
        m_pauseEvent = nullptr;
436
        delete m_mltConsumer;
Laurent Montel's avatar
Laurent Montel committed
437
        m_mltConsumer = nullptr;
438
        return;
439
    }
440
    m_isRefreshing = true;
441
    m_mltConsumer->set("refresh", 1);
442
    m_isActive = true;
443 444
}

Laurent Montel's avatar
Laurent Montel committed
445
int Render::setSceneList(const QDomDocument &list, int position)
446
{
447
    return setSceneList(list.toString(), position);
448 449
}

450
int Render::setSceneList(QString playlist, int position)
451
{
452
    requestedSeekPosition = SEEK_INACTIVE;
453
    m_refreshTimer.stop();
454
    QMutexLocker locker(&m_mutex);
455
    //if (m_winid == -1) return -1;
456
    int error = 0;
457

Laurent Montel's avatar
Laurent Montel committed
458
    //qCDebug(KDENLIVE_LOG) << "//////  RENDER, SET SCENE LIST:\n" << playlist <<"\n..........:::.";
459

460 461 462
    // Remove previous profile info
    QDomDocument doc;
    doc.setContent(playlist);
463
    QDomElement profile = doc.documentElement().firstChildElement(QStringLiteral("profile"));
464 465 466
    doc.documentElement().removeChild(profile);
    playlist = doc.toString();

467 468
    if (m_mltConsumer) {
        if (!m_mltConsumer->is_stopped()) {
469
            m_mltConsumer->stop();
470 471
        }
    } else {
Laurent Montel's avatar
Laurent Montel committed
472
        qCWarning(KDENLIVE_LOG) << "///////  ERROR, TRYING TO USE nullptr MLT CONSUMER";
473
        error = -1;
474 475
    }

476
    if (m_mltProducer) {
477
        m_mltProducer->set_speed(0);
478
        qDeleteAll(m_slowmotionProducers);
479
        m_slowmotionProducers.clear();
480

481
        delete m_mltProducer;
Laurent Montel's avatar
Laurent Montel committed
482
        m_mltProducer = nullptr;
483
        emit stopped();
484
    }
485
    m_binController->destroyBin();
486
    blockSignals(true);
487
    m_locale = QLocale();
488
    m_locale.setNumberOptions(QLocale::OmitGroupSeparator);
489 490
    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());
491
    if (!m_mltProducer || !m_mltProducer->is_valid()) {
Laurent Montel's avatar
Laurent Montel committed
492
        qCDebug(KDENLIVE_LOG) << " WARNING - - - - -INVALID PLAYLIST: " << playlist.toUtf8().constData();
493
        m_mltProducer = m_blackClip->cut(0, 1);
494
        error = -1;
495
    }
496
    m_mltProducer->set("eof", "pause");
497
    checkMaxThreads();
498
    m_mltProducer->optimise();
499

500
    m_fps = m_mltProducer->get_fps();
501
    if (position != 0) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
502
        // Seek to correct place after opening project.
503
        m_mltProducer->seek(position);
504
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
505

506 507 508
    // init MLT's document root, useful to find full urls
    m_binController->setDocumentRoot(doc.documentElement().attribute(QStringLiteral("root")));

509 510 511
    // Fill Bin's playlist
    Mlt::Service service(m_mltProducer->parent().get_service());
    if (service.type() != tractor_type) {
Laurent Montel's avatar
Laurent Montel committed
512
        qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM";
513
    }
514
    blockSignals(false);
515 516
    Mlt::Tractor tractor(service);
    Mlt::Properties retainList((mlt_properties) tractor.get_data("xml_retain"));
517 518
    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()));
519 520
        if (playlist.is_valid() && playlist.type() == playlist_type) {
            // Load bin clips
Laurent Montel's avatar
Laurent Montel committed
521
            m_binController->initializeBin(playlist);
522
        }
523 524
    }
    // No Playlist found, create new one
525
    if (m_qmlView) {
526
        m_binController->createIfNeeded(m_qmlView->profile());
527
        QString retain = QStringLiteral("xml_retain %1").arg(m_binController->binPlaylistId());
528 529
        tractor.set(retain.toUtf8().constData(), m_binController->service(), 0);
        //if (!m_binController->hasClip("black")) m_binController->addClipToBin("black", *m_blackClip);
530 531 532
        m_qmlView->setProducer(m_mltProducer);
        m_mltConsumer = m_qmlView->consumer();
    }
533

Laurent Montel's avatar
Laurent Montel committed
534
    //qCDebug(KDENLIVE_LOG) << "// NEW SCENE LIST DURATION SET TO: " << m_mltProducer->get_playtime();
535
    //m_mltConsumer->connect(*m_mltProducer);
536
    m_mltProducer->set_speed(0);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
537
    fillSlowMotionProducers();
538
    emit durationChanged(m_mltProducer->get_playtime() - 1);
539

540
    // Fill bin
Laurent Montel's avatar
Laurent Montel committed
541 542
    const QStringList ids = m_binController->getClipIds();
    for (const QString &id : ids) {
Laurent Montel's avatar
Laurent Montel committed
543
        if (id == QLatin1String("black")) {
544
            continue;
Laurent Montel's avatar
Laurent Montel committed
545
        }
546 547 548 549 550 551
        // 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));
552 553
    }

Laurent Montel's avatar
Laurent Montel committed
554
    ////qCDebug(KDENLIVE_LOG)<<"// SETSCN LST, POS: "<<position;
Laurent Montel's avatar
Laurent Montel committed
555 556 557
    if (position != 0) {
        emit rendererPosition(position);
    }
558
    return error;
559 560
}

561 562 563 564 565 566 567 568 569
void Render::checkMaxThreads()
{
    // Make sure we don't use too much threads, MLT avformat does not cope with too much threads
    // Currently, Kdenlive uses the following avformat threads:
    // One thread to get info when adding a clip
    // One thread to create the timeline video thumbnails
    // One thread to create the audio thumbnails
    Mlt::Service service(m_mltProducer->parent().get_service());
    if (service.type() != tractor_type) {
Laurent Montel's avatar
Laurent Montel committed
570
        qCWarning(KDENLIVE_LOG) << "// TRACTOR PROBLEM" << m_mltProducer->parent().get("mlt_service");
571 572 573 574
        return;
    }
    Mlt::Tractor tractor(service);
    int mltMaxThreads = mlt_service_cache_get_size(service.get_service(), "producer_avformat");
575
    int requestedThreads = tractor.count() + m_qmlView->realTime() + 2;
576 577
    if (requestedThreads > mltMaxThreads) {
        mlt_service_cache_set_size(service.get_service(), "producer_avformat", requestedThreads);
Laurent Montel's avatar
Laurent Montel committed
578
        //qCDebug(KDENLIVE_LOG)<<"// MLT threads updated to: "<<mlt_service_cache_get_size(service.get_service(), "producer_avformat");
579 580 581
    }
}

582
const QString Render::sceneList(const QString &root)
583
{
584
    QString playlist;
Laurent Montel's avatar
Laurent Montel committed
585
    qCDebug(KDENLIVE_LOG) << " * * *Setting document xml root: " << root;
586
    Mlt::Consumer xmlConsumer(*m_qmlView->profile(), "xml:kdenlive_playlist");
Laurent Montel's avatar
Laurent Montel committed
587
    if (!root.isEmpty()) {
588
        xmlConsumer.set("root", root.toUtf8().constData());
Laurent Montel's avatar
Laurent Montel committed
589
    }
Laurent Montel's avatar
Laurent Montel committed
590
    //qCDebug(KDENLIVE_LOG)<<" ++ + READY TO SAVE: "<<m_qmlView->profile()->width()<<" / "<<m_qmlView->profile()->description();
Laurent Montel's avatar
Laurent Montel committed
591 592 593
    if (!xmlConsumer.is_valid()) {
        return QString();
    }
594
    m_mltProducer->optimise();
595
    xmlConsumer.set("terminate_on_pause", 1);
596
    xmlConsumer.set("store", "kdenlive");
597 598 599
    // 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);
600
    Mlt::Producer prod(m_mltProducer->get_producer());
Laurent Montel's avatar
Laurent Montel committed
601 602 603
    if (!prod.is_valid()) {
        return QString();
    }
604
    xmlConsumer.connect(prod);
605
    xmlConsumer.run();
606
    playlist = QString::fromUtf8(xmlConsumer.get("kdenlive_playlist"));
607 608 609
    return playlist;
}

Laurent Montel's avatar
Laurent Montel committed
610
void Render::saveZone(const QString &projectFolder, QPoint zone)
611
{
612
    QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
613 614 615 616
    if (clipFolder.isEmpty()) {
        clipFolder = QDir::homePath();
    }
    QString url = QFileDialog::getSaveFileName(qApp->activeWindow(), i18n("Save Zone"), clipFolder, i18n("MLT playlist (*.mlt)"));
617 618 619
    if (url.isEmpty()) {
        return;
    }
620
    Mlt::Consumer xmlConsumer(*m_qmlView->profile(), ("xml:" + url).toUtf8().constData());
621
    xmlConsumer.set("terminate_on_pause", 1);
622
    m_mltProducer->optimise();
Laurent Montel's avatar
Laurent Montel committed
623
    qCDebug(KDENLIVE_LOG) << " - - -- - SAVE ZONE SERVICE: " << m_mltProducer->get("mlt_type");
624
    if (QString(m_mltProducer->get("mlt_type")) != QLatin1String("producer")) {
Laurent Montel's avatar
Laurent Montel committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
        // 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;
641 642 643
    }
}

644 645
double Render::fps() const
{
646 647
    return m_fps;
}
648

649 650
int Render::volume() const
{
Laurent Montel's avatar
Laurent Montel committed
651 652 653
    if (!m_mltConsumer || !m_mltProducer) {
        return -1;
    }
654
    if (m_mltConsumer->get("mlt_service") == QStringLiteral("multi")) {
655 656 657
        return ((int) 100 * m_mltConsumer->get_double("0.volume"));
    }
    return ((int) 100 * m_mltConsumer->get_double("volume"));
658 659
}

660 661
void Render::start()
{
662
    m_refreshTimer.stop();
663
    QMutexLocker locker(&m_mutex);
664
    /*if (m_winid == -1) {
Laurent Montel's avatar
Laurent Montel committed
665
        //qCDebug(KDENLIVE_LOG) << "-----  BROKEN MONITOR: " << m_name << ", RESTART";
666
        return;
667
    }*/
668
    if (!m_mltConsumer) {
Laurent Montel's avatar
Laurent Montel committed
669
        //qCDebug(KDENLIVE_LOG)<<" / - - - STARTED BEFORE CONSUMER!!!";
Laurent Montel's avatar
Laurent Montel committed
670
        return;
671
    }
672
    if (m_mltConsumer->is_stopped()) {
673
        if (m_mltConsumer->start() == -1) {
674
            //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."));
Laurent Montel's avatar
Laurent Montel committed
675
            qCWarning(KDENLIVE_LOG) << "/ / / / CANNOT START MONITOR";
676
        } else {
677
            m_mltConsumer->purge();
678
            m_isRefreshing = true;
679
            m_mltConsumer->set("refresh", 1);
680
        }
681 682 683
    }
}

684 685
void Render::stop()
{
686
    requestedSeekPosition = SEEK_INACTIVE;
687
    m_refreshTimer.stop();
688
    QMutexLocker locker(&m_mutex);
689
    m_isActive = false;
690
    if (m_mltProducer) {
Laurent Montel's avatar
Laurent Montel committed
691 692 693
        if (m_isZoneMode) {
            resetZoneMode();
        }
694 695
        m_mltProducer->set_speed(0.0);
    }
696
    if (m_mltConsumer) {
697
        m_mltConsumer->purge();
698 699 700
        if (!m_mltConsumer->is_stopped()) {
            m_mltConsumer->stop();
        }
701
    }
702
    m_isRefreshing = false;
703 704
}

Laurent Montel's avatar
Laurent Montel committed
705
void Render::stop(const GenTime &startTime)
706
{
707
    requestedSeekPosition = SEEK_INACTIVE;
708
    m_refreshTimer.stop();
709
    QMutexLocker locker(&m_mutex);
710
    m_isActive = false;
711
    if (m_mltProducer) {
Laurent Montel's avatar
Laurent Montel committed
712 713 714
        if (m_isZoneMode) {
            resetZoneMode();
        }
715 716
        m_mltProducer->set_speed(0.0);
        m_mltProducer->seek((int) startTime.frames(m_fps));
717
    }
718 719 720
    if (m_mltConsumer) {
        m_mltConsumer->purge();
    }
721
    m_isRefreshing = false;
722 723
}

724
/*void Render::pause()
725
{
726
    requestedSeekPosition = SEEK_INACTIVE;
727
    if (!m_mltProducer || !m_mltConsumer || !m_isActive)
728 729
        return;
    m_mltProducer->set_speed(0.0);
730
    //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
731
    //m_mltProducer->seek(m_mltConsumer->position());
732
}*/
733

734 735
void Render::setActiveMonitor()
{
Laurent Montel's avatar
Laurent Montel committed
736 737 738
    if (!m_isActive) {
        emit activateMonitor(m_name);
    }
739 740
}

741
void Render::switchPlay(bool play, double speed)
742
{
743
    QMutexLocker locker(&m_mutex);
744
    requestedSeekPosition = SEEK_INACTIVE;
Laurent Montel's avatar
Laurent Montel committed
745
    if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
746
	qWarning("no producer/consumer!");
747
        return;
Laurent Montel's avatar
Laurent Montel committed
748 749 750 751
    }
    if (m_isZoneMode) {
        resetZoneMode();
    }
752
    if (play) {
753
        double currentSpeed = m_mltProducer->get_speed();
754
        if (m_name == Kdenlive::ClipMonitor && m_mltConsumer->position() == m_mltProducer->get_out() && speed > 0) {
Laurent Montel's avatar
Laurent Montel committed
755 756
            m_mltProducer->seek(0);
        }
757
        m_mltProducer->set_speed(speed);
758 759 760
	m_mltConsumer->start();
	m_isRefreshing = true;
	m_mltConsumer->set("refresh", 1);
761
    } else {
762
        m_mltProducer->set_speed(0);
763
        m_mltProducer->seek(m_mltConsumer->position() + 1);
764
        m_mltConsumer->purge();
765
	m_mltConsumer->start();
766 767 768
    }
}

769 770
void Render::play(double speed)
{
771
    requestedSeekPosition = SEEK_INACTIVE;
Laurent Montel's avatar
Laurent Montel committed
772 773 774
    if (!m_mltProducer || !m_isActive) {
        return;
    }
775
    double current_speed = m_mltProducer->get_speed();
Laurent Montel's avatar
Laurent Montel committed
776 777 778 779 780 781
    if (current_speed == speed) {
        return;
    }
    if (m_isZoneMode) {
        resetZoneMode();
    }
782 783 784 785 786 787 788 789
    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();
        }
790
    }
791 792
    if (current_speed == 0) {
        m_mltConsumer->start();
793 794 795
        m_isRefreshing = true;
        m_mltConsumer->set("refresh", 1);
    }
796
    m_mltProducer->set_speed(speed);
797 798
}

Laurent Montel's avatar
Laurent Montel committed
799
void Render::play(const GenTime &startTime)
800
{
801
    requestedSeekPosition = SEEK_INACTIVE;
Laurent Montel's avatar
Laurent Montel committed
802
    if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
803
        return;
Laurent Montel's avatar
Laurent Montel committed
804
    }
805
    m_mltProducer->seek((int)(startTime.frames(m_fps)));
806
    m_mltProducer->set_speed(1.0);
807
    m_isRefreshing = true;
808
    m_mltConsumer->set("refresh", 1);
809 810
}

Laurent Montel's avatar
Laurent Montel committed
811
void Render::loopZone(const GenTime &startTime, const GenTime &stopTime)
812
{
813
    requestedSeekPosition = SEEK_INACTIVE;
Laurent Montel's avatar
Laurent Montel committed
814
    if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
815
        return;
Laurent Montel's avatar
Laurent Montel committed
816
    }
817 818 819 820 821 822
    //m_mltProducer->set("eof", "loop");
    m_isLoopMode = true;
    m_loopStart = startTime;
    playZone(startTime, stopTime);
}

Laurent Montel's avatar
Laurent Montel committed
823
bool Render::playZone(const GenTime &startTime, const GenTime &stopTime)
824
{
825
    requestedSeekPosition = SEEK_INACTIVE;
Laurent Montel's avatar
Laurent Montel committed
826
    if (!m_mltProducer || !m_mltConsumer || !m_isActive) {
827
        return false;
Laurent Montel's avatar
Laurent Montel committed
828
    }
829
    m_mltProducer->seek((int)(startTime.frames(m_fps)));
830 831 832
    m_mltProducer->set_speed(0);
    m_mltConsumer->purge();
    m_mltProducer->set("out", (int)(stopTime.frames(m_fps)));
833
    m_mltProducer->set_speed(1.0);
Laurent Montel's avatar
Laurent Montel committed
834 835 836
    if (m_mltConsumer->is_stopped()) {
        m_mltConsumer->start();
    }
837
    m_isRefreshing = true;
838
    m_mltConsumer->set("refresh", 1);
839
    m_isZoneMode = true;
840
    return true;
841 842
}

843 844
void Render::resetZoneMode()
{
Laurent Montel's avatar
Laurent Montel committed
845 846 847
    if (!m_isZoneMode && !m_isLoopMode) {
        return;
    }
848
    m_mltProducer->set("out", m_mltProducer->get_length());
849 850
    m_isZoneMode = false;
    m_isLoopMode = false;
851 852
}

853 854
void Render::seekToFrame(int pos)
{
Laurent Montel's avatar
Laurent Montel committed
855
    if (!m_mltProducer || !m_isActive) {
856
        return;
Laurent Montel's avatar
Laurent Montel committed
857
    }
858
    pos = qBound(0, pos - m_mltProducer->get_in(), m_mltProducer->get_length() - 1);
859
    seek(pos);
860 861
}

862 863
void Render::seekToFrameDiff(int diff)
{
864 865 866 867
    if (byPassSeek) {
        emit renderSeek(diff);
        return;
    }
Laurent Montel's avatar
Laurent Montel committed
868
    if (!m_mltProducer || !m_isActive) {
869
        return;
Laurent Montel's avatar
Laurent Montel committed
870
    }
871
    if (requestedSeekPosition == SEEK_INACTIVE) {
872
        seek(seekFramePosition() + diff);
Laurent Montel's avatar
Laurent Montel committed
873
    } else {
874 875
        seek(requestedSeekPosition + diff);
    }
876 877
}

878 879
void Render::doRefresh()
{
880
    if (m_mltProducer && (playSpeed() == 0) && m_isActive) {
Laurent Montel's avatar
Laurent Montel committed
881 882 883 884 885
        if (m_isRefreshing) {
            m_refreshTimer.start();
        } else {
            refresh();
        }
886
    }
887 888
}

889 890
void Render::refresh()
{
891
    m_refreshTimer.stop();
Laurent Montel's avatar
Laurent Montel committed
892
    if (!m_mltProducer || !m_isActive) {
893
        return;
Laurent Montel's avatar
Laurent Montel committed
894
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
895
    QMutexLocker locker(&m_mutex);
896
    if (m_mltConsumer) {
897
        m_isRefreshing = true;
Laurent Montel's avatar
Laurent Montel committed
898 899 900
        if (m_mltConsumer->is_stopped()) {
            m_mltConsumer->start();
        }
901
        m_mltConsumer->purge();
Laurent Montel's avatar
Laurent Montel committed
902
        m_mltConsumer->set("refresh", 1);
903 904 905
    }
}

906
void Render::setDropFrames(bool drop)
907
{
908
    QMutexLocker locker(&m_mutex);
909
    if (m_mltConsumer) {
910
        int dropFrames = m_qmlView->realTime();
Laurent Montel's avatar
Laurent Montel committed
911 912 913
        if (drop == false) {
            dropFrames = -dropFrames;
        }
914
        //m_mltConsumer->stop();
915
        m_mltConsumer->set("real_time", dropFrames);
916
        if (m_mltConsumer->start() == -1) {
Laurent Montel's avatar
Laurent Montel committed
917
            qCWarning(KDENLIVE_LOG) << "ERROR, Cannot start monitor";
918 919
        }

920 921 922
    }
}

923 924 925 926
void Render::setConsumerProperty(const QString &name, const QString &value)
{
    QMutexLocker locker(&m_mutex);
    if (m_mltConsumer) {
927
        //m_mltConsumer->stop();
928 929
        m_mltConsumer->set(name.toUtf8().constData(), value.toUtf8().constData());
        if (m_isActive && m_mltConsumer->start() == -1) {
Laurent Montel's avatar
Laurent Montel committed
930
            qCWarning(KDENLIVE_LOG) << "ERROR, Cannot start monitor";
931 932 933 934 935
        }

    }
}

936 937
bool Render::isPlaying() const
{
Laurent Montel's avatar
Laurent Montel committed
938 939 940
    if (!m_mltConsumer || m_mltConsumer->is_stopped()) {
        return false;
    }
941
    return playSpeed() != 0;
942 943
}

944
double Render::playSpeed() const
945
{
Laurent Montel's avatar
Laurent Montel committed
946 947 948
    if (m_mltProducer) {
        return m_mltProducer->get_speed();
    }
949 950 951
    return 0.0;
}

952 953
GenTime Render::seekPosition() const
{
Laurent Montel's avatar
Laurent Montel committed
954 955 956 957 958
    if (m_mltConsumer) {
        return GenTime((int) m_mltConsumer->position(), m_fps);
    } else {
        return GenTime();
    }
959 960
}

961 962
int Render::seekFramePosition() const
{
Laurent Montel's avatar
Laurent Montel committed
963
    if (m_mltProducer && m_mltProducer->get_speed() == 0) {
964
        return (int) m_mltProducer->position();
Laurent Montel's avatar
Laurent Montel committed
965 966 967 968
    }
    if (m_mltConsumer) {
        return (int) m_mltConsumer->position();
    }
969 970
    return 0;
}
971

Laurent Montel's avatar
Laurent Montel committed
972
void Render::emitFrameUpdated(Mlt::Frame &frame)
973
{
Vincent Pinon's avatar
Vincent Pinon committed
974
    Q_UNUSED(frame)
975
    return;
976
    /*TODO: fix movit crash
977
    mlt_image_format format = mlt_image_rgb24;
978 979
    int width = 0;
    int height = 0;
980 981 982
    //frame.set("rescale.interp", "bilinear");
    //frame.set("deinterlace_method", "onefield");
    //frame.set("top_field_first", -1);
983
    const uchar* image = frame.get_image(format, width, height);
984 985 986
    QImage qimage(width, height, QImage::Format_RGB888);  //Format_ARGB32_Premultiplied);
    memcpy(qimage.scanLine(0), image, width * height * 3);
    emit frameUpdated(qimage);
987
    */
988 989
}

990 991
int Render::getCurrentSeekPosition() const
{
Laurent Montel's avatar
Laurent Montel committed
992 993 994
    if (requestedSeekPosition != SEEK_INACTIVE) {
        return requestedSeekPosition;
    }
995
    return (int) m_mltConsumer->position();
996 997
}

998
bool Render::checkFrameNumber(int pos)