renderer.cpp 53.4 KB
Newer Older
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
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
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
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 27
#include "renderer.h"
#include "kdenlivesettings.h"
Vincent Pinon's avatar
Vincent Pinon committed
28
#include "doc/kthumb.h"
29
#include "definitions.h"
Vincent Pinon's avatar
Vincent Pinon committed
30
#include "project/dialogs/slideshowclip.h"
Vincent Pinon's avatar
Vincent Pinon committed
31
#include "dialogs/profilesdialog.h"
32
#include "mltcontroller/bincontroller.h"
33
#include "bin/projectclip.h"
34
#include "timeline/clip.h"
35
#include "monitor/glwidget.h"
36
#include "mltcontroller/clipcontroller.h"
37
#include "timeline/transitionhandler.h"
38
#include <mlt++/Mlt.h>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
39

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
40
#include <QDebug>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
41
#include <KMessageBox>
42
#include <KLocalizedString>
Vincent Pinon's avatar
Vincent Pinon committed
43
#include <QDialog>
44
#include <QPainter>
45
#include <QString>
46
#include <QApplication>
47 48
#include <QProcess>
#include <QtConcurrent>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
49

50 51
#include <cstdlib>
#include <cstdarg>
Vincent Pinon's avatar
Vincent Pinon committed
52
#include <KConfigGroup>
53
#include <KRecentDirs>
Vincent Pinon's avatar
Vincent Pinon committed
54 55 56
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
57

58
#define SEEK_INACTIVE (-1)
59

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

105 106
Render::~Render()
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
107 108 109
    closeMlt();
}

110
void Render::closeMlt()
111
{
Laurent Montel's avatar
Laurent Montel committed
112 113 114 115 116
    delete m_showFrameEvent;
    delete m_pauseEvent;
    delete m_mltConsumer;
    delete m_mltProducer;
    delete m_blackClip;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
117 118
}

119 120
void Render::slotSwitchFullscreen()
{
Laurent Montel's avatar
Laurent Montel committed
121 122
    if (m_mltConsumer)
        m_mltConsumer->set("full_screen", 1);
123 124
}

125
void Render::prepareProfileReset(double fps)
126
{
127
    m_refreshTimer.stop();
128
    m_fps = fps;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
129 130
}

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

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

