renderjob.cpp 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/***************************************************************************
 *   Copyright (C) 2007 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          *
 ***************************************************************************/

#include <QtDBus>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
21
#include <QFile>
22
#include <QThread>
23 24
#include "renderjob.h"

25 26 27 28 29 30 31 32
// Can't believe I need to do this to sleep.
class SleepThread : QThread {
public:
    virtual void run() {};
    static void msleep(unsigned long msecs) {
        QThread::msleep(msecs);
    }
};
33

34 35
static QDBusConnection connection(QLatin1String(""));

36
RenderJob::RenderJob(bool erase, const QString &renderer, const QString &profile, const QString &rendermodule, const QString &player, const QString &scenelist, const QString &dest, const QStringList &preargs, const QStringList &args, int in, int out) : QObject(), m_jobUiserver(NULL), m_kdenliveinterface(NULL) {
37 38 39 40
    m_scenelist = scenelist;
    m_dest = dest;
    m_player = player;
    m_progress = 0;
41
    m_erase = erase;
42
    m_renderProcess = new QProcess;
43
    m_prog = renderer;
44
    m_args << scenelist;
45 46
    if (in != -1) m_args << "in=" + QString::number(in);
    if (out != -1) m_args << "out=" + QString::number(out);
47 48
    m_args << preargs;
    //qDebug()<<"PRE ARGS: "<<preargs;
49
    if (scenelist.startsWith("consumer:")) {
50 51 52
        // Use MLT's producer_consumer, needs a different syntax for profile:
        m_args << "profile=" + profile;
    } else m_args << "-profile" << profile;
53
    m_args << "-consumer" << rendermodule + ":" + m_dest << "progress=1" << args;
54 55
    connect(m_renderProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotIsOver(int, QProcess::ExitStatus)));
    m_renderProcess->setReadChannel(QProcess::StandardError);
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    // Create a log of every render process.
    m_logfile.setAutoRemove(false);
    m_logfile.setFileTemplate(QDir::tempPath() + "/kdenlive_render.log.XXXXXXXX");
    if (m_logfile.open()) {
        qDebug() << "Writing render log to " << m_logfile.fileName();

    } else {
        qDebug() << "Unable to log to " << m_logfile.fileName();
    }
    m_logstream.setDevice(&m_logfile);
    m_logstream << "Log starting. Dumping contents of " << scenelist << endl;
    QFile file(scenelist);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        m_logstream << "Unable to read contents of " << scenelist << endl;
    } else {
        m_logstream.flush();
        QTextStream in(&file);
        m_logstream << in.readAll() << endl;
    }
75 76 77 78
}


RenderJob::~RenderJob() {
79
    if (m_renderProcess) delete m_renderProcess;
80
    m_logfile.close();
81 82
}

83 84 85 86
void RenderJob::slotAbort(const QString& url) {
    if (m_dest == url) slotAbort();
}

87
void RenderJob::slotAbort() {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
88
    qDebug() << "Kdenlive-render: JOB ABORTED BY USER...";
89
    m_renderProcess->kill();
90 91 92 93 94

    if (m_kdenliveinterface) {
        m_dbusargs[1] = -3;
        m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, "setRenderingProgress", m_dbusargs);
    }
95
    if (m_jobUiserver) m_jobUiserver->call("terminate", QString());
96 97 98 99 100 101
    if (m_erase) {
        QFile f(m_scenelist);
        f.remove();
    }
    QFile f(m_dest);
    f.remove();
102 103 104
    m_logstream << "Job aborted by user" << endl;
    m_logstream.flush();
    m_logfile.close();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
105
    qApp->quit();
106 107 108
}

void RenderJob::receivedStderr() {
109
    QString result = QString(m_renderProcess->readAllStandardError()).simplified();
110
    m_logstream << "ReceivedStderr from inigo: " << result << endl;
111 112
    result = result.section(" ", -1);
    int pro = result.toInt();
113 114 115 116 117
    if (m_kdenliveinterface && pro > m_progress) {
        m_dbusargs[1] = pro;
        m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, "setRenderingProgress", m_dbusargs);
    }
    if (m_jobUiserver && pro > m_progress) {
118
        m_progress = pro;
119 120 121 122
        m_jobUiserver->call("setPercent", (uint) m_progress);
        int seconds = m_startTime.secsTo(QTime::currentTime());
        seconds = seconds * (100 - m_progress) / m_progress;
        m_jobUiserver->call("setDescriptionField", (uint) 1, tr("Remaining time"), QTime(0, 0, seconds).toString("hh:mm:ss"));
123
    }
124

125 126 127
}

