clipcontroller.cpp 26.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
Copyright (C) 2012  Till Theato <root@ttill.de>
Copyright (C) 2014  Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.kdenlive.org.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License or (at your option) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy 
defined in Section 14 of version 3 of the license.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "clipcontroller.h"
#include "bincontroller.h"
25
#include "mltcontroller/effectscontroller.h"
26
#include "lib/audio/audioStreamInfo.h"
Vincent Pinon's avatar
Vincent Pinon committed
27
#include "timeline/timeline.h"
28
#include "timeline/effectmanager.h"
29 30 31 32

#include <QUrl>
#include <QDebug>
#include <QPixmap>
33
#include <QFileInfo>
34 35 36
#include <KLocalizedString>


37
ClipController::ClipController(BinController *bincontroller, Mlt::Producer& producer) : QObject()
38
    , selectedEffectIndex(1)
39
    , audioThumbCreated(false)
Vincent Pinon's avatar
Vincent Pinon committed
40
    , m_properties(new Mlt::Properties(producer.get_properties()))
41
    , m_usesProxy(false)
Vincent Pinon's avatar
Vincent Pinon committed
42
    , m_audioInfo(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
43 44
    , m_audioIndex(0)
    , m_videoIndex(0)
45
    , m_clipType(Unknown)
Vincent Pinon's avatar
Vincent Pinon committed
46 47 48
    , m_hasLimitedDuration(true)
    , m_binController(bincontroller)
    , m_snapMarkers(QList < CommentedTime >())
49 50
{
    m_masterProducer = &producer;
51 52 53 54
    if (!m_masterProducer->is_valid()) {
        qDebug()<<"// WARNING, USING INVALID PRODUCER";
        return;
    }
55
    else {
56
        QString proxy = m_properties->get("kdenlive:proxy");
57 58
        if (proxy.length() > 2) {
            // This is a proxy producer, read original url from kdenlive property
59 60 61 62 63
            QString path = m_properties->get("kdenlive:originalurl");
            if (!path.startsWith(QLatin1String("/"))) {
                path.prepend(bincontroller->documentRoot());
            }
            m_url = QUrl::fromLocalFile(path);
64
            m_usesProxy = true;
65 66 67
        }
        else m_url = QUrl::fromLocalFile(m_properties->get("resource"));
        m_service = m_properties->get("mlt_service");
68
        getInfoForProducer();
69 70 71 72
    }
}

ClipController::ClipController(BinController *bincontroller) : QObject()
73
    , selectedEffectIndex(1)
74
    , audioThumbCreated(false)
Vincent Pinon's avatar
Vincent Pinon committed
75
    , m_masterProducer(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
76
    , m_properties(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
77
    , m_usesProxy(false)
Vincent Pinon's avatar
Vincent Pinon committed
78
    , m_audioInfo(NULL)
79 80
    , m_audioIndex(0)
    , m_videoIndex(0)
Vincent Pinon's avatar
Vincent Pinon committed
81 82 83 84
    , m_clipType(Unknown)
    , m_hasLimitedDuration(true)
    , m_binController(bincontroller)
    , m_snapMarkers(QList < CommentedTime >())
85 86 87 88 89
{
}

ClipController::~ClipController()
{
90 91
  delete m_properties;
  delete m_masterProducer;
92 93
}

94 95 96 97 98
double ClipController::dar() const
{
    return m_binController->dar();
}

99 100 101 102 103
AudioStreamInfo *ClipController::audioInfo() const
{
    return m_audioInfo;
}

104
void ClipController::addMasterProducer(Mlt::Producer &producer)
105
{
106
    m_properties = new Mlt::Properties(producer.get_properties());
107 108 109
    m_masterProducer = &producer;
    if (!m_masterProducer->is_valid()) qDebug()<<"// WARNING, USING INVALID PRODUCER";
    else {
110
        QString proxy = m_properties->get("kdenlive:proxy");
111 112
        if (proxy.length() > 2) {
            // This is a proxy producer, read original url from kdenlive property
113
            m_url = QUrl::fromLocalFile(m_properties->get("kdenlive:originalurl"));
114 115 116 117 118
            m_usesProxy = true;
        }
        else {
            m_url = QUrl::fromLocalFile(m_properties->get("resource"));
            m_usesProxy = false;
119 120
        }
        m_service = m_properties->get("mlt_service");
121 122 123 124
        getInfoForProducer();
    }
}

125
void ClipController::getProducerXML(QDomDocument& document, bool includeMeta)
126 127
{
    if (m_masterProducer) {
128
        QString xml = m_binController->getProducerXML(*m_masterProducer, includeMeta);
129 130 131 132 133
        document.setContent(xml);
    }
    else qDebug()<<" + + ++ NO MASTER PROD";
}

134 135
void ClipController::getInfoForProducer()
{
136
    date = QFileInfo(m_url.path()).lastModified();
137 138
    m_audioIndex = -1;
    m_videoIndex = -1;
139
    if (m_service == QLatin1String("avformat") || m_service == QLatin1String("avformat-novalidate")) {
140 141
        m_audioIndex = int_property(QStringLiteral("audio_index"));
        m_videoIndex = int_property(QStringLiteral("video_index"));
142 143 144 145 146 147 148 149 150
        if (m_audioIndex == -1) {
            m_clipType = Video;
        }
        else if (m_videoIndex == -1) {
            m_clipType = Audio;
        }
        else {
            m_clipType = AV;
        }
151
    }
152
    else if (m_service == QLatin1String("qimage") || m_service == QLatin1String("pixbuf")) {
153
        if (m_url.path().contains(QStringLiteral("%")) || m_url.path().contains(QStringLiteral("/.all."))) {
154 155 156 157 158
            m_clipType = SlideShow;
        }
        else {
            m_clipType = Image;
        }
159
        m_hasLimitedDuration = false;
160
    }
161
    else if (m_service == QLatin1String("colour") || m_service == QLatin1String("color")) {
162
        m_clipType = Color;
163
        m_hasLimitedDuration = false;
164
    }
165
    else if (m_service == QLatin1String("kdenlivetitle")) {
166 167 168 169 170
        if (!m_url.isEmpty()) {
            m_clipType = TextTemplate;
        } else {
            m_clipType = Text;
        }
171
        m_hasLimitedDuration = false;
172
    }
173
    else if (m_service == QLatin1String("xml") || m_service == QLatin1String("consumer")) {
174 175
        m_clipType = Playlist;
    }
176
    else if (m_service == QLatin1String("webvfx")) {
177
        m_clipType = WebVfx;
178
    }
Vincent Pinon's avatar
Vincent Pinon committed
179 180 181
    else if (m_service == QLatin1String("qtext")) {
        m_clipType = QText;
    }
182
    else m_clipType = Unknown;
183 184 185
    if (m_audioIndex > -1 || m_clipType == Playlist) {
        m_audioInfo = new AudioStreamInfo(m_masterProducer, m_audioIndex);
    }
186 187
}

188 189 190 191 192
bool ClipController::hasLimitedDuration() const
{
    return m_hasLimitedDuration;
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
Mlt::Producer &ClipController::originalProducer()
{
    return *m_masterProducer;
}

Mlt::Producer *ClipController::masterProducer()
{
    return new Mlt::Producer(*m_masterProducer);
}

bool ClipController::isValid()
{
    if (m_masterProducer == NULL) return false;
    return m_masterProducer->is_valid();
}

209
const QString ClipController::clipId()
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
210 211
{
    if (m_masterProducer == NULL) return QString();
212
    return property(QStringLiteral("id"));
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
213 214
}

215
// static
216
const char *ClipController::getPassPropertiesList(bool passLength)
217
{
218
    if (!passLength) {
219
	return "kdenlive:proxy,kdenlive:originalurl,force_aspect_num,force_aspect_den,force_aspect_ratio,force_fps,force_progressive,force_tff,threads,force_colorspace,set.force_full_luma,file_hash,autorotate";
220
    }
221
    return "kdenlive:proxy,kdenlive:originalurl,force_aspect_num,force_aspect_den,force_aspect_ratio,force_fps,force_progressive,force_tff,threads,force_colorspace,set.force_full_luma,templatetext,file_hash,autorotate,xmldata,length";
222 223
}

224
QMap <QString, QString> ClipController::getPropertiesFromPrefix(const QString &prefix, bool withPrefix)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
225 226
{
    Mlt::Properties subProperties;
227
    subProperties.pass_values(*m_properties, prefix.toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
228 229
    QMap <QString,QString> subclipsData;
    for (int i = 0; i < subProperties.count(); i++) {
230
        subclipsData.insert(withPrefix ? QString(prefix + subProperties.get_name(i)) : subProperties.get_name(i), subProperties.get(i));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
231 232 233 234
    }
    return subclipsData;
}

235

236
void ClipController::updateProducer(const QString &id, Mlt::Producer* producer)
237 238
{
    //TODO replace all track producers
239 240
    Q_UNUSED(id)

241 242
    Mlt::Properties passProperties;
    // Keep track of necessary properties
243
    QString proxy = producer->get("kdenlive:proxy");
244 245 246 247 248
    if (proxy.length() > 2) {
        // This is a proxy producer, read original url from kdenlive property
        m_usesProxy = true;
    }
    else m_usesProxy = false;
249 250 251 252 253
    passProperties.pass_list(*m_properties, getPassPropertiesList(m_usesProxy));
    delete m_properties;
    delete m_masterProducer;
    m_masterProducer = producer;
    m_properties = new Mlt::Properties(producer->get_properties());
254
    // Pass properties from previous producer
255
    m_properties->pass_list(passProperties, getPassPropertiesList(m_usesProxy));
256 257 258 259 260 261 262 263 264 265 266
    if (!m_masterProducer->is_valid()) qDebug()<<"// WARNING, USING INVALID PRODUCER";
    else {
        // URL and name shoule not be updated otherwise when proxying a clip we cannot find back the original url
        /*m_url = QUrl::fromLocalFile(m_masterProducer->get("resource"));
        if (m_url.isValid()) {
            m_name = m_url.fileName();
        }
        */
    }
}

267

268
Mlt::Producer *ClipController::getTrackProducer(const QString trackName, PlaylistState::ClipState clipState, double speed)
269
{
270 271 272
    //TODO
    Q_UNUSED(speed)

273
    if (trackName.isEmpty()) {
274 275
        return m_masterProducer;
    }
276 277 278 279
    if  (m_clipType != AV && m_clipType != Audio && m_clipType != Playlist) {
        // Only producers with audio need a different producer for each track (or we have an audio crackle bug)
        return new Mlt::Producer(m_masterProducer->parent());
    }
280
    QString clipWithTrackId = clipId();
281
    clipWithTrackId.append("_" + trackName);
282

283 284 285
    //TODO handle audio / video only producers and framebuffer
    if (clipState == PlaylistState::AudioOnly) clipWithTrackId.append("_audio");
    else if (clipState == PlaylistState::VideoOnly) clipWithTrackId.append("_video");
286

287 288
    Mlt::Producer *clone = m_binController->cloneProducer(*m_masterProducer);
    clone->set("id", clipWithTrackId.toUtf8().constData());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
289
    //m_binController->replaceBinPlaylistClip(clipWithTrackId, clone->parent());
290 291 292 293 294
    return clone;
}

const QString ClipController::getStringDuration()
{
295 296 297 298 299 300 301
    if (m_masterProducer) {
        int playtime = m_masterProducer->get_int("kdenlive:duration");
        if (playtime > 0) {
            return QString(properties().frames_to_time(playtime, mlt_time_smpte));
        }
        return m_masterProducer->get_length_time(mlt_time_smpte);
    }
302 303 304
    return QString(i18n("Unknown"));
}

305
GenTime ClipController::getPlaytime() const
306
{
307 308 309 310
    if (!m_hasLimitedDuration) {
        int playtime = m_masterProducer->get_int("kdenlive:duration");
        return GenTime(playtime == 0 ? m_masterProducer->get_playtime() : playtime, m_binController->fps());
    }
311
    return GenTime(m_masterProducer->get_playtime(), m_binController->fps());
312 313
}

314
QString ClipController::property(const QString &name) const
315
{
316
    if (!m_properties) return QString();
317 318 319 320
    if (m_usesProxy && name.startsWith("meta.")) {
        QString correctedName = QStringLiteral("kdenlive:") + name;
        return m_properties->get(correctedName.toUtf8().constData());
    }
321
    return QString(m_properties->get(name.toUtf8().constData()));
322 323
}

324 325
int ClipController::int_property(const QString &name) const
{
326
    if (!m_properties) return 0;
327 328 329 330
    if (m_usesProxy && name.startsWith("meta.")) {
        QString correctedName = QStringLiteral("kdenlive:") + name;
        return m_properties->get_int(correctedName.toUtf8().constData());
    }
331
    return m_properties->get_int(name.toUtf8().constData());
332 333
}

334 335 336 337 338 339
qint64 ClipController::int64_property(const QString &name) const
{
    if (!m_properties) return 0;
    return m_properties->get_int64(name.toUtf8().constData());
}

340 341 342 343 344 345
double ClipController::double_property(const QString &name) const
{
    if (!m_properties) return 0;
    return m_properties->get_double(name.toUtf8().constData());
}

346 347 348 349 350 351 352
QColor ClipController::color_property(const QString &name) const
{
    if (!m_properties) return QColor();
    mlt_color color = m_properties->get_color(name.toUtf8().constData());
    return QColor::fromRgb(color.r, color.g, color.b);
}

353 354 355
double ClipController::originalFps() const
{
    if (!m_properties) return 0;
356
    QString propertyName = QStringLiteral("meta.media.%1.stream.frame_rate").arg(m_videoIndex);
357 358 359
    return m_properties->get_double(propertyName.toUtf8().constData());
}

360 361 362
QString ClipController::videoCodecProperty(const QString &property) const
{
    if (!m_properties) return QString();
363
    QString propertyName = QStringLiteral("meta.media.%1.codec.%2").arg(m_videoIndex).arg(property);
364 365 366
    return m_properties->get(propertyName.toUtf8().constData());
}

367 368
const QString ClipController::codec(bool audioCodec) const
{
369
    if (!m_properties || (m_clipType!= AV && m_clipType != Video && m_clipType != Audio)) return QString();
370
    QString propertyName = QStringLiteral("meta.media.%1.codec.name").arg(audioCodec ? m_audioIndex : m_videoIndex);
371 372 373
    return m_properties->get(propertyName.toUtf8().constData());
}

374 375 376 377 378 379 380
QUrl ClipController::clipUrl() const
{
    return m_url;
}

QString ClipController::clipName() const
{
381
    QString name = property(QStringLiteral("kdenlive:clipname"));
382 383
    if (!name.isEmpty()) return name;
    return m_url.fileName();
384 385
}

386 387
QString ClipController::description() const
{
388 389 390 391
    if (m_clipType == TextTemplate) {
        QString name = property(QStringLiteral("templatetext"));
        return name;
    }
392
    QString name = property(QStringLiteral("kdenlive:description"));
393
    if (!name.isEmpty()) return name;
394
    return property(QStringLiteral("meta.attr.comment.markup"));
395 396
}

397 398 399 400 401 402 403 404
QString ClipController::serviceName() const
{
    return m_service;
}

void ClipController::setProperty(const QString& name, int value)
{
    //TODO: also set property on all track producers
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
405
    m_masterProducer->parent().set(name.toUtf8().constData(), value);
406 407 408 409 410
}

void ClipController::setProperty(const QString& name, double value)
{
    //TODO: also set property on all track producers
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
411
    m_masterProducer->parent().set(name.toUtf8().constData(), value);
412 413 414 415 416
}

void ClipController::setProperty(const QString& name, const QString& value)
{
    //TODO: also set property on all track producers
417 418 419 420
    if (value.isEmpty()) {
        m_masterProducer->parent().set(name.toUtf8().constData(), (char *)NULL);
    }
    else m_masterProducer->parent().set(name.toUtf8().constData(), value.toUtf8().constData());
421 422
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
423 424 425 426 427 428
void ClipController::resetProperty(const QString& name)
{
    //TODO: also set property on all track producers
    m_masterProducer->parent().set(name.toUtf8().constData(), (char *)NULL);
}

429 430
ClipType ClipController::clipType() const
{
431
    return m_clipType;
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
}


QPixmap ClipController::pixmap(int framePosition, int width, int height)
{
    //int currentPosition = position();
    m_masterProducer->seek(framePosition);
    Mlt::Frame *frame = m_masterProducer->get_frame();
    if (frame == NULL || !frame->is_valid()) {
        QPixmap p(width, height);
        p.fill(QColor(Qt::red).rgb());
        return p;
    }

    frame->set("rescale.interp", "bilinear");
    frame->set("deinterlace_method", "onefield");
    frame->set("top_field_first", -1);

    if (width == 0) {
        width = m_masterProducer->get_int("meta.media.width");
        if (width == 0) {
            width = m_masterProducer->get_int("width");
        }
    }
    if (height == 0) {
        height = m_masterProducer->get_int("meta.media.height");
        if (height == 0) {
            height = m_masterProducer->get_int("height");
        }
    }
    
    //     int ow = frameWidth;
    //     int oh = height;
    mlt_image_format format = mlt_image_rgb24a;

467
    QImage image(width, height, QImage::Format_RGBA8888);
468 469
    const uchar* imagedata = frame->get_image(format, width, height);
    if (imagedata) {
470
        memcpy(image.bits(), imagedata, width * height * 4);
471 472 473 474 475 476 477 478 479 480
    }
    else image.fill(QColor(Qt::red).rgb());
    delete frame;


    QPixmap pixmap;;
    pixmap.convertFromImage(image);

    return pixmap;
}
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496

QList < GenTime > ClipController::snapMarkers() const
{
    QList < GenTime > markers;
    for (int count = 0; count < m_snapMarkers.count(); ++count) {
        markers.append(m_snapMarkers.at(count).time());
    }

    return markers;
}

QList < CommentedTime > ClipController::commentedSnapMarkers() const
{
    return m_snapMarkers;
}

497 498 499 500 501
void ClipController::loadSnapMarker(const QString &seconds, const QString &hash)
{
    QLocale locale;
    GenTime markerTime(locale.toDouble(seconds));
    CommentedTime marker(hash, markerTime);
502 503
    if (m_snapMarkers.contains(marker)) {
        m_snapMarkers.removeAll(marker);
504
    }
505 506
    m_snapMarkers.append(marker);
    qSort(m_snapMarkers);
507
}
508 509 510

void ClipController::addSnapMarker(const CommentedTime &marker)
{
511 512
    if (m_snapMarkers.contains(marker)) {
        m_snapMarkers.removeAll(marker);
513
    }
514
    m_snapMarkers.append(marker);
515 516
    QLocale locale;
    QString markerId = clipId() + ":" + locale.toString(marker.time().seconds());
517 518
    m_binController->storeMarker(markerId, marker.hash());
    qSort(m_snapMarkers);
519 520 521 522
}

void ClipController::editSnapMarker(const GenTime & time, const QString &comment)
{
523 524 525 526 527
    CommentedTime marker(time, comment);
    int ix = m_snapMarkers.indexOf(marker);
    if (ix == -1) {
        qCritical() << "trying to edit Snap Marker that does not already exists";
        return;
528
    }
529
    m_snapMarkers[ix].setComment(comment);
530 531
    QLocale locale;
    QString markerId = clipId() + ":" + locale.toString(time.seconds());
532
    m_binController->storeMarker(markerId, QString());
533 534 535 536
}

QString ClipController::deleteSnapMarker(const GenTime & time)
{
537 538 539 540 541
    CommentedTime marker(time, QString());
    int ix = m_snapMarkers.indexOf(marker);
    if (ix == -1) {
        qCritical() << "trying to edit Snap Marker that does not already exists";
        return QString();
542
    }
543 544
    QString result = m_snapMarkers.at(ix).comment();
    m_snapMarkers.removeAt(ix);
545 546
    QLocale locale;
    QString markerId = clipId() + ":" + locale.toString(time.seconds());
547
    m_binController->storeMarker(markerId, QString());
548 549 550 551 552 553
    return result;
}


GenTime ClipController::findPreviousSnapMarker(const GenTime & currTime)
{
554 555 556
    CommentedTime marker(currTime, QString());
    int ix = m_snapMarkers.indexOf(marker) - 1;
    return m_snapMarkers.at(qMax(ix, 0)).time();
557 558 559 560
}

GenTime ClipController::findNextSnapMarker(const GenTime & currTime)
{
561 562 563 564
    CommentedTime marker(currTime, QString());
    int ix = m_snapMarkers.indexOf(marker) + 1;
    if (ix == 0 || ix == m_snapMarkers.count()) return getPlaytime();
    return m_snapMarkers.at(ix).time();
565 566 567 568
}

QString ClipController::markerComment(const GenTime &t) const
{
569 570 571 572
    CommentedTime marker(t, QString());
    int ix = m_snapMarkers.indexOf(marker);
    if (ix == -1) return QString();
    return m_snapMarkers.at(ix).comment();
573 574
}

575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
QStringList ClipController::markerComments(const GenTime &start, const GenTime &end) const
{
    QStringList comments;
    for (int count = 0; count < m_snapMarkers.count(); ++count) {
        if (m_snapMarkers.at(count).time() >= start) {
            if (m_snapMarkers.at(count).time() > end) {
                break;
            } else {
                comments << m_snapMarkers.at(count).comment();
            }
        }
    }
    return comments;
}

590 591
CommentedTime ClipController::markerAt(const GenTime &t) const
{
592 593 594 595
    CommentedTime marker(t, QString());
    int ix = m_snapMarkers.indexOf(marker);
    if (ix == -1) return CommentedTime();
    return m_snapMarkers.at(ix);
596 597 598 599
}

void ClipController::setZone(const QPoint &zone)
{
600 601
    setProperty(QStringLiteral("kdenlive:zone_in"), zone.x());
    setProperty(QStringLiteral("kdenlive:zone_out"), zone.y());
602 603 604 605
}

QPoint ClipController::zone() const
{
606
    int in = int_property(QStringLiteral("kdenlive:zone_in"));
607 608 609
    int max = getPlaytime().frames(m_binController->fps()) - 1;
    int out = qMin(int_property(QStringLiteral("kdenlive:zone_out")), max);
    if (out <= in ) out = max;
610 611 612 613 614 615
    QPoint zone(in, out);
    return zone;
}

const QString ClipController::getClipHash() const
{
616
    return property(QStringLiteral("kdenlive:file_hash"));
617 618
}

619 620 621 622
Mlt::Properties &ClipController::properties()
{
    return *m_properties;
}
623

624 625 626 627 628
Mlt::Profile *ClipController::profile()
{
    return m_binController->profile();
}

629 630 631 632 633 634 635 636 637 638 639
void ClipController::initEffect(const ProfileInfo &pInfo, QDomElement &xml)
{
    QMutexLocker lock(&m_effectMutex);
    Mlt::Service service = m_masterProducer->parent();
    ItemInfo info;
    info.cropStart = GenTime();
    info.cropDuration = getPlaytime();
    EffectsList eff = effectList();
    EffectsController::initEffect(info, pInfo, eff, property(QStringLiteral("kdenlive:proxy")), xml);
}

640
void ClipController::addEffect(const ProfileInfo &pInfo, QDomElement &xml)
641
{
642
    QMutexLocker lock(&m_effectMutex);
643
    Mlt::Service service = m_masterProducer->parent();
644 645 646
    ItemInfo info;
    info.cropStart = GenTime();
    info.cropDuration = getPlaytime();
647
    EffectsList eff = effectList();
648
    EffectsController::initEffect(info, pInfo, eff, property(QStringLiteral("kdenlive:proxy")), xml);
649
    // Add effect to list and setup a kdenlive_ix value
650 651
    int kdenlive_ix = 0;
    for (int i = 0; i < service.filter_count(); ++i) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
652
        QScopedPointer<Mlt::Filter> effect(service.filter(i));
653 654 655 656
        int ix = effect->get_int("kdenlive_ix");
        if (ix > kdenlive_ix) kdenlive_ix = ix;
    }
    kdenlive_ix++;
657 658
    xml.setAttribute(QStringLiteral("kdenlive_ix"), kdenlive_ix);
    EffectsParameterList params = EffectsController::getEffectArgs(pInfo, xml);
659 660
    EffectManager effect(service);
    effect.addEffect(params, getPlaytime().frames(m_binController->fps()));
661 662 663
    m_binController->updateTrackProducer(clipId());
}

664
void ClipController::removeEffect(int effectIndex, bool delayRefresh)
665
{
666
    QMutexLocker lock(&m_effectMutex);
667
    Mlt::Service service(m_masterProducer->parent());
668 669
    EffectManager effect(service);
    effect.removeEffect(effectIndex, true);
670 671
    if (!delayRefresh)
        m_binController->updateTrackProducer(clipId());
672 673 674 675
}

EffectsList ClipController::effectList()
{
676
    return xmlEffectList(m_masterProducer->profile(), m_masterProducer->parent());
677 678
}

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
void ClipController::moveEffect(int oldPos, int newPos)
{
    QMutexLocker lock(&m_effectMutex);
    Mlt::Service service(m_masterProducer->parent());
    EffectManager effect(service);
    effect.moveEffect(oldPos, newPos);
}

void ClipController::reloadTrackProducers()
{
    m_binController->updateTrackProducer(clipId());
}

int ClipController::effectsCount()
{
    int count = 0;
    Mlt::Service service(m_masterProducer->parent());
    for (int ix = 0; ix < service.filter_count(); ++ix) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
697
        QScopedPointer<Mlt::Filter> effect(service.filter(ix));
698 699 700 701 702 703 704 705
        QString id = effect->get("kdenlive_id");
        if (!id.isEmpty()) {
            count++;
        }
    }
    return count;
}

706 707
// static
EffectsList ClipController::xmlEffectList(Mlt::Profile *profile, Mlt::Service &service)
708
{
709
    ProfileInfo profileinfo;
710 711
    profileinfo.profileSize = QSize(profile->width(), profile->height());
    profileinfo.profileFps = profile->fps();
712
    EffectsList effList(true);
713
    for (int ix = 0; ix < service.filter_count(); ++ix) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
714
        QScopedPointer<Mlt::Filter> effect(service.filter(ix));
Vincent Pinon's avatar
Vincent Pinon committed
715
        QDomElement clipeffect = Timeline::getEffectByTag(effect->get("tag"), effect->get("kdenlive_id"));
716
        QDomElement currenteffect = clipeffect.cloneNode().toElement();
717
	// recover effect parameters
718
        QDomNodeList params = currenteffect.elementsByTagName(QStringLiteral("parameter"));
719
	if (effect->get_int("disable") == 1) {
720
	    currenteffect.setAttribute(QStringLiteral("disable"), 1);
721
	}
722 723
        for (int i = 0; i < params.count(); ++i) {
            QDomElement param = params.item(i).toElement();
724
            Timeline::setParam(profileinfo, param, effect->get(param.attribute(QStringLiteral("name")).toUtf8().constData()));
725
        }
726
        effList.append(currenteffect);
727
    }
728
    return effList;
729 730 731 732 733 734
}

void ClipController::changeEffectState(const QList <int> indexes, bool disable)
{
    Mlt::Service service = m_masterProducer->parent();
    for (int i = 0; i < service.filter_count(); ++i) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
735
        QScopedPointer<Mlt::Filter> effect(service.filter(i));
736 737 738 739
        if (effect && effect->is_valid() && indexes.contains(effect->get_int("kdenlive_ix"))) {
            effect->set("disable", (int) disable);
        }
    }
740
    m_binController->updateTrackProducer(clipId());
741 742
}

743
void ClipController::updateEffect(const ProfileInfo &pInfo, const QDomElement &e, int ix)
744
{
745 746 747 748 749 750 751 752
    QString tag = e.attribute(QLatin1String("id"));
    if (tag == QLatin1String("autotrack_rectangle") || tag.startsWith(QLatin1String("ladspa")) || tag == QLatin1String("sox")) {
        // this filters cannot be edited, remove and re-add it
        removeEffect(ix, true);
        QDomElement clone = e.cloneNode().toElement();
        addEffect(pInfo, clone);
        return;
    }
753
    EffectsParameterList params = EffectsController::getEffectArgs(pInfo, e);
754 755
    Mlt::Service service = m_masterProducer->parent();
    for (int i = 0; i < service.filter_count(); ++i) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
756 757 758 759
        QScopedPointer<Mlt::Filter> effect(service.filter(i));
        if (!effect || !effect->is_valid() || effect->get_int("kdenlive_ix") != ix) {
            continue;
        }
760 761 762
        service.lock();
        QString prefix;
        QString ser = effect->get("mlt_service");
763
        if (ser == QLatin1String("region")) prefix = QStringLiteral("filter0.");
764 765
        for (int j = 0; j < params.count(); ++j) {
            effect->set((prefix + params.at(j).name()).toUtf8().constData(), params.at(j).value().toUtf8().constData());
766
            //qDebug()<<params.at(j).name()<<" = "<<params.at(j).value();
767
        }
768
        service.unlock();
769
    }
770 771
    m_binController->updateTrackProducer(clipId());
    //slotRefreshTracks();
772 773 774 775
}

bool ClipController::hasEffects() const
{
776 777
    Mlt::Service service = m_masterProducer->parent();
    for (int ix = 0; ix < service.filter_count(); ++ix) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
778
        QScopedPointer<Mlt::Filter> effect(service.filter(ix));
779 780 781 782
        QString id = effect->get("kdenlive_ix");
        if (!id.isEmpty()) return true;
    }
    return false;
783 784
}

785 786 787 788 789
void ClipController::disableEffects(bool disable)
{
    Mlt::Service service = m_masterProducer->parent();
    bool changed = false;
    for (int ix = 0; ix < service.filter_count(); ++ix) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
790
        QScopedPointer<Mlt::Filter> effect(service.filter(ix));
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
        QString id = effect->get("kdenlive_ix");
        if (id.isEmpty()) continue;
        int disabled = effect->get_int("disable");
        if (disable) {
            // we want to disable all kdenlive effects
            if (disabled == 1) {
                continue;
            }
            effect->set("disable", 1);
            effect->set("auto_disable", 1);
            changed = true;
        } else {
            // We want to re-enable effects
            int auto_disable = effect->get_int("auto_disable");
            if (auto_disable == 1) {
                effect->set("disable", (char*) NULL);
                effect->set("auto_disable", (char*) NULL);
                changed = true;
            }
        }
    }
    if (changed) m_binController->updateTrackProducer(clipId());
}