149 150
void Render::seek(int time)
{
151
    resetZoneMode();
152
    time = qBound(0, time, m_mltProducer->get_length() - 1);
153
    if (requestedSeekPosition == SEEK_INACTIVE) {
Laurent Montel's avatar
Laurent Montel committed
154
        requestedSeekPosition = time;
155 156 157
        if (m_mltProducer->get_speed() != 0) {
            m_mltConsumer->purge();
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
158
        m_mltProducer->seek(time);
159
        if (!externalConsumer) {
160
            m_isRefreshing = true;
Laurent Montel's avatar
Laurent Montel committed
161 162
            m_mltConsumer->set("refresh", 1);
        }
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
163
    }
164 165 166
    else {
        requestedSeekPosition = time;
    }
167 168
}

169 170
int Render::frameRenderWidth() const
{
171
    return m_qmlView->profile()->width();
172 173
}

174 175
int Render::renderWidth() const
{
176
    return (int)(m_qmlView->profile()->height() * m_qmlView->profile()->dar() + 0.5);
177 178
}

179 180
int Render::renderHeight() const
{
181
    return m_qmlView->profile()->height();
182
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
183

Laurent Montel's avatar
Laurent Montel committed
184
QImage Render::extractFrame(int frame_position, const QString &path, int width, int height)
185
{
186
    if (width == -1) {
187
        width = frameRenderWidth();
188
        height = renderHeight();
189
    } else if (width % 2 == 1) width++;
190
    if (!path.isEmpty()) {
191
        Mlt::Producer *producer = new Mlt::Producer(*m_qmlView->profile(), path.toUtf8().constData());
192 193
        if (producer) {
            if (producer->is_valid()) {
194
                QImage img = KThumb::getFrame(producer, frame_position, width, height);
195 196 197 198 199 200
                delete producer;
                return img;
            }
            else delete producer;
        }
    }
201

202
    if (!m_mltProducer || !path.isEmpty()) {
203
        QImage pix(width, height, QImage::Format_RGB32);
204 205
        pix.fill(Qt::black);
        return pix;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
206
    }
207
    Mlt::Frame *frame = NULL;
208 209 210
    if (KdenliveSettings::gpu_accel()) {
        QString service = m_mltProducer->get("mlt_service");
        //TODO: create duplicate prod from xml data
211
        Mlt::Producer *tmpProd = new Mlt::Producer(*m_qmlView->profile(), service.toUtf8().constData(), path.toUtf8().constData());
212 213
        Mlt::Filter scaler(*m_qmlView->profile(), "swscale");
        Mlt::Filter converter(*m_qmlView->profile(), "avcolor_space");
214 215 216 217
        tmpProd->attach(scaler);
        tmpProd->attach(converter);
        tmpProd->seek(m_mltProducer->position());
        frame = tmpProd->get_frame();
218
        delete tmpProd;
219 220 221 222
    }
    else {
        frame = m_mltProducer->get_frame();
    }
223
    QImage img = KThumb::getFrame(frame, width, height);
224
    delete frame;
225
    return img;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
226 227
}

228 229
int Render::getLength()
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
230

231 232
    if (m_mltProducer) {
        return mlt_producer_get_playtime(m_mltProducer->get_producer());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
233 234 235 236
    }
    return 0;
}

237
bool Render::isValid(const QUrl &url)
238
{
239
    Mlt::Producer producer(*m_qmlView->profile(), url.toLocalFile().toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
240
    if (producer.is_blank())
241
        return false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
242 243 244 245

    return true;
}

246 247
double Render::dar() const
{
248
    return m_qmlView->profile()->dar();
249 250
}

251 252
double Render::sar() const
{
253
    return m_qmlView->profile()->sar();
254
}
255

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
256

257

258
#if 0
Alberto Villa's avatar
Alberto Villa committed
259
/** Create the producer from the MLT XML QDomDocument */
260 261
void Render::initSceneList()
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
262
    //qDebug() << "--------  INIT SCENE LIST ------_";
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
263
    QDomDocument doc;
264 265
    QDomElement mlt = doc.createElement("mlt");
    doc.appendChild(mlt);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
266 267 268 269 270 271 272 273
    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");
274
    QDomElement multitrack = doc.createElement("multitrack");
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
275 276 277 278 279 280 281 282 283 284 285 286 287

    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);
288
    mlt.appendChild(tractor);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
289
    // //qDebug()<<doc.toString();
290
    /*
291
       QString tmp = QString("<mlt><producer resource=\"colour\" colour=\"red\" id=\"red\" /><tractor><multitrack><playlist></playlist><playlist></playlist><playlist /><playlist /><playlist></playlist></multitrack></tractor></mlt>");*/
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
292 293
    setSceneList(doc, 0);
}
294
#endif
295

296 297
void Render::loadUrl(const QString &url)
{
298
    Mlt::Producer *producer = new Mlt::Producer(*m_qmlView->profile(), url.toUtf8().constData());
299
    setProducer(producer, 0, true);
300 301
}

Vincent Pinon's avatar
Vincent Pinon committed
302
bool Render::updateProducer(Mlt::Producer *producer)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
{
    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;
        m_mltProducer = NULL;
    }
    if (m_mltConsumer) {
        if (!m_mltConsumer->is_stopped()) {
            m_mltConsumer->stop();
        }
    }
    if (!producer || !producer->is_valid()) {
Vincent Pinon's avatar
Vincent Pinon committed
321
        return false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
322 323 324 325
    }
    m_fps = producer->get_fps();
    m_mltProducer = producer;
    if (m_qmlView) {
Vincent Pinon's avatar
Vincent Pinon committed
326
        m_qmlView->setProducer(producer);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
327 328
        m_mltConsumer = m_qmlView->consumer();
    }
