renderer.cpp 137 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 "monitor/glwidget.h"
35
#include "mltcontroller/clipcontroller.h"
36
#include <mlt++/Mlt.h>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
37

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

48 49
#include <cstdlib>
#include <cstdarg>
Vincent Pinon's avatar
Vincent Pinon committed
50 51 52 53
#include <KConfigGroup>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
54

55
#define SEEK_INACTIVE (-1)
56

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

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


108
void Render::closeMlt()
109
{
110 111
    m_requestList.clear();
    m_infoThread.waitForFinished();
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 126
Mlt::Producer *Render::invalidProducer(const QString &id)
{
127
    Mlt::Producer *clip;
128
    QString txt = '+' + i18n("Missing clip") + ".txt";
129
    char *tmp = qstrdup(txt.toUtf8().constData());
130
    clip = new Mlt::Producer(*m_qmlView->profile(), tmp);
131
    delete[] tmp;
Laurent Montel's avatar
Laurent Montel committed
132
    if (clip == NULL) {
133
        clip = new Mlt::Producer(*m_qmlView->profile(), "colour", "red");
Laurent Montel's avatar
Laurent Montel committed
134
    } else {
135 136 137
        clip->set("bgcolour", "0xff0000ff");
        clip->set("pad", "10");
    }
138
    clip->set("id", id.toUtf8().constData());
139 140 141 142
    clip->set("mlt_type", "producer");
    return clip;
}

143
void Render::prepareProfileReset()
144
{
145
    m_refreshTimer.stop();
146
    if (m_isSplitView)
Laurent Montel's avatar
Laurent Montel committed
147
            slotSplitView(false);
148 149
    m_requestList.clear();
    m_infoThread.waitForFinished();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
150 151
}

152

Laurent Montel's avatar
Laurent Montel committed
153
void Render::seek(const GenTime &time)
154
{
155
    if (!m_mltProducer || !m_isActive)
156
        return;
157 158
    int pos = time.frames(m_fps);
    seek(pos);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
159 160
}

161 162
void Render::seek(int time)
{
163
    resetZoneMode();
164
    if (requestedSeekPosition == SEEK_INACTIVE) {
Laurent Montel's avatar
Laurent Montel committed
165 166
        requestedSeekPosition = time;
        m_mltProducer->seek(time);
167
        m_mltConsumer->purge();
168
        if (!externalConsumer) {
Laurent Montel's avatar
Laurent Montel committed
169 170
            m_mltConsumer->set("refresh", 1);
        }
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
171
    }
172 173 174
    else {
        requestedSeekPosition = time;
    }
175 176
}

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

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

187 188
int Render::renderHeight() const
{
189
    return m_qmlView->profile()->height();
190
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
191

Laurent Montel's avatar
Laurent Montel committed
192
QImage Render::extractFrame(int frame_position, const QString &path, int width, int height)
193
{
194
    if (width == -1) {
195
        width = frameRenderWidth();
196
        height = renderHeight();
197
    } else if (width % 2 == 1) width++;
198
    if (!path.isEmpty()) {
199
        Mlt::Producer *producer = new Mlt::Producer(*m_qmlView->profile(), path.toUtf8().constData());
200 201
        if (producer) {
            if (producer->is_valid()) {
202
                QImage img = KThumb::getFrame(producer, frame_position, width, height);
203 204 205 206 207 208
                delete producer;
                return img;
            }
            else delete producer;
        }
    }
209

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

236 237
int Render::getLength()
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
238

239
    if (m_mltProducer) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
240
        // //qDebug()<<"//////  LENGTH: "<<mlt_producer_get_playtime(m_mltProducer->get_producer());
241
        return mlt_producer_get_playtime(m_mltProducer->get_producer());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
242 243 244 245
    }
    return 0;
}

246
bool Render::isValid(const QUrl &url)
247
{
248
    Mlt::Producer producer(*m_qmlView->profile(), url.toLocalFile().toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
249
    if (producer.is_blank())
250
        return false;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
251 252 253 254

    return true;
}

255 256
double Render::dar() const
{
257
    return m_qmlView->profile()->dar();
258 259
}

260 261
double Render::sar() const
{
262
    return m_qmlView->profile()->sar();
263
}
264

265 266
void Render::slotSplitView(bool doit)
{
267
    m_isSplitView = doit;
268 269
    Mlt::Service service(m_mltProducer->parent().get_service());
    Mlt::Tractor tractor(service);
270
    if (service.type() != tractor_type || tractor.count() < 2) return;
271 272
    Mlt::Field *field = tractor.field();
    if (doit) {
273
        for (int i = 1, screen = 0; i < tractor.count() && screen < 4; ++i) {
274
            Mlt::Producer trackProducer(tractor.track(i));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
275
            //qDebug() << "// TRACK: " << i << ", HIDE: " << trackProducer.get("hide");
276
            if (QString(trackProducer.get("hide")).toInt() != 1) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
277
                //qDebug() << "// ADIDNG TRACK: " << i;
278
                Mlt::Transition *transition = new Mlt::Transition(*m_qmlView->profile(), "composite");
279 280 281
                transition->set("mlt_service", "composite");
                transition->set("a_track", 0);
                transition->set("b_track", i);
282
                transition->set("distort", 0);
Laurent Montel's avatar
Laurent Montel committed
283
                transition->set("aligned", 0);
284
                transition->set("internal_added", "200");
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
285
                QString geometry;
286 287
                switch (screen) {
                case 0:
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
288
                    geometry = "0/0:50%x50%";
289 290
                    break;
                case 1:
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
291
                    geometry = "50%/0:50%x50%";
292 293
                    break;
                case 2:
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
294
                    geometry = "0/50%:50%x50%";
295 296
                    break;
                case 3:
297
                default:
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
298
                    geometry = "50%/50%:50%x50%";
299 300
                    break;
                }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
301
                transition->set("geometry", geometry.toUtf8().constData());
302 303 304 305 306
                transition->set("always_active", "1");
                field->plant_transition(*transition, 0, i);
                screen++;
            }
        }
307
        m_mltConsumer->set("refresh", 1);
308 309 310 311 312 313
    } else {
        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");
Laurent Montel's avatar
Laurent Montel committed
314
        mlt_service nextservicetodisconnect;
315 316 317 318

        while (mlt_type == "transition") {
            QString added = mlt_properties_get(MLT_SERVICE_PROPERTIES(nextservice), "internal_added");
            if (added == "200") {
Laurent Montel's avatar
Laurent Montel committed
319 320 321
                nextservicetodisconnect = nextservice;
                nextservice = mlt_service_producer(nextservice);
                mlt_field_disconnect_service(field->get_field(), nextservicetodisconnect);
322
            }
323
            else nextservice = mlt_service_producer(nextservice);
324 325 326 327
            if (nextservice == NULL) break;
            properties = MLT_SERVICE_PROPERTIES(nextservice);
            mlt_type = mlt_properties_get(properties, "mlt_type");
            resource = mlt_properties_get(properties, "mlt_service");
328
            m_mltConsumer->set("refresh", 1);
329 330 331 332
        }
    }
}

333 334
void Render::getFileProperties(const QDomElement &xml, const QString &clipId, int imageHeight, bool replaceProducer)
{
335 336 337
    // Make sure we don't request the info for same clip twice
    m_infoMutex.lock();
    if (m_processingClipId.contains(clipId)) {
Laurent Montel's avatar
Laurent Montel committed
338 339
        m_infoMutex.unlock();
        return;
340
    }
341
    for (int i = 0; i < m_requestList.count(); ++i) {
Laurent Montel's avatar
Laurent Montel committed
342 343 344 345 346
        if (m_requestList.at(i).clipId == clipId) {
            // Clip is already queued
            m_infoMutex.unlock();
            return;
        }
347
    }
348 349 350 351 352 353
    requestClipInfo info;
    info.xml = xml;
    info.clipId = clipId;
    info.imageHeight = imageHeight;
    info.replaceProducer = replaceProducer;
    m_requestList.append(info);
354
    m_infoMutex.unlock();
355
    if (!m_infoThread.isRunning()) {
356
        m_infoThread = QtConcurrent::run(this, &Render::processFileProperties);
357
    }
358 359 360 361
}

void Render::forceProcessing(const QString &id)
{
362 363 364
    // Make sure we load the clip producer now so that we can use it in timeline
    QList <requestClipInfo> requestListCopy;
    if (m_processingClipId.contains(id)) {
365
        m_infoMutex.lock();
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
	requestListCopy = m_requestList;
	m_requestList.clear();
	m_infoMutex.unlock();
	m_infoThread.waitForFinished();
	emit infoProcessingFinished();
    } else {
	m_infoMutex.lock();
	for (int i = 0; i < m_requestList.count(); ++i) {
	    requestClipInfo info = m_requestList.at(i);
	    if (info.clipId == id) {
		m_requestList.removeAt(i);
		requestListCopy = m_requestList;
		m_requestList.clear();
		m_requestList.append(info);
		break;
	    }
382
        }
383 384 385 386 387 388
        m_infoMutex.unlock();
	if (!m_infoThread.isRunning()) {
	    m_infoThread = QtConcurrent::run(this, &Render::processFileProperties);
	}
	m_infoThread.waitForFinished();
	emit infoProcessingFinished();
389
    }
390 391 392 393 394 395 396
    
    m_infoMutex.lock();
    m_requestList.append(requestListCopy);
    m_infoMutex.unlock();
    if (!m_infoThread.isRunning()) {
        m_infoThread = QtConcurrent::run(this, &Render::processFileProperties);
    }
397 398
}

399
int Render::processingItems()
400
{
401
    QMutexLocker lock(&m_infoMutex);
Laurent Montel's avatar
Laurent Montel committed
402
    const int count = m_requestList.count() + m_processingClipId.count();
403 404 405
    return count;
}

406
void Render::slotProcessingDone(const QString &id)
407 408 409 410 411
{
    QMutexLocker lock(&m_infoMutex);
    m_processingClipId.removeAll(id);
}

412 413
bool Render::isProcessing(const QString &id)
{
414
    if (m_processingClipId.contains(id)) return true;
415
    QMutexLocker lock(&m_infoMutex);
416
    for (int i = 0; i < m_requestList.count(); ++i) {
417
        if (m_requestList.at(i).clipId == id) {
418 419 420 421 422 423
            return true;
        }
    }
    return false;
}

424
ClipType Render::getTypeForService(const QString &id, const QString &path) const
425
{
426 427 428 429 430
    if (id.isEmpty()) {
        QString ext = path.section(".", -1);
        if (ext == "mlt" || ext == "kdenlive") return Playlist;
        return Unknown;
    }
431 432
    if (id == "color" || id == "colour") return Color;
    if (id == "kdenlivetitle") return Text;
433
    if (id == "xml" || id == "consumer") return Playlist;
434 435 436 437
    if (id == "webvfx") return WebVfx;
    return Unknown;
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
438 439
void Render::processProducerProperties(Mlt::Producer *prod, QDomElement xml)
{
440
    //TODO: there is some duplication with clipcontroller > updateproducer that alsa copies properties 
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
441 442
    QString value;
    QStringList internalProperties;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
443
    internalProperties << "bypassDuplicate" << "resource" << "mlt_service";
444 445 446 447 448 449 450
    QDomNodeList props;
    if (xml.tagName() == "producer") {
	props = xml.elementsByTagName("property");
    }
    else {
	props = xml.firstChildElement("producer").elementsByTagName("property");
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
451 452 453 454 455 456 457 458 459
    for (int i = 0; i < props.count(); ++i) {
        QString propertyName = props.at(i).toElement().attribute("name");
        if (!internalProperties.contains(propertyName)) {
            value = props.at(i).firstChild().nodeValue();
            prod->set(propertyName.toUtf8().constData(), value.toUtf8().constData());
        }
    }
}

460
void Render::processFileProperties()
461
{
462
    requestClipInfo info;
463
    QLocale locale;
464
    locale.setNumberOptions(QLocale::OmitGroupSeparator);
465 466 467
    ProfileInfo profileinfo;
    profileinfo.profileSize = QSize(frameRenderWidth(), renderHeight());
    profileinfo.profileFps = m_fps;
468

469 470 471
    while (!m_requestList.isEmpty()) {
        m_infoMutex.lock();
        info = m_requestList.takeFirst();
472 473

        if (info.xml.hasAttribute("thumbnailOnly")) {
474
            m_infoMutex.unlock();
475
            // Special case, we just want the thumbnail for existing producer
476
            Mlt::Producer *prod = new Mlt::Producer(*m_binController->getBinProducer(info.clipId));
477 478 479
	    if (!prod) {
		continue;
	    }
480 481 482
            // Check if we are using GPU accel, then we need to use alternate producer
            if (KdenliveSettings::gpu_accel()) {
                QString service = prod->get("mlt_service");
483 484 485
                QString res = prod->get("resource");
                delete prod;
                prod = new Mlt::Producer(*m_qmlView->profile(), service.toUtf8().constData(), res.toUtf8().constData());
486 487
                Mlt::Filter scaler(*m_qmlView->profile(), "swscale");
                Mlt::Filter converter(*m_qmlView->profile(), "avcolor_space");
488 489 490
                prod->attach(scaler);
                prod->attach(converter);
            }
491
            int frameNumber = ProjectClip::getXmlProperty(info.xml, "kdenlive:thumbnailFrame", "-1").toInt();
492 493 494
            if (frameNumber > 0) prod->seek(frameNumber);
            Mlt::Frame *frame = prod->get_frame();
            if (frame && frame->is_valid()) {
495
                int fullWidth = (int)((double) info.imageHeight * m_qmlView->profile()->dar() + 0.5);
496
                QImage img = KThumb::getFrame(frame, fullWidth, info.imageHeight);
497 498 499 500
                emit replyGetImage(info.clipId, img);
            }
            delete frame;
            delete prod;
501
            continue;
502
        }
503
        m_processingClipId.append(info.clipId);
504
        m_infoMutex.unlock();
505 506
        //TODO: read all xml meta.kdenlive properties into a QMap or an MLT::Properties and pass them to the newly created producer

507 508
        QString path;
        bool proxyProducer;
509
        QString proxy = ProjectClip::getXmlProperty(info.xml, "kdenlive:proxy");
510 511
        if (!proxy.isEmpty()) {
            if (proxy == "-") {
512
                path = ProjectClip::getXmlProperty(info.xml, "kdenlive:originalurl");
513 514
                proxyProducer = false;
            }
515 516 517 518 519 520 521 522 523 524 525 526
            else {
                path = proxy;
                // Check for missing proxies
                if (QFileInfo(path).size() <= 0) {
                    // proxy is missing, re-create it
                    emit requestProxy(info.clipId);
                    proxyProducer = false;
                    //path = info.xml.attribute("resource");
                    path = ProjectClip::getXmlProperty(info.xml, "resource");
                }
                else proxyProducer = true;
            }
527 528
        }
        else {
529 530
	    path = ProjectClip::getXmlProperty(info.xml, "resource");
            //path = info.xml.attribute("resource");
531 532
            proxyProducer = false;
        }
533
        //qDebug()<<" / / /CHECKING PRODUCER PATH: "<<path;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
534
        QUrl url = QUrl::fromLocalFile(path);
535
        Mlt::Producer *producer = NULL;
536
        ClipType type = (ClipType)info.xml.attribute("type").toInt();
537
        if (type == Unknown) {
538
            type = getTypeForService(ProjectClip::getXmlProperty(info.xml, "mlt_service"), path);
539
        }
540
        if (type == Color) {
541
            path.prepend("color:");
542
            producer = new Mlt::Producer(*m_qmlView->profile(), 0, path.toUtf8().constData());
543
        } else if (type == Text) {
544
            path.prepend("kdenlivetitle:");
545
            producer = new Mlt::Producer(*m_qmlView->profile(), 0, path.toUtf8().constData());
546
        } else if (type == Playlist) {
547
            path.prepend("consumer:");
548
            producer = new Mlt::Producer(*m_qmlView->profile(), 0, path.toUtf8().constData());
549
        } else if (type == SlideShow) {
550
            producer = new Mlt::Producer(*m_qmlView->profile(), 0, path.toUtf8().constData());
551
        } else if (!url.isValid()) {
Laurent Montel's avatar
Laurent Montel committed
552
            //WARNING: when is this case used? Not sure it is working.. JBM/
553 554 555
            QDomDocument doc;
            QDomElement mlt = doc.createElement("mlt");
            QDomElement play = doc.createElement("playlist");
Laurent Montel's avatar
Laurent Montel committed
556
            play.setAttribute("id", "playlist0");
557 558 559
            doc.appendChild(mlt);
            mlt.appendChild(play);
            play.appendChild(doc.importNode(info.xml, true));
Laurent Montel's avatar
Laurent Montel committed
560 561 562 563 564 565
            QDomElement tractor = doc.createElement("tractor");
            tractor.setAttribute("id", "tractor0");
            QDomElement track = doc.createElement("track");
            track.setAttribute("producer", "playlist0");
            tractor.appendChild(track);
            mlt.appendChild(tractor);
566
            producer = new Mlt::Producer(*m_qmlView->profile(), "xml-string", doc.toString().toUtf8().constData());
567
        } else {
568
            producer = new Mlt::Producer(*m_qmlView->profile(), 0, path.toUtf8().constData());
569
        }
570
        if (producer == NULL || producer->is_blank() || !producer->is_valid()) {
571
            qDebug() << " / / / / / / / / ERROR / / / / // CANNOT LOAD PRODUCER: "<<path;
572
            m_processingClipId.removeAll(info.clipId);
573 574 575 576 577
            if (proxyProducer) {
                // Proxy file is corrupted
                emit removeInvalidProxy(info.clipId, false);
            }
            else emit removeInvalidClip(info.clipId, info.replaceProducer);
578
            delete producer;
579
            continue;
580
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
581 582
        // Pass useful properties
        processProducerProperties(producer, info.xml);
583
        QString clipName = ProjectClip::getXmlProperty(info.xml, "kdenlive:clipname");
584
        if (!clipName.isEmpty()) {
585
            producer->set("kdenlive:clipname", clipName.toUtf8().constData());
586
        }
587
        QString groupId = ProjectClip::getXmlProperty(info.xml, "kdenlive:folderid");
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
588
        if (!groupId.isEmpty()) {
589
            producer->set("kdenlive:folderid", groupId.toUtf8().constData());
590 591
        }
        
592 593 594 595 596
        if (proxyProducer && info.xml.hasAttribute("proxy_out")) {
            producer->set("length", info.xml.attribute("proxy_out").toInt() + 1);
            producer->set("out", info.xml.attribute("proxy_out").toInt());
            if (producer->get_out() != info.xml.attribute("proxy_out").toInt()) {
                // Proxy file length is different than original clip length, this will corrupt project so disable this proxy clip
597
                qDebug()<<"/ // PROXY LENGTH MISMATCH, DELETE PRODUCER";
598
                m_processingClipId.removeAll(info.clipId);
599 600 601 602 603
                emit removeInvalidProxy(info.clipId, true);
                delete producer;
                continue;
            }
        }
604 605
        //TODO: handle forced properties
        /*if (info.xml.hasAttribute("force_aspect_ratio")) {
606 607 608
            double aspect = info.xml.attribute("force_aspect_ratio").toDouble();
            if (aspect > 0) producer->set("force_aspect_ratio", aspect);
        }
609

610 611 612 613 614 615 616 617
        if (info.xml.hasAttribute("force_aspect_num") && info.xml.hasAttribute("force_aspect_den")) {
            int width = info.xml.attribute("frame_size").section('x', 0, 0).toInt();
            int height = info.xml.attribute("frame_size").section('x', 1, 1).toInt();
            int aspectNumerator = info.xml.attribute("force_aspect_num").toInt();
            int aspectDenominator = info.xml.attribute("force_aspect_den").toInt();
            if (aspectDenominator != 0 && width != 0)
                producer->set("force_aspect_ratio", double(height) * aspectNumerator / aspectDenominator / width);
        }
618

619 620 621
        if (info.xml.hasAttribute("force_fps")) {
            double fps = info.xml.attribute("force_fps").toDouble();
            if (fps > 0) producer->set("force_fps", fps);
622
        }
623

624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
        if (info.xml.hasAttribute("force_progressive")) {
            bool ok;
            int progressive = info.xml.attribute("force_progressive").toInt(&ok);
            if (ok) producer->set("force_progressive", progressive);
        }
        if (info.xml.hasAttribute("force_tff")) {
            bool ok;
            int fieldOrder = info.xml.attribute("force_tff").toInt(&ok);
            if (ok) producer->set("force_tff", fieldOrder);
        }
        if (info.xml.hasAttribute("threads")) {
            int threads = info.xml.attribute("threads").toInt();
            if (threads != 1) producer->set("threads", threads);
        }
        if (info.xml.hasAttribute("video_index")) {
            int vindex = info.xml.attribute("video_index").toInt();
            if (vindex != 0) producer->set("video_index", vindex);
        }
        if (info.xml.hasAttribute("audio_index")) {
            int aindex = info.xml.attribute("audio_index").toInt();
            if (aindex != 0) producer->set("audio_index", aindex);
        }
        if (info.xml.hasAttribute("force_colorspace")) {
            int colorspace = info.xml.attribute("force_colorspace").toInt();
            if (colorspace != 0) producer->set("force_colorspace", colorspace);
        }
        if (info.xml.hasAttribute("full_luma")) {
            int full_luma = info.xml.attribute("full_luma").toInt();
            if (full_luma != 0) producer->set("set.force_full_luma", full_luma);
653
        }*/
654

655 656 657
        int clipOut = 0;
        int duration = 0;
        if (info.xml.hasAttribute("out")) clipOut = info.xml.attribute("out").toInt();
Simon Eugster's avatar
Simon Eugster committed
658

659
        // setup length here as otherwise default length (currently 15000 frames in MLT) will be taken even if outpoint is larger
660
        if (type == Color || type == Text || type == Image || type == SlideShow) {
661 662 663 664 665
            int length;
            if (info.xml.hasAttribute("length")) {
                length = info.xml.attribute("length").toInt();
                clipOut = length - 1;
            }
666
            else length = info.xml.attribute("out").toInt() - info.xml.attribute("in").toInt() + 1;
Laurent Montel's avatar
Laurent Montel committed
667 668 669 670 671 672 673 674 675 676
            // Pass duration if it was forced
            if (info.xml.hasAttribute("duration")) {
                duration = info.xml.attribute("duration").toInt();
                if (length < duration) {
                    length = duration;
                    if (clipOut > 0) clipOut = length - 1;
                }
            }
            if (duration == 0) duration = length;
            producer->set("length", length);
677
        }
678

679
        if (clipOut > 0) producer->set_in_and_out(info.xml.attribute("in").toInt(), clipOut);
680

681 682
        if (info.xml.hasAttribute("templatetext"))
            producer->set("templatetext", info.xml.attribute("templatetext").toUtf8().constData());
683

684
        int fullWidth = (int)((double) info.imageHeight * m_qmlView->profile()->dar() + 0.5);
685
        int frameNumber = ProjectClip::getXmlProperty(info.xml, "kdenlive:thumbnailFrame", "-1").toInt();
686

687
        if ((!info.replaceProducer && info.xml.hasAttribute("kdenlive:file_hash")) || proxyProducer) {
688
            // Clip  already has all properties
689 690
            // We want to replace an existing producer. We MUST NOT set the producer's id property until 
            // the old one has been removed.
691 692 693 694 695
            if (proxyProducer) {
                // Recreate clip thumb
                if (frameNumber > 0) producer->seek(frameNumber);
                Mlt::Frame *frame = producer->get_frame();
                if (frame && frame->is_valid()) {
696
                    QImage img = KThumb::getFrame(frame, fullWidth, info.imageHeight);
697 698
                    emit replyGetImage(info.clipId, img);
                }
699
                if (frame) delete frame;
700
            }
701
            // replace clip
702
            m_processingClipId.removeAll(info.clipId);
703
            m_binController->replaceProducer(info.clipId, *producer, profileinfo);
704
            emit gotFileProperties(info, NULL);
705 706
            continue;
        }
707 708
        // We are not replacing an existing producer, so set the id
        producer->set("id", info.clipId.toUtf8().constData());
709 710
        stringMap filePropertyMap;
        stringMap metadataPropertyMap;
711
        char property[200];
712

713 714
        if (frameNumber > 0) producer->seek(frameNumber);
        duration = duration > 0 ? duration : producer->get_playtime();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
715
        ////qDebug() << "///////  PRODUCER: " << url.path() << " IS: " << producer->get_playtime();
716

717
        if (type == SlideShow) {
718 719 720
	    int ttl = EffectsList::property(info.xml,"ttl").toInt();
	    QString anim = EffectsList::property(info.xml,"animation");
            if (!anim.isEmpty()) {
721
                Mlt::Filter *filter = new Mlt::Filter(*m_qmlView->profile(), "affine");
722 723
                if (filter && filter->is_valid()) {
                    int cycle = ttl;
724
                    QString geometry = SlideshowClip::animationToGeometry(anim, cycle);
725
                    if (!geometry.isEmpty()) {
726
                        if (anim.contains("low-pass")) {
727
                            Mlt::Filter *blur = new Mlt::Filter(*m_qmlView->profile(), "boxblur");
728 729 730 731 732 733
                            if (blur && blur->is_valid())
                                producer->attach(*blur);
                        }
                        filter->set("transition.geometry", geometry.toUtf8().data());
                        filter->set("transition.cycle", cycle);
                        producer->attach(*filter);
734 735 736
                    }
                }
            }
737 738
            QString fade = EffectsList::property(info.xml,"fade");
	    if (fade == "1") {
739
                // user wants a fade effect to slideshow
740
                Mlt::Filter *filter = new Mlt::Filter(*m_qmlView->profile(), "luma");
741 742
                if (filter && filter->is_valid()) {
                    if (ttl) filter->set("cycle", ttl);
743 744 745 746 747 748 749 750
		    QString luma_duration = EffectsList::property(info.xml,"luma_duration");
		    QString luma_file = EffectsList::property(info.xml,"luma_file");
		    if (!luma_duration.isEmpty()) filter->set("duration", luma_duration.toInt());
                    if (!luma_file.isEmpty()) {
                        filter->set("luma.resource", luma_file.toUtf8().constData());
			QString softness = EffectsList::property(info.xml,"softness");
                        if (!softness.isEmpty()) {
                            int soft = softness.toInt();
751 752
                            filter->set("luma.softness", (double) soft / 100.0);
                        }
753
                    }
754 755 756
                    producer->attach(*filter);
                }
            }
757 758
            QString crop = EffectsList::property(info.xml,"crop");
            if (crop == "1") {
759
                // user wants to center crop the slides