void RenderJob::start() {
128
    QDBusConnectionInterface* interface = QDBusConnection::sessionBus().interface();
129
    if (interface) {
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        if (!interface->isServiceRegistered("org.kde.JobViewServer")) {
            qDebug() << "No org.kde.JobViewServer registered, trying to start kuiserver";
            m_logstream << "No org.kde.JobViewServer registered, trying to start kuiserver";
            if (QProcess::startDetached("kuiserver")) {
                qDebug() << "Started kuiserver";
                m_logstream << "Started kuiserver";
                // Give it a couple of seconds to start
                QTime t;
                t.start();
                while (!interface->isServiceRegistered("org.kde.JobViewServer") && t.elapsed() < 3000) {
                    SleepThread::msleep(100);   //Sleep 100 ms
                }
            } else {
                qDebug() << "Failed to start kuiserver";
                m_logstream << "Failed to start kuiserver";
            }
        }

        if (interface->isServiceRegistered("org.kde.JobViewServer")) {
            QDBusInterface kuiserver("org.kde.JobViewServer", "/JobViewServer", "org.kde.JobViewServer");
            QDBusReply<QDBusObjectPath> objectPath = kuiserver.call("requestView", "kdenlive", "kdenlive", 1);
            QString reply = ((QDBusObjectPath) objectPath).path();
            m_jobUiserver = new QDBusInterface("org.kde.JobViewServer", reply, "org.kde.JobView");
            if (m_jobUiserver) {
                m_startTime = QTime::currentTime();
                m_jobUiserver->call("setPercent", (uint) 0);
                QDBusReply<QString> reply = m_jobUiserver->call("setInfoMessage", tr("Rendering %1").arg(QFileInfo(m_dest).fileName()));
                m_jobUiserver->call("setDescriptionField", (uint) 0, tr("Rendering to"), m_dest);
                QDBusConnection::sessionBus().connect("org.kde.JobViewServer", reply, "org.kde.JobView", "cancelRequested", this, SLOT(slotAbort()));
            }
        }
161
    }
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

    QString kdenliveId;
    QDBusConnection connection = QDBusConnection::sessionBus();
    QDBusConnectionInterface *ibus = connection.interface();
    const QStringList services = ibus->registeredServiceNames();
    foreach(const QString &service, services) {
        if (!service.startsWith("org.kde.kdenlive"))
            continue;
        kdenliveId = service;
        break;
    }

    QDBusConnection bus = QDBusConnection::sessionBus();
    m_kdenliveinterface = new QDBusInterface(kdenliveId,
            "/MainWindow",
            "org.kdenlive.MainWindow",
            bus,
            this);

    if (m_kdenliveinterface) {
        m_dbusargs.append(m_dest);
        m_dbusargs.append((int) 0);
        m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, "setRenderingProgress", m_dbusargs);
185 186
        connect(m_kdenliveinterface, SIGNAL(abortRenderJob(const QString &)),
                this, SLOT(slotAbort(const QString&)));
187 188
    }

189 190
    // Because of the logging, we connect to stderr in all cases.
    connect(m_renderProcess, SIGNAL(readyReadStandardError()), this, SLOT(receivedStderr()));
191
    m_renderProcess->start(m_prog, m_args);
192 193
    m_logstream << "Started render process: " << m_prog << " " << m_args.join(" ") << endl;
    qDebug() << "Started render process: " << m_prog << " " << m_args.join(" ");
194 195 196 197
}


void RenderJob::slotIsOver(int exitcode, QProcess::ExitStatus status) {
198
    if (m_jobUiserver) m_jobUiserver->call("terminate", QString());
199 200 201 202
    if (m_erase) {
        QFile f(m_scenelist);
        f.remove();
    }
203 204
    if (status == QProcess::CrashExit) {
        // rendering crashed
205 206 207 208
        if (m_kdenliveinterface) {
            m_dbusargs[1] = -2;
            m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, "setRenderingProgress", m_dbusargs);
        }
209 210
        QStringList args;
        args << "--error" << tr("Rendering of %1 aborted, resulting video will probably be corrupted.").arg(m_dest);
211 212
        m_logstream << "Rendering of " << m_dest << " aborted, resulting video will probably be corrupted." << endl;
        qDebug() << "Rendering of " << m_dest << " aborted, resulting video will probably be corrupted.";
213
        QProcess::startDetached("kdialog", args);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
214
    } else {
215 216 217 218
        if (m_kdenliveinterface) {
            m_dbusargs[1] = -1;
            m_kdenliveinterface->callWithArgumentList(QDBus::NoBlock, "setRenderingProgress", m_dbusargs);
        }
219 220 221
        QDBusConnectionInterface* interface = QDBusConnection::sessionBus().interface();
        if (interface && interface->isServiceRegistered("org.kde.knotify")) {
            QDBusMessage m = QDBusMessage::createMethodCall("org.kde.knotify",
222 223 224
                             "/Notify",
                             "org.kde.KNotify",
                             "event");
225 226 227 228 229 230 231 232 233 234 235
            int seconds = m_startTime.secsTo(QTime::currentTime());
            QList<QVariant> args;
            args.append(QString("RenderFinished"));   // action name
            args.append(QString("kdenlive"));   // app name
            args.append(QVariantList());   // contexts
            args.append(tr("Rendering of %1 finished in %2").arg(m_dest, QTime(0, 0, seconds).toString("hh:mm:ss")));   // body
            args.append(QByteArray());   // app icon
            QStringList actionList;
            args.append(actionList);   // actions
            qlonglong wid;
            args.append(wid);   // win id
236

237 238 239 240 241
            m.setArguments(args);
            QDBusMessage replyMsg = QDBusConnection::sessionBus().call(m);
        }
        m_logstream << "Rendering of " << m_dest << " finished" << endl;
        qDebug() << "Rendering of " << m_dest << " finished";
242
        if (m_player != "-") {
243
            m_logstream << "Starting player" << endl;
244 245 246 247
            QStringList args;
            args << m_dest;
            QProcess::startDetached(m_player, args);
        }
248
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
249
    qApp->quit();
250 251 252
}

#include "renderjob.moc"