Vincent Pinon's avatar
Vincent Pinon committed
329
    return true;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
330 331
}

Vincent Pinon's avatar
Vincent Pinon committed
332
bool Render::setProducer(Mlt::Producer *producer, int position, bool isActive)
333
{
334
    m_refreshTimer.stop();
335
    requestedSeekPosition = SEEK_INACTIVE;
336
    QMutexLocker locker(&m_mutex);
337 338
    QString currentId;
    int consumerPosition = 0;
339 340 341 342
    if (!producer && m_mltProducer && m_mltProducer->parent().get("id") == QLatin1String("black")) {
        // Black clip already displayed no need to refresh
        return true;
    }
343 344 345
    if (m_mltProducer) {
        currentId = m_mltProducer->get("id");
        m_mltProducer->set_speed(0);
346
        if (QString(m_mltProducer->get("resource")) == QLatin1String("<tractor>")) {
347 348 349 350 351 352
            // 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);
            }
        }
353 354 355
        delete m_mltProducer;
        m_mltProducer = NULL;
    }
356
    if (m_mltConsumer) {
357
        if (!m_mltConsumer->is_stopped()) {
Vincent Pinon's avatar
Vincent Pinon committed
358
            isActive = true;
359
            m_mltConsumer->stop();
360
        }
361
        consumerPosition = m_mltConsumer->position();
362
    }
363
    blockSignals(true);
364
    if (!producer || !producer->is_valid()) {
365
        producer = m_blackClip->cut(0,1);
366
    }
367

368 369 370
    emit stopped();
    if (position == -1 && producer->get("id") == currentId) position = consumerPosition;
    if (position != -1) producer->seek(position);
371
    m_fps = producer->get_fps();
372

373
    blockSignals(false);
374
    m_mltProducer = producer;
375
    m_mltProducer->set_speed(0);
376 377 378
    if (m_qmlView) {
        m_qmlView->setProducer(producer);
        m_mltConsumer = m_qmlView->consumer();
379
        //m_mltConsumer->set("refresh", 1);
380 381
    }
    //m_mltConsumer->connect(*producer);
382 383
    if (isActive) {
        startConsumer();
384
    }
385
    emit durationChanged(m_mltProducer->get_playtime() - 1, m_mltProducer->get_in());
386 387
    position = m_mltProducer->position();
    emit rendererPosition(position);
Vincent Pinon's avatar
Vincent Pinon committed
388
    return true;
389 390 391
}

void Render::startConsumer() {
Laurent Montel's avatar
Laurent Montel committed
392
    if (m_mltConsumer->is_stopped() && m_mltConsumer->start() == -1) {
393 394 395 396 397 398 399 400
        // 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 = NULL;
        if (m_pauseEvent) delete m_pauseEvent;
        m_pauseEvent = NULL;
        delete m_mltConsumer;
        m_mltConsumer = NULL;
401
        return;
402
    }
403
    m_isRefreshing = true;
404
    m_mltConsumer->set("refresh", 1);
405
    m_isActive = true;
406 407
}

Laurent Montel's avatar
Laurent Montel committed
408
int Render::setSceneList(const QDomDocument &list, int position)
409
{
410
    return setSceneList(list.toString(), position);
411 412
}

