dvdwizardvob.cpp 29.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/***************************************************************************
 *   Copyright (C) 2009 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

Vincent Pinon's avatar
Vincent Pinon committed
20
#include "dvdwizardvob.h"
Vincent Pinon's avatar
Vincent Pinon committed
21
#include "doc/kthumb.h"
22
#include "kdenlivesettings.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
23
#include "timecode.h"
24
#include "dialogs/clipcreationdialog.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
25
#include <mlt++/Mlt.h>
26

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
27
#include <QDebug>
28
#include <KConfigGroup>
29 30
#include <KSharedConfig>
#include <KIO/Global>
31
#include "klocalizedstring.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
32
#include <KRecentDirs>
33
#include <KMessageBox>
34

35
#include <QFileDialog>
36
#include <QHBoxLayout>
37
#include <QDomDocument>
38
#include <QMimeData>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
39
#include <QTreeWidgetItem>
40
#include <QHeaderView>
41
#include <unistd.h>
42
#include <QStandardPaths>
43
#include <QProgressBar>
44

45
DvdTreeWidget::DvdTreeWidget(QWidget *parent) :
46
    QTreeWidget(parent)
47 48 49 50 51 52
{
    setAcceptDrops(true);
}

void DvdTreeWidget::dragEnterEvent(QDragEnterEvent * event ) {
    if (event->mimeData()->hasUrls()) {
53 54
        event->setDropAction(Qt::CopyAction);
        event->setAccepted(true);
55 56 57 58 59
    }
    else QTreeWidget::dragEnterEvent(event);
}

void DvdTreeWidget::dragMoveEvent(QDragMoveEvent * event) {
60
    event->acceptProposedAction();
61 62 63 64 65 66 67 68 69 70 71 72 73
}

void DvdTreeWidget::mouseDoubleClickEvent( QMouseEvent * )
{
    emit addNewClip();
}

void DvdTreeWidget::dropEvent(QDropEvent * event ) {
    QList<QUrl> clips = event->mimeData()->urls();
    event->accept();
    emit addClips(clips);
}

74
DvdWizardVob::DvdWizardVob(QWidget *parent) :
75 76 77
    QWizardPage(parent)
    , m_installCheck(true)
    , m_duration(0)
78
{
79
    m_view.setupUi(this);
80 81 82 83
    m_view.button_add->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
    m_view.button_delete->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
    m_view.button_up->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
    m_view.button_down->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
84 85 86 87 88 89
    m_vobList = new DvdTreeWidget(this);
    QVBoxLayout *lay1 = new QVBoxLayout;
    lay1->addWidget(m_vobList);
    m_view.list_frame->setLayout(lay1);
    m_vobList->setColumnCount(3);
    m_vobList->setHeaderHidden(true);
90
    m_view.convert_box->setVisible(false);
91 92

    connect(m_vobList, SIGNAL(addClips(QList<QUrl>)), this, SLOT(slotAddVobList(QList<QUrl>)));
93 94
    connect(m_vobList, SIGNAL(addNewClip()), this, SLOT(slotAddVobList()));
    connect(m_view.button_add, SIGNAL(clicked()), this, SLOT(slotAddVobList()));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
95 96 97
    connect(m_view.button_delete, SIGNAL(clicked()), this, SLOT(slotDeleteVobFile()));
    connect(m_view.button_up, SIGNAL(clicked()), this, SLOT(slotItemUp()));
    connect(m_view.button_down, SIGNAL(clicked()), this, SLOT(slotItemDown()));
98
    connect(m_view.convert_abort, SIGNAL(clicked()), this, SLOT(slotAbortTranscode()));
99
    connect(m_vobList, SIGNAL(itemSelectionChanged()), this, SLOT(slotCheckVobList()));
100
    
101
    m_vobList->setIconSize(QSize(60, 45));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
102

103
    QString errorMessage;
104 105
    if (QStandardPaths::findExecutable(QStringLiteral("dvdauthor")).isEmpty()) errorMessage.append(i18n("<strong>Program %1 is required for the DVD wizard.</strong>", i18n("dvdauthor")));
    if (QStandardPaths::findExecutable(QStringLiteral("mkisofs")).isEmpty() && QStandardPaths::findExecutable(QStringLiteral("genisoimage")).isEmpty()) errorMessage.append(i18n("<strong>Program %1 or %2 is required for the DVD wizard.</strong>", i18n("mkisofs"), i18n("genisoimage")));
106
    if (!errorMessage.isEmpty()) {
107 108
        m_view.button_add->setEnabled(false);
        m_view.dvd_profile->setEnabled(false);
109
    }
110

111 112
    m_view.dvd_profile->addItems(QStringList() << i18n("PAL 4:3") << i18n("PAL 16:9") << i18n("NTSC 4:3") << i18n("NTSC 16:9"));

113
    connect(m_view.dvd_profile, SIGNAL(activated(int)), this, SLOT(slotCheckProfiles()));
114
    m_vobList->header()->setStretchLastSection(false);
115 116 117
    m_vobList->header()->setSectionResizeMode(0, QHeaderView::Stretch);
    m_vobList->header()->setSectionResizeMode(1, QHeaderView::Custom);
    m_vobList->header()->setSectionResizeMode(2, QHeaderView::Custom);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
118

119
    m_capacityBar = new KCapacityBar(KCapacityBar::DrawTextInline, this);
120 121 122 123
    QHBoxLayout *lay = new QHBoxLayout;
    lay->addWidget(m_capacityBar);
    m_view.size_box->setLayout(lay);

124 125 126
    m_vobList->setItemDelegate(new DvdViewDelegate(m_vobList));
    m_transcodeAction = new QAction(i18n("Transcode"), this);
    connect(m_transcodeAction, SIGNAL(triggered()), this, SLOT(slotTranscodeFiles()));
127 128

    m_warnMessage = new KMessageWidget;
129
    m_warnMessage->setCloseButtonVisible(false);
130
    QGridLayout *s =  static_cast <QGridLayout*> (layout());
131
    s->addWidget(m_warnMessage, 2, 0, 1, -1);
132
    if (!errorMessage.isEmpty()) {
133 134 135
        m_warnMessage->setMessageType(KMessageWidget::Error);
        m_warnMessage->setText(errorMessage);
        m_installCheck = false;
136
    }else {
137 138 139 140
        m_warnMessage->setMessageType(KMessageWidget::Warning);
        m_warnMessage->setText(i18n("Your clips do not match selected DVD format, transcoding required."));
        m_warnMessage->addAction(m_transcodeAction);
        m_warnMessage->hide();
141
    }
142
    m_view.button_transcode->setHidden(true);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
143
    slotCheckVobList();
144 145 146 147

    m_transcodeProcess.setProcessChannelMode(QProcess::MergedChannels);
    connect(&m_transcodeProcess, &QProcess::readyReadStandardOutput, this, &DvdWizardVob::slotShowTranscodeInfo);
    connect(&m_transcodeProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &DvdWizardVob::slotTranscodeFinished);
148 149
}

150 151
DvdWizardVob::~DvdWizardVob()
{
152
    delete m_capacityBar;
153 154 155 156 157 158
    // Abort running transcoding
    if (m_transcodeProcess.state() != QProcess::NotRunning) {
        disconnect(&m_transcodeProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &DvdWizardVob::slotTranscodeFinished);
        m_transcodeProcess.close();
        m_transcodeProcess.waitForFinished();
    }
159 160
}

161 162 163 164 165
bool DvdWizardVob::isComplete() const
{
    return m_vobList->topLevelItemCount() > 0;
}

166 167 168 169
void DvdWizardVob::slotShowTranscodeInfo()
{
    QString log = QString(m_transcodeProcess.readAll());
    if (m_duration == 0) {
170 171
        if (log.contains(QStringLiteral("Duration:"))) {
            QString data = log.section(QStringLiteral("Duration:"), 1, 1).section(',', 0, 0).simplified();
172 173 174 175 176 177 178 179 180 181 182
            QStringList numbers = data.split(':');
            if (numbers.size() < 3) return;
            m_duration = numbers.at(0).toInt() * 3600 + numbers.at(1).toInt() * 60 + numbers.at(2).toDouble();
            //log_text->setHidden(true);
            //job_progress->setHidden(false);
        }
        else {
            //log_text->setHidden(false);
            //job_progress->setHidden(true);
        }
    }
183
    else if (log.contains(QStringLiteral("time="))) {
184
        int progress;
185
        QString time = log.section(QStringLiteral("time="), 1, 1).simplified().section(' ', 0, 0);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
        if (time.contains(':')) {
            QStringList numbers = time.split(':');
            if (numbers.size() < 3) return;
            progress = numbers.at(0).toInt() * 3600 + numbers.at(1).toInt() * 60 + numbers.at(2).toDouble();
        }
        else progress = (int) time.toDouble();
        m_view.convert_progress->setValue((int) (100.0 * progress / m_duration));
    }
    //log_text->setPlainText(log);
}

void DvdWizardVob::slotAbortTranscode()
{
    if (m_transcodeProcess.state() != QProcess::NotRunning) {
        m_transcodeProcess.close();
201
        m_transcodeProcess.waitForFinished();
202 203 204 205 206 207 208 209 210
    }
    m_transcodeQueue.clear();
    m_view.convert_box->hide();
    slotCheckProfiles();
}

void DvdWizardVob::slotTranscodeFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    if (exitCode == 0 && exitStatus == QProcess::NormalExit) {
211
        slotTranscodedClip(m_currentTranscoding.filename, m_currentTranscoding.filename + m_currentTranscoding.params.section(QStringLiteral("%1"), 1, 1).section(' ', 0, 0));
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
        if (!m_transcodeQueue.isEmpty()) {
            m_transcodeProcess.close();
            processTranscoding();
            return;
        }
    }
    else {
        // Something failed
      //TODO show log
        m_warnMessage->setMessageType(KMessageWidget::Warning);
        m_warnMessage->setText(i18n("Transcoding failed!"));
        m_warnMessage->animatedShow();
        m_transcodeQueue.clear();
    }
    m_duration = 0;
    m_transcodeProcess.close();
    if (m_transcodeQueue.isEmpty()) {
        m_view.convert_box->setHidden(true);
        slotCheckProfiles();
    }
}


235 236 237 238
void DvdWizardVob::slotCheckProfiles()
{
    bool conflict = false;
    int comboProfile = m_view.dvd_profile->currentIndex();
239
    for (int i = 0; i < m_vobList->topLevelItemCount(); ++i) {
240
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
241
        if (item->data(0, Qt::UserRole + 1).toInt() != comboProfile) {
242 243 244
            conflict = true;
            break;
        }
245
    }
246
    m_transcodeAction->setEnabled(conflict);
247
    if (conflict) {
248
        showProfileError();
249
    }
250
    else {
251
        m_warnMessage->animatedHide();
252
    }
253
}
254

255
void DvdWizardVob::slotAddVobList(QList<QUrl> list)
256
{
257
    if (list.isEmpty()) {
258
        QString allExtensions = ClipCreationDialog::getExtensions().join(QStringLiteral(" "));
259
        QString dialogFilter = i18n("All Supported Files") + " (" + allExtensions + ");; " + i18n("MPEG Files") + " (*.mpeg *.mpg *.vob);; " + i18n("All Files") + " (*.*)";
260
        list = QFileDialog::getOpenFileUrls(this, i18n("Add new video file"), QUrl::fromLocalFile(KRecentDirs::dir(QStringLiteral(":KdenliveDvdFolder"))), dialogFilter);
261
        if (!list.isEmpty()) {
262
            KRecentDirs::add(QStringLiteral(":KdenliveDvdFolder"), list.at(0).adjusted(QUrl::RemoveFilename).path());
263 264 265
        }
    }
    foreach(const QUrl &url, list) {
266
        slotAddVobFile(url, QString(), false);
267 268 269 270 271
    }
    slotCheckVobList();
    slotCheckProfiles();
}

272
void DvdWizardVob::slotAddVobFile(QUrl url, const QString &chapters, bool checkFormats)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
273
{
274
    if (!url.isValid()) return;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
275 276
    QFile f(url.path());
    qint64 fileSize = f.size();
277

278
    Mlt::Profile profile;
279
    profile.set_explicit(false);
280 281
    QTreeWidgetItem *item = new QTreeWidgetItem(m_vobList, QStringList() << url.path() << QString() << KIO::convertSize(fileSize));
    item->setData(2, Qt::UserRole, fileSize);
282
    item->setData(0, Qt::DecorationRole, QIcon::fromTheme(QStringLiteral("video-x-generic")).pixmap(60, 45));
283
    item->setToolTip(0, url.path());
284 285 286 287

    QString resource = url.path();
    resource.prepend("avformat:");
    Mlt::Producer *producer = new Mlt::Producer(profile, resource.toUtf8().data());
288
    if (producer && producer->is_valid() && !producer->is_blank()) {
289
        double fps = profile.fps();
290
        profile.from_producer(*producer);
291 292 293 294 295 296 297 298
        profile.set_explicit(true);
        if (profile.fps() != fps) {
            // fps changed, rebuild producer
            delete producer;
            producer = new Mlt::Producer(profile, resource.toUtf8().data());
        }
    }
    if (producer && producer->is_valid() && !producer->is_blank()) {
299
        int width = 45.0 * profile.dar();
300
        if (width % 2 == 1) width++;
301
        item->setData(0, Qt::DecorationRole, QPixmap::fromImage(KThumb::getFrame(producer, 0, width, 45)));
302 303 304
        int playTime = producer->get_playtime();
        item->setText(1, Timecode::getStringTimecode(playTime, profile.fps()));
        item->setData(1, Qt::UserRole, playTime);
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
        int standard = -1;
        int aspect = profile.dar() * 100;
        if (profile.height() == 576 && profile.fps() == 25.0) {
            if (aspect > 150) standard = 1;
            else standard = 0;
        }
        else if (profile.height() == 480 && qAbs(profile.fps() - 30000.0 / 1001) < 0.2) {
            if (aspect > 150) standard = 3;
            else standard = 2;
        }
        QString standardName;
        switch (standard) {
        case 3:
            standardName = i18n("NTSC 16:9");
            break;
        case 2:
            standardName = i18n("NTSC 4:3");
            break;
        case 1:
            standardName = i18n("PAL 16:9");
            break;
        case 0:
            standardName = i18n("PAL 4:3");
            break;
        default:
            standardName = i18n("Unknown");
        }
332
        standardName.append(QStringLiteral(" | %1x%2, %3fps").arg(profile.width()).arg(profile.height()).arg(profile.fps()));
333 334 335 336 337 338 339 340 341 342 343 344
        item->setData(0, Qt::UserRole, standardName);
        item->setData(0, Qt::UserRole + 1, standard);
        item->setData(0, Qt::UserRole + 2, QSize(profile.dar() * profile.height(), profile.height()));
        if (m_vobList->topLevelItemCount() == 1) {
            // This is the first added movie, auto select DVD format
            if (standard >= 0) {
                m_view.dvd_profile->blockSignals(true);
                m_view.dvd_profile->setCurrentIndex(standard);
                m_view.dvd_profile->blockSignals(false);
            }
        }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
345
    }
346
    else {
347 348
        // Cannot load movie, reject
        showError(i18n("The clip %1 is invalid.", url.fileName()));
349
    }
350
    if (producer) delete producer;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
351

352
    if (chapters.isEmpty() == false) {
353
        item->setData(1, Qt::UserRole + 1, chapters);
354
    }
355
    else if (QFile::exists(url.path() + ".dvdchapter")) {
356 357 358 359
        // insert chapters as children
        QFile file(url.path() + ".dvdchapter");
        if (file.open(QIODevice::ReadOnly)) {
            QDomDocument doc;
360 361 362 363
            if (doc.setContent(&file) == false) {
                file.close();
                return;
            }
364
            file.close();
365
            QDomNodeList chapters = doc.elementsByTagName(QStringLiteral("chapter"));
366
            QStringList chaptersList;
367
            for (int j = 0; j < chapters.count(); ++j) {
368
                chaptersList.append(QString::number(chapters.at(j).toElement().attribute(QStringLiteral("time")).toInt()));
369
            }
370
            item->setData(1, Qt::UserRole + 1, chaptersList.join(QStringLiteral(";")));
371
        }
372 373
    } else // Explicitly add a chapter at 00:00:00:00
        item->setData(1, Qt::UserRole + 1, "0");
374

375
    if (checkFormats) {
376 377
        slotCheckVobList();
        slotCheckProfiles();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
378 379 380 381 382
    }
}

void DvdWizardVob::slotDeleteVobFile()
{
383
    QTreeWidgetItem *item = m_vobList->currentItem();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
384 385 386
    if (item == NULL) return;
    delete item;
    slotCheckVobList();
387
    slotCheckProfiles();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
388 389 390
}


391 392
void DvdWizardVob::setUrl(const QString &url)
{
393
    slotAddVobFile(QUrl::fromLocalFile(url));
394 395
}

396 397
QStringList DvdWizardVob::selectedUrls() const
{
398
    QStringList result;
399
    QString path;
400
    int max = m_vobList->topLevelItemCount();
401 402
    int i = 0;
    if (m_view.use_intro->isChecked()) {
403 404
        // First movie is only for intro
        i = 1;
405
    }
406
    for (; i < max; ++i) {
407
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
408
        if (item) result.append(item->text(0));
409 410 411 412
    }
    return result;
}

413 414

QStringList DvdWizardVob::durations() const
415 416
{
    QStringList result;
417
    QString path;
418
    int max = m_vobList->topLevelItemCount();
419 420
    int i = 0;
    if (m_view.use_intro->isChecked()) {
421 422
        // First movie is only for intro
        i = 1;
423
    }
424
    for (; i < max; ++i) {
425
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
426
        if (item) result.append(QString::number(item->data(1, Qt::UserRole).toInt()));
427 428 429 430
    }
    return result;
}

431 432 433 434
QStringList DvdWizardVob::chapters() const
{
    QStringList result;
    QString path;
435
    int max = m_vobList->topLevelItemCount();
436 437
    int i = 0;
    if (m_view.use_intro->isChecked()) {
438 439
        // First movie is only for intro
        i = 1;
440
    }
441
    for (; i < max; ++i) {
442
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
443 444 445
        if (item) {
            result.append(item->data(1, Qt::UserRole + 1).toString());
        }
446 447 448 449
    }
    return result;
}

450
void DvdWizardVob::updateChapters(const QMap <QString, QString> &chaptersdata)
451
{
452
    int max = m_vobList->topLevelItemCount();
453 454
    int i = 0;
    if (m_view.use_intro->isChecked()) {
455 456
        // First movie is only for intro
        i = 1;
457
    }
458
    for (; i < max; ++i) {
459
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
460 461
        if (chaptersdata.contains(item->text(0)))
            item->setData(1, Qt::UserRole + 1, chaptersdata.value(item->text(0)));
462 463 464
    }
}

465
int DvdWizardVob::duration(int ix) const
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
466
{
467
    int result = -1;
468
    QTreeWidgetItem *item = m_vobList->topLevelItem(ix);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
469
    if (item) {
470
        result = item->data(1, Qt::UserRole).toInt();
471
    }
472 473 474
    return result;
}

475
const QString DvdWizardVob::introMovie() const
476
{
477
    QString url;
478 479
    if (m_view.use_intro->isChecked() && m_vobList->topLevelItemCount() > 0)
        url = m_vobList->topLevelItem(0)->text(0);
480
    return url;
481 482
}

483
void DvdWizardVob::setUseIntroMovie(bool use)
484
{
485
    m_view.use_intro->setChecked(use);
486 487
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
488
void DvdWizardVob::slotCheckVobList()
489
{
490
    emit completeChanged();
491 492
    int max = m_vobList->topLevelItemCount();
    QTreeWidgetItem *item = m_vobList->currentItem();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
493 494 495
    bool hasItem = true;
    if (item == NULL) hasItem = false;
    m_view.button_delete->setEnabled(hasItem);
496
    if (hasItem && m_vobList->indexOfTopLevelItem(item) == 0) m_view.button_up->setEnabled(false);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
497
    else m_view.button_up->setEnabled(hasItem);
498
    if (hasItem && m_vobList->indexOfTopLevelItem(item) == max - 1) m_view.button_down->setEnabled(false);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
499 500
    else m_view.button_down->setEnabled(hasItem);

501
    qint64 totalSize = 0;
502
    for (int i = 0; i < max; ++i) {
503
        item = m_vobList->topLevelItem(i);
504 505
        if (item)
            totalSize += (qint64) item->data(2, Qt::UserRole).toInt();
506 507
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
508
    qint64 maxSize = (qint64) 47000 * 100000;
509 510
    m_capacityBar->setValue(100 * totalSize / maxSize);
    m_capacityBar->setText(KIO::convertSize(totalSize));
511 512
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
513 514
void DvdWizardVob::slotItemUp()
{
515
    QTreeWidgetItem *item = m_vobList->currentItem();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
516
    if (item == NULL) return;
517
    int index = m_vobList->indexOfTopLevelItem(item);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
518
    if (index == 0) return;
519
    m_vobList->insertTopLevelItem(index - 1, m_vobList->takeTopLevelItem(index));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
520 521 522 523
}

void DvdWizardVob::slotItemDown()
{
524 525
    int max = m_vobList->topLevelItemCount();
    QTreeWidgetItem *item = m_vobList->currentItem();
526 527
    if (item == NULL)
        return;
528
    int index = m_vobList->indexOfTopLevelItem(item);
529 530
    if (index == max - 1)
        return;
531
    m_vobList->insertTopLevelItem(index + 1, m_vobList->takeTopLevelItem(index));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
532 533
}

534
DVDFORMAT DvdWizardVob::dvdFormat() const
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
535
{
536
    return (DVDFORMAT) m_view.dvd_profile->currentIndex();
537 538
}

539
const QString DvdWizardVob::dvdProfile() const
540
{
541 542
    QString profile;
    switch (m_view.dvd_profile->currentIndex()) {
543
    case PAL_WIDE:
544
        profile = QStringLiteral("dv_pal_wide");
545 546
        break;
    case NTSC:
547
        profile = QStringLiteral("dv_ntsc");
548 549
        break;
    case NTSC_WIDE:
550
        profile = QStringLiteral("dv_ntsc_wide");
551 552
        break;
    default:
553
        profile = QStringLiteral("dv_pal");
554 555 556 557 558 559 560 561 562
    }
    return profile;
}

//static
QString DvdWizardVob::getDvdProfile(DVDFORMAT format)
{
    QString profile;
    switch (format) {
563
    case PAL_WIDE:
564
        profile = QStringLiteral("dv_pal_wide");
565 566
        break;
    case NTSC:
567
        profile = QStringLiteral("dv_ntsc");
568 569
        break;
    case NTSC_WIDE:
570
        profile = QStringLiteral("dv_ntsc_wide");
571 572
        break;
    default:
573
        profile = QStringLiteral("dv_pal");
574 575
    }
    return profile;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
576 577
}

578
void DvdWizardVob::setProfile(const QString& profile)
579
{
580
    if (profile == QLatin1String("dv_pal_wide"))
581
        m_view.dvd_profile->setCurrentIndex(PAL_WIDE);
582
    else if (profile == QLatin1String("dv_ntsc"))
583
        m_view.dvd_profile->setCurrentIndex(NTSC);
584
    else if (profile == QLatin1String("dv_ntsc_wide"))
585 586 587
        m_view.dvd_profile->setCurrentIndex(NTSC_WIDE);
    else
        m_view.dvd_profile->setCurrentIndex(PAL);
588
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
589

590 591
void DvdWizardVob::clear()
{
592
    m_vobList->clear();
593
}
594 595 596

void DvdWizardVob::slotTranscodeFiles()
{
597
    m_warnMessage->animatedHide();
Vincent Pinon's avatar
Vincent Pinon committed
598
    // Find transcoding info related to selected DVD profile
599
    KSharedConfigPtr config = KSharedConfig::openConfig(QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("kdenlivetranscodingrc")), KConfig::CascadeConfig);
600 601 602 603 604 605
    KConfigGroup transConfig(config, "Transcoding");
    // read the entries
    QString profileEasyName;
    QSize destSize;
    QSize finalSize;
    switch (m_view.dvd_profile->currentIndex()) {
606
    case PAL_WIDE:
607
        profileEasyName = QStringLiteral("DVD PAL 16:9");
608 609 610 611
        destSize = QSize(1024, 576);
        finalSize = QSize(720, 576);
        break;
    case NTSC:
612
        profileEasyName = QStringLiteral("DVD NTSC 4:3");
613 614 615 616
        destSize = QSize(640, 480);
        finalSize = QSize(720, 480);
        break;
    case NTSC_WIDE:
617
        profileEasyName = QStringLiteral("DVD NTSC 16:9");
618 619 620 621
        destSize = QSize(853, 480);
        finalSize = QSize(720, 480);
        break;
    default:
622
        profileEasyName = QStringLiteral("DVD PAL 4:3");
623 624
        destSize = QSize(768, 576);
        finalSize = QSize(720, 576);
625
    }
626
    QString params = transConfig.readEntry(profileEasyName);
627 628 629
    m_transcodeQueue.clear();
    m_view.convert_progress->setValue(0);
    m_duration = 0;
630 631 632
    // Transcode files that do not match selected profile
    int max = m_vobList->topLevelItemCount();
    int format = m_view.dvd_profile->currentIndex();
633
    m_view.convert_box->setVisible(true);
634
    for (int i = 0; i < max; ++i) {
635
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
636 637 638 639 640 641 642 643 644 645 646
        if (item->data(0, Qt::UserRole + 1).toInt() != format) {
            // File needs to be transcoded
            m_transcodeAction->setEnabled(false);
            QSize original = item->data(0, Qt::UserRole + 2).toSize();
            double input_aspect= (double) original.width() / original.height();
            QStringList postParams;
            if (input_aspect > (double) destSize.width() / destSize.height()) {
                // letterboxing
                int conv_height = (int) (destSize.width() / input_aspect);
                int conv_pad = (int) (((double) (destSize.height() - conv_height)) / 2.0);
                if (conv_pad %2 == 1) conv_pad --;
647
                postParams << QStringLiteral("-vf") << QStringLiteral("scale=%1:%2,pad=%3:%4:0:%5,setdar=%6").arg(finalSize.width()).arg(destSize.height() - 2 * conv_pad).arg(finalSize.width()).arg(finalSize.height()).arg(conv_pad).arg(input_aspect);
648 649 650 651 652
            } else {
                // pillarboxing
                int conv_width = (int) (destSize.height() * input_aspect);
                int conv_pad = (int) (((double) (destSize.width() - conv_width)) / destSize.width() * finalSize.width() / 2.0);
                if (conv_pad %2 == 1) conv_pad --;
653
                postParams << QStringLiteral("-vf") << QStringLiteral("scale=%1:%2,pad=%3:%4:%5:0,setdar=%6").arg(finalSize.width() - 2 * conv_pad).arg(destSize.height()).arg(finalSize.width()).arg(finalSize.height()).arg(conv_pad).arg(input_aspect);
654
            }
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
            TranscodeJobInfo jobInfo;
            jobInfo.filename = item->text(0);
            jobInfo.params = params.section(';', 0, 0);
            jobInfo.postParams = postParams;
            m_transcodeQueue << jobInfo;
        }
    }
    processTranscoding();
}

void DvdWizardVob::processTranscoding()
{
    if (m_transcodeQueue.isEmpty()) {
        return;
    }
    m_currentTranscoding = m_transcodeQueue.takeFirst();
    QStringList parameters;
    QStringList postParams = m_currentTranscoding.postParams;
    QString params = m_currentTranscoding.params;
674 675
    QString extension = params.section(QStringLiteral("%1"), 1, 1).section(' ', 0, 0);
    parameters << QStringLiteral("-i") << m_currentTranscoding.filename;
676 677 678 679 680 681
    if (QFile::exists(m_currentTranscoding.filename + extension)) {
        if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", m_currentTranscoding.filename + extension)) == KMessageBox::No) {
            // TODO inform about abortion
            m_transcodeQueue.clear();
            return;
        }
682
        parameters << QStringLiteral("-y");
683 684 685 686 687 688 689 690 691 692
    }

    bool replaceVfParams = false;
    QStringList splitted = params.split(' ');
    foreach(QString s, splitted) {
        if (replaceVfParams) {
            parameters << postParams.at(1);
            replaceVfParams = false;
        } else if (s.startsWith(QLatin1String("%1"))) {
            parameters << s.replace(0, 2, m_currentTranscoding.filename);
693
        } else if (!postParams.isEmpty() && s == QLatin1String("-vf")) {
694 695 696 697
            replaceVfParams = true;
            parameters << s;
        } else {
            parameters << s;
698
        }
699
    }
700 701 702
    qDebug()<<" / / /STARTING TCODE JB: \n"<<KdenliveSettings::ffmpegpath()<<" = "<< parameters;
    m_transcodeProcess.start(KdenliveSettings::ffmpegpath(), parameters);
    m_view.convert_label->setText(i18n("Transcoding: %1", QUrl::fromLocalFile(m_currentTranscoding.filename).fileName()));
703 704
}

705
void DvdWizardVob::slotTranscodedClip(const QString &src, const QString &transcoded)
706
{
707
    if (transcoded.isEmpty()) {
708 709 710
        // Transcoding canceled or failed
        m_transcodeAction->setEnabled(true);
        return;
711
    }
712
    int max = m_vobList->topLevelItemCount();
713
    for (int i = 0; i < max; ++i) {
714
        QTreeWidgetItem *item = m_vobList->topLevelItem(i);
715
        if (QUrl::fromLocalFile(item->text(0)).path() == src) {
716
            // Replace movie with transcoded version
717
            item->setText(0, transcoded);
718

719
            QFile f(transcoded);
720 721 722 723 724 725
            qint64 fileSize = f.size();

            Mlt::Profile profile;
            profile.set_explicit(false);
            item->setText(2, KIO::convertSize(fileSize));
            item->setData(2, Qt::UserRole, fileSize);
726
            item->setData(0, Qt::DecorationRole, QIcon::fromTheme(QStringLiteral("video-x-generic")).pixmap(60, 45));
727
            item->setToolTip(0, transcoded);
728

729
            QString resource = transcoded;
730 731 732
            resource.prepend("avformat:");
            Mlt::Producer *producer = new Mlt::Producer(profile, resource.toUtf8().data());
            if (producer && producer->is_valid() && !producer->is_blank()) {
733
                double fps = profile.fps();
734
                profile.from_producer(*producer);
735 736 737 738 739 740 741 742
                profile.set_explicit(true);
                if (profile.fps() != fps) {
                    // fps changed, rebuild producer
                    delete producer;
                    producer = new Mlt::Producer(profile, resource.toUtf8().data());
                  }
            }
            if (producer && producer->is_valid() && !producer->is_blank()) {
743
                int width = 45.0 * profile.dar();
744
                if (width % 2 == 1) width++;
745
                item->setData(0, Qt::DecorationRole, QPixmap::fromImage(KThumb::getFrame(producer, 0, width, 45)));
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
                int playTime = producer->get_playtime();
                item->setText(1, Timecode::getStringTimecode(playTime, profile.fps()));
                item->setData(1, Qt::UserRole, playTime);
                int standard = -1;
                int aspect = profile.dar() * 100;
                if (profile.height() == 576) {
                    if (aspect > 150) standard = 1;
                    else standard = 0;
                }
                else if (profile.height() == 480) {
                    if (aspect > 150) standard = 3;
                    else standard = 2;
                }
                QString standardName;
                switch (standard) {
                case 3:
                    standardName = i18n("NTSC 16:9");
                    break;
                case 2:
                    standardName = i18n("NTSC 4:3");
                    break;
                case 1:
                    standardName = i18n("PAL 16:9");
                    break;
                case 0:
                    standardName = i18n("PAL 4:3");
                    break;
                default:
                    standardName = i18n("Unknown");
                }
                item->setData(0, Qt::UserRole, standardName);
                item->setData(0, Qt::UserRole + 1, standard);
                item->setData(0, Qt::UserRole + 2, QSize(profile.dar() * profile.height(), profile.height()));
            }
            else {
                // Cannot load movie, reject
782
                showError(i18n("The clip %1 is invalid.", transcoded));
783 784 785
            }
            if (producer) delete producer;
            slotCheckVobList();
786
            if (m_transcodeQueue.isEmpty()) slotCheckProfiles();
787 788
            break;
        }
789 790 791 792 793 794 795 796 797 798 799
    }
}

void DvdWizardVob::showProfileError()
{
    m_warnMessage->setText(i18n("Your clips do not match selected DVD format, transcoding required."));
    m_warnMessage->setCloseButtonVisible(false);
    m_warnMessage->addAction(m_transcodeAction);
    m_warnMessage->animatedShow();
}

800
void DvdWizardVob::showError(const QString &error)
801 802 803 804 805
{
    m_warnMessage->setText(error);
    m_warnMessage->setCloseButtonVisible(true);
    m_warnMessage->removeAction(m_transcodeAction);
    m_warnMessage->animatedShow();
Laurent Montel's avatar
Laurent Montel committed
806 807
}