413
int Render::setSceneList(QString playlist, int position)
414
{
415
    requestedSeekPosition = SEEK_INACTIVE;
416
    m_refreshTimer.stop();
417
    QMutexLocker locker(&m_mutex);
418
    //if (m_winid == -1) return -1;
419
    int error = 0;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
420

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
421
    //qDebug() << "//////  RENDER, SET SCENE LIST:\n" << playlist <<"\n..........:::.";
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
422

423 424 425
    // Remove previous profile info
    QDomDocument doc;
    doc.setContent(playlist);
426
    QDomElement profile = doc.documentElement().firstChildElement(QStringLiteral("profile"));
427 428 429
    doc.documentElement().removeChild(profile);
    playlist = doc.toString();

430 431
    if (m_mltConsumer) {
        if (!m_mltConsumer->is_stopped()) {
432
            m_mltConsumer->stop();
433 434
        }
    } else {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
435
        qWarning() << "///////  ERROR, TRYING TO USE NULL MLT CONSUMER";
436
        error = -1;
437 438
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
439
    if (m_mltProducer) {
440
        m_mltProducer->set_speed(0);
Vincent Pinon's avatar
Vincent Pinon committed
441
        qDeleteAll(m_slowmotionProducers);
442
        m_slowmotionProducers.clear();
443

444 445 446
        delete m_mltProducer;
        m_mltProducer = NULL;
        emit stopped();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
447
    }
448
    m_binController->destroyBin();
449
    blockSignals(true);
450
    m_locale = QLocale();
451
    m_locale.setNumberOptions(QLocale::OmitGroupSeparator);
452 453
    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());
454
    if (!m_mltProducer || !m_mltProducer->is_valid()) {
455
        qDebug() << " WARNING - - - - -INVALID PLAYLIST: " << playlist.toUtf8().constData();
456
        m_mltProducer = m_blackClip->cut(0, 1);
457
        error = -1;
458
    }
459
    m_mltProducer->set("eof", "pause");
460
    checkMaxThreads();
461
    m_mltProducer->optimise();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
462

463
    m_fps = m_mltProducer->get_fps();
464
    if (position != 0) {
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
465
        // Seek to correct place after opening project.
466
        m_mltProducer->seek(position);
467
    }
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
468

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

472 473 474 475 476
    // Fill Bin's playlist
    Mlt::Service service(m_mltProducer->parent().get_service());
    if (service.type() != tractor_type) {
        qWarning() << "// TRACTOR PROBLEM";
    }
477
    blockSignals(false);
478 479
    Mlt::Tractor tractor(service);
    Mlt::Properties retainList((mlt_properties) tractor.get_data("xml_retain"));
480 481
    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()));
482 483
        if (playlist.is_valid() && playlist.type() == playlist_type) {
            // Load bin clips
484
	    m_binController->initializeBin(playlist);
485
        }
486 487
    }
    // No Playlist found, create new one
488
    if (m_qmlView) {
489
        m_binController->createIfNeeded(m_qmlView->profile());
490
        QString retain = QStringLiteral("xml_retain %1").arg(m_binController->binPlaylistId());
491 492
        tractor.set(retain.toUtf8().constData(), m_binController->service(), 0);
        //if (!m_binController->hasClip("black")) m_binController->addClipToBin("black", *m_blackClip);
493 494 495
        m_qmlView->setProducer(m_mltProducer);
        m_mltConsumer = m_qmlView->consumer();
    }
496

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
497
    //qDebug() << "// NEW SCENE LIST DURATION SET TO: " << m_mltProducer->get_playtime();
498
    //m_mltConsumer->connect(*m_mltProducer);
499
    m_mltProducer->set_speed(0);
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
500
    fillSlowMotionProducers();
501
    emit durationChanged(m_mltProducer->get_playtime() - 1);
502

503
    // Fill bin
504 505
    QStringList ids = m_binController->getClipIds();
    foreach(const QString &id, ids) {
506 507 508 509 510 511 512 513
        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));
514 515
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
516
    ////qDebug()<<"// SETSCN LST, POS: "<<position;
517 518
    if (position != 0) emit rendererPosition(position);
    return error;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
519 520
}

521 522 523 524 525 526 527 528 529
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) {
530
        qWarning() << "// TRACTOR PROBLEM"<<m_mltProducer->parent().get("mlt_service");
531 532 533 534
        return;
    }
    Mlt::Tractor tractor(service);
    int mltMaxThreads = mlt_service_cache_get_size(service.get_service(), "producer_avformat");
535
    int requestedThreads = tractor.count() + m_qmlView->realTime() + 2;
536 537
    if (requestedThreads > mltMaxThreads) {
        mlt_service_cache_set_size(service.get_service(), "producer_avformat", requestedThreads);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
538
        //qDebug()<<"// MLT threads updated to: "<<mlt_service_cache_get_size(service.get_service(), "producer_avformat");
539 540 541
    }
}

542

543 544
const QString Render::sceneList()
{
545
    QString playlist;
546
    Mlt::Consumer xmlConsumer(*m_qmlView->profile(), "xml:kdenlive_playlist");
547
    //qDebug()<<" ++ + READY TO SAVE: "<<m_qmlView->profile()->width()<<" / "<<m_qmlView->profile()->description();
548
    if (!xmlConsumer.is_valid()) return QString();
549
    m_mltProducer->optimise();
550
    xmlConsumer.set("terminate_on_pause", 1);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
551
    xmlConsumer.set("store", "kdenlive");
552 553 554
    // 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);
555
    Mlt::Producer prod(m_mltProducer->get_producer());
556
    if (!prod.is_valid()) return QString();
557
    xmlConsumer.connect(prod);
558
    xmlConsumer.run();
559
    playlist = QString::fromUtf8(xmlConsumer.get("kdenlive_playlist"));
560 561 562
    return playlist;
}

563 564
bool Render::saveSceneList(QString path, QDomElement kdenliveData)
{
565 566 567
    QFile file(path);
    QDomDocument doc;
    doc.setContent(sceneList(), false);
568
    if (doc.isNull()) return false;
569 570
    QDomElement root = doc.documentElement();
    if (!kdenliveData.isNull() && !root.isNull()) {
571
        // add Kdenlive specific tags
572
        root.appendChild(doc.importNode(kdenliveData, true));
573
    }
574
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
575
        qWarning() << "//////  ERROR writing to file: " << path;
576
        return false;
577
    }
578
    file.write(doc.toString().toUtf8());
579 580 581 582
    if (file.error() != QFile::NoError) {
        file.close();
        return false;
    }
583
    file.close();
584
    return true;
585 586
}

587
void Render::saveZone(QPoint zone)
588
{
589
    QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
590 591 592 593 594
    if (clipFolder.isEmpty()) {
        clipFolder = QDir::homePath();
    }
    QString url = QFileDialog::getSaveFileName(qApp->activeWindow(), i18n("Save Zone"), clipFolder, i18n("MLT playlist (*.mlt)"));
    Mlt::Consumer xmlConsumer(*m_qmlView->profile(), ("xml:" + url).toUtf8().constData());
595
    xmlConsumer.set("terminate_on_pause", 1);
596 597
    m_mltProducer->optimise();
    qDebug()<<" - - -- - SAVE ZONE SEVICE: "<<m_mltProducer->get("mlt_type");
598
    if (QString(m_mltProducer->get("mlt_type")) != QLatin1String("producer")) {
599 600 601 602 603 604 605 606 607 608 609
	// TODO: broken
	QString scene = sceneList();
	Mlt::Producer duplicate(*m_mltProducer->profile(), "xml-string", scene.toUtf8().constData());
	duplicate.set_in_and_out(zone.x(), zone.y());
	qDebug()<<"/// 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());
610
	Mlt::Playlist list(*m_mltProducer->profile());
611 612 613 614 615
	list.insert_at(0, *prod2, 0);
	//list.set("title", desc.toUtf8().constData());
	xmlConsumer.connect(list);
	xmlConsumer.run();
	delete prod2;
616 617 618
    }
}

619 620
double Render::fps() const
{
621 622
    return m_fps;
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
623

624 625 626
int Render::volume() const
{
    if (!m_mltConsumer || !m_mltProducer) return -1;
627
    if (m_mltConsumer->get("mlt_service") == QStringLiteral("multi")) {
628 629 630
        return ((int) 100 * m_mltConsumer->get_double("0.volume"));
    }
    return ((int) 100 * m_mltConsumer->get_double("volume"));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
631 632
}

633 634
void Render::start()
{
635
    m_refreshTimer.stop();
636
    QMutexLocker locker(&m_mutex);
637
    /*if (m_winid == -1) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
638
        //qDebug() << "-----  BROKEN MONITOR: " << m_name << ", RESTART";
639
        return;
640
    }*/
641
    if (!m_mltConsumer) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
642
        //qDebug()<<" / - - - STARTED BEFORE CONSUMER!!!";
Laurent Montel's avatar
Laurent Montel committed
643
        return;
644
    }
645
    if (m_mltConsumer->is_stopped()) {
646
        if (m_mltConsumer->start() == -1) {
647
            //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."));
648
            qWarning() << "/ / / / CANNOT START MONITOR";
649
        } else {
650
            m_mltConsumer->purge();
651
            m_isRefreshing = true;
652
            m_mltConsumer->set("refresh", 1);
653
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
654 655 656
    }
}

657 658
void Render::stop()
{
659
    requestedSeekPosition = SEEK_INACTIVE;
660
    m_refreshTimer.stop();
661
    QMutexLocker locker(&m_mutex);
662
    m_isActive = false;
663 664 665 666
    if (m_mltProducer) {
        if (m_isZoneMode) resetZoneMode();
        m_mltProducer->set_speed(0.0);
    }
667
    if (m_mltConsumer) {
668
        m_mltConsumer->purge();
669 670 671
        if (!m_mltConsumer->is_stopped()) {
            m_mltConsumer->stop();
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
672
    }
673
    m_isRefreshing = false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
674 675
}

676 677
void Render::stop(const GenTime & startTime)
{
678
    requestedSeekPosition = SEEK_INACTIVE;
679
    m_refreshTimer.stop();
680
    QMutexLocker locker(&m_mutex);
681
    m_isActive = false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
682
    if (m_mltProducer) {
683
        if (m_isZoneMode) resetZoneMode();
684 685
        m_mltProducer->set_speed(0.0);
        m_mltProducer->seek((int) startTime.frames(m_fps));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
686
    }
Simon Eugster's avatar
Simon Eugster committed
687 688 689
    if (m_mltConsumer) {
        m_mltConsumer->purge();
    }
690
    m_isRefreshing = false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
691 692
}

693
/*void Render::pause()
694
{
695
    requestedSeekPosition = SEEK_INACTIVE;
696
    if (!m_mltProducer || !m_mltConsumer || !m_isActive)
697 698
        return;
    m_mltProducer->set_speed(0.0);
699
    //if (!m_mltConsumer->is_stopped()) m_mltConsumer->stop();
700
    //m_mltProducer->seek(m_mltConsumer->position());
701
}*/
702

703 704
void Render::setActiveMonitor()
{
Laurent Montel's avatar
Laurent Montel committed
705
    if (!m_isActive) emit activateMonitor(m_name);
706 707
}

708
void Render::switchPlay(bool play, double speed)
709
{
710
    QMutexLocker locker(&m_mutex);
711
    requestedSeekPosition = SEEK_INACTIVE;
712
    if (!m_mltProducer || !m_mltConsumer || !m_isActive)
713
        return;
714
    if (m_isZoneMode) resetZoneMode();
715
    if (play) {
716
        double currentSpeed = m_mltProducer->get_speed();
717
        if (m_name == Kdenlive::ClipMonitor && m_mltConsumer->position() == m_mltProducer->get_out()) m_mltProducer->seek(0);
718 719
        if (m_mltConsumer->get_int("real_time") != m_qmlView->realTime()) {
            m_mltConsumer->set("real_time", m_qmlView->realTime());
720 721 722
            m_mltConsumer->set("buffer", 25);
            m_mltConsumer->set("prefill", 1);
            // Changes to real_time require a consumer restart if running.
723
            if (!m_mltConsumer->is_stopped()) {
724
                m_mltConsumer->stop();
725
            }
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
726
        }
727 728 729 730 731 732 733 734
        if (currentSpeed == 0) {
            m_mltConsumer->start();
            m_isRefreshing = true;
            m_mltConsumer->set("refresh", 1);
        } else {
            m_mltConsumer->purge();
        }
        m_mltProducer->set_speed(speed);
735
    } else {
736
        m_mltConsumer->purge();
737
        m_mltProducer->set_speed(0.0);
738 739
        m_mltConsumer->set("buffer", 0);
        m_mltConsumer->set("prefill", 0);
740 741 742
        m_mltConsumer->set("real_time", -1);
        m_mltProducer->seek(m_mltConsumer->position() + 1);
        m_mltConsumer->start();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
743 744 745
    }
}

746 747
void Render::play(double speed)
{
748
    requestedSeekPosition = SEEK_INACTIVE;
749
    if (!m_mltProducer || !m_isActive) return;
750
    double current_speed = m_mltProducer->get_speed();
751
    if (current_speed == speed) return;
752
    if (m_isZoneMode) resetZoneMode();
753 754 755 756 757 758 759 760
    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();
        }
761
    }
762 763
    if (current_speed == 0) {
        m_mltConsumer->start();
764 765 766
        m_isRefreshing = true;
        m_mltConsumer->set("refresh", 1);
    }
767
    m_mltProducer->set_speed(speed);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
768 769
}

770 771
void Render::play(const GenTime & startTime)
{
772
    requestedSeekPosition = SEEK_INACTIVE;
773
    if (!m_mltProducer || !m_mltConsumer || !m_isActive)
774 775
        return;
    m_mltProducer->seek((int)(startTime.frames(m_fps)));
776
    m_mltProducer->set_speed(1.0);
777
    m_isRefreshing = true;
778
    m_mltConsumer->set("refresh", 1);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
779 780
}

781 782
void Render::loopZone(const GenTime & startTime, const GenTime & stopTime)
{
783
    requestedSeekPosition = SEEK_INACTIVE;
784
    if (!m_mltProducer || !m_mltConsumer || !m_isActive)
785 786 787 788 789 790 791
        return;
    //m_mltProducer->set("eof", "loop");
    m_isLoopMode = true;
    m_loopStart = startTime;
    playZone(startTime, stopTime);
}

Simon Eugster's avatar
Simon Eugster committed
792
bool Render::playZone(const GenTime & startTime, const GenTime & stopTime)
793
{
794
    requestedSeekPosition = SEEK_INACTIVE;
795
    if (!m_mltProducer || !m_mltConsumer || !m_isActive)
Simon Eugster's avatar
Simon Eugster committed
796
        return false;
797
    m_mltProducer->seek((int)(startTime.frames(m_fps)));
798 799 800
    m_mltProducer->set_speed(0);
    m_mltConsumer->purge();
    m_mltProducer->set("out", (int)(stopTime.frames(m_fps)));
801
    m_mltProducer->set_speed(1.0);
802
    if (m_mltConsumer->is_stopped()) m_mltConsumer->start();
803
    m_isRefreshing = true;
804
    m_mltConsumer->set("refresh", 1);
805
    m_isZoneMode = true;
Simon Eugster's avatar
Simon Eugster committed
806
    return true;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
807 808
}

809 810
void Render::resetZoneMode()
{
811
    if (!m_isZoneMode && !m_isLoopMode) return;
812
    m_mltProducer->set("out", m_mltProducer->get_length());
813 814
    m_isZoneMode = false;
    m_isLoopMode = false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
815 816
}

817 818
void Render::seekToFrame(int pos)
{
819
    if (!m_mltProducer || !m_isActive)
820
        return;
821
    pos = qBound(0, pos - m_mltProducer->get_in(), m_mltProducer->get_length() - 1);
822
    seek(pos);
823 824
}

825 826
void Render::seekToFrameDiff(int diff)
{
827
    if (!m_mltProducer || !m_isActive)
828
        return;
829 830 831 832 833 834
    if (requestedSeekPosition == SEEK_INACTIVE) {
        seek(m_mltConsumer->position() + diff);
    }
    else {
        seek(requestedSeekPosition + diff);
    }
835 836
}

837 838
void Render::doRefresh()
{
839
    if (m_mltProducer && (playSpeed() == 0) && m_isActive) {
840 841
        if (m_isRefreshing) m_refreshTimer.start();
        else refresh();
842
    }
843 844
}

845 846
void Render::refresh()
{
847
    m_refreshTimer.stop();
848
    if