Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Multimedia
Kdenlive
Commits
00f0c3a5
Commit
00f0c3a5
authored
Apr 25, 2021
by
Jean-Baptiste Mardelle
Browse files
Convert transcoding to new taskmanager
parent
15228e34
Changes
6
Hide whitespace changes
Inline
Side-by-side
src/bin/bin.cpp
View file @
00f0c3a5
...
...
@@ -30,7 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "doc/kdenlivedoc.h"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "jobs/audiothumbjob.hpp"
#include "jobs/transcode
clipjob
.h"
#include "jobs/transcode
task
.h"
#include "jobs/taskmanager.h"
#include "jobs/thumbjob.hpp"
#include "jobs/cliploadtask.h"
...
...
@@ -4494,6 +4494,11 @@ void Bin::requestTranscoding(const QString &url, const QString &id)
connect
(
m_transcodingDialog
,
&
QDialog
::
accepted
,
this
,
[
=
]
()
{
qDebug
()
<<
"==== STARTING TCODE JOB: "
<<
m_transcodingDialog
->
ids
().
front
()
<<
" = "
<<
m_transcodingDialog
->
params
();
//pCore->jobManager()->startJob<TranscodeJob>(m_transcodingDialog->ids(), -1, QString(), m_transcodingDialog->params(), true);
std
::
vector
<
QString
>
ids
=
m_transcodingDialog
->
ids
();
for
(
QString
id
:
ids
)
{
std
::
shared_ptr
<
ProjectClip
>
clip
=
m_itemModel
->
getClipByBinID
(
id
);
TranscodeTask
::
start
({
ObjectType
::
BinClip
,
id
.
toInt
()},
m_transcodingDialog
->
params
(),
-
1
,
-
1
,
true
,
clip
.
get
());
}
delete
m_transcodingDialog
;
m_transcodingDialog
=
nullptr
;
});
...
...
src/jobs/CMakeLists.txt
View file @
00f0c3a5
...
...
@@ -7,6 +7,7 @@ set(kdenlive_SRCS
jobs/audiolevelstask.cpp
jobs/cliploadtask.cpp
jobs/proxytask.cpp
jobs/transcodetask.cpp
jobs/jobmanager.cpp
jobs/cachejob.cpp
jobs/loadjob.cpp
...
...
src/jobs/transcodetask.cpp
0 → 100644
View file @
00f0c3a5
/***************************************************************************
* *
* Copyright (C) 2011 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 "transcodetask.h"
#include "bin/bin.h"
#include "mainwindow.h"
#include "bin/projectclip.h"
#include "bin/projectfolder.h"
#include "bin/projectitemmodel.h"
#include "bin/clipcreator.hpp"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
#include "macros.hpp"
#include <QProcess>
#include <QTemporaryFile>
#include <QThread>
#include <klocalizedstring.h>
TranscodeTask
::
TranscodeTask
(
const
ObjectId
&
owner
,
QString
params
,
int
in
,
int
out
,
bool
replaceProducer
,
QObject
*
object
)
:
AbstractTask
(
owner
,
AbstractTask
::
TRANSCODEJOB
,
object
)
,
m_jobDuration
(
0
)
,
m_isFfmpegJob
(
true
)
,
m_transcodeParams
(
params
)
,
m_replaceProducer
(
replaceProducer
)
,
m_inPoint
(
in
)
,
m_outPoint
(
out
)
,
m_jobProcess
(
nullptr
)
{
}
void
TranscodeTask
::
start
(
const
ObjectId
&
owner
,
QString
params
,
int
in
,
int
out
,
bool
replaceProducer
,
QObject
*
object
,
bool
force
)
{
TranscodeTask
*
task
=
new
TranscodeTask
(
owner
,
params
,
in
,
out
,
replaceProducer
,
object
);
// See if there is already a task for this MLT service and resource.
if
(
pCore
->
taskManager
.
hasPendingJob
(
owner
,
AbstractTask
::
TRANSCODEJOB
))
{
delete
task
;
task
=
0
;
}
if
(
task
)
{
// Otherwise, start a new audio levels generation thread.
task
->
m_isForce
=
force
;
pCore
->
taskManager
.
startTask
(
owner
.
second
,
task
);
}
}
void
TranscodeTask
::
run
()
{
if
(
m_isCanceled
)
{
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
return
;
}
QMutexLocker
lk
(
&
m_runMutex
);
m_running
=
true
;
auto
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
QString
::
number
(
m_owner
.
second
));
const
QString
source
=
binClip
->
url
();
ClipType
::
ProducerType
type
=
binClip
->
clipType
();
QString
transcoderExt
=
m_transcodeParams
.
section
(
QLatin1String
(
"%1"
),
1
).
section
(
QLatin1Char
(
' '
),
0
,
0
);
if
(
transcoderExt
.
isEmpty
())
{
qDebug
()
<<
"// INVALID TRANSCODING PROFILE"
;
m_progress
=
100
;
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
return
;
}
QFileInfo
finfo
(
source
);
QString
fileName
=
finfo
.
fileName
().
section
(
QLatin1Char
(
'.'
),
0
,
-
2
);
QDir
dir
=
finfo
.
absoluteDir
();
int
fileCount
=
1
;
QString
num
=
QString
::
number
(
fileCount
).
rightJustified
(
4
,
'0'
,
false
);
QString
path
=
fileName
+
num
+
transcoderExt
;
while
(
dir
.
exists
(
path
))
{
++
fileCount
;
num
=
QString
::
number
(
fileCount
).
rightJustified
(
4
,
'0'
,
false
);
path
=
fileName
+
num
+
transcoderExt
;
}
QString
destUrl
=
dir
.
absoluteFilePath
(
fileName
);
destUrl
.
append
(
QString
::
number
(
fileCount
).
rightJustified
(
4
,
'0'
,
false
));
bool
result
;
if
(
type
==
ClipType
::
Playlist
||
type
==
ClipType
::
SlideShow
)
{
// change FFmpeg params to MLT format
m_isFfmpegJob
=
false
;
// insert transcoded filename
m_transcodeParams
.
replace
(
QStringLiteral
(
"%1"
),
QString
(
"-consumer %1"
));
// Convert param style
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QStringList
params
=
m_transcodeParams
.
split
(
QLatin1Char
(
'-'
),
QString
::
SkipEmptyParts
);
#else
QStringList
params
=
m_transcodeParams
.
split
(
QLatin1Char
(
'-'
),
Qt
::
SkipEmptyParts
);
#endif
QStringList
mltParameters
;
for
(
const
QString
&
s
:
qAsConst
(
params
))
{
QString
t
=
s
.
simplified
();
if
(
t
.
count
(
QLatin1Char
(
' '
))
==
0
)
{
t
.
append
(
QLatin1String
(
"=1"
));
}
else
{
if
(
t
.
contains
(
QLatin1String
(
"%1"
)))
{
// file name
mltParameters
.
prepend
(
t
.
section
(
QLatin1Char
(
' '
),
1
).
replace
(
QLatin1String
(
"%1"
),
QString
(
"avformat:%1"
).
arg
(
destUrl
)));
mltParameters
.
prepend
(
QStringLiteral
(
"-consumer"
));
continue
;
}
if
(
t
.
startsWith
(
QLatin1String
(
"aspect "
)))
{
// Fix aspect ratio calculation
t
.
replace
(
QLatin1Char
(
' '
),
QLatin1String
(
"=@"
));
t
.
replace
(
QLatin1Char
(
':'
),
QLatin1String
(
"/"
));
}
else
{
t
.
replace
(
QLatin1Char
(
' '
),
QLatin1String
(
"="
));
}
}
mltParameters
<<
t
;
}
int
threadCount
=
QThread
::
idealThreadCount
();
if
(
threadCount
>
2
)
{
threadCount
=
qMin
(
threadCount
-
1
,
4
);
}
else
{
threadCount
=
1
;
}
mltParameters
.
append
(
QStringLiteral
(
"real_time=-%1"
).
arg
(
threadCount
));
mltParameters
.
append
(
QStringLiteral
(
"threads=%1"
).
arg
(
threadCount
));
// Ask for progress reporting
mltParameters
<<
QStringLiteral
(
"progress=1"
);
if
(
m_outPoint
>
0
)
{
mltParameters
.
prepend
(
QString
(
"out=%1"
).
arg
(
m_outPoint
));
mltParameters
.
prepend
(
QString
(
"in=%1"
).
arg
(
m_inPoint
));
}
mltParameters
.
prepend
(
source
);
m_jobProcess
.
reset
(
new
QProcess
);
// m_jobProcess->setProcessChannelMode(QProcess::MergedChannels);
QObject
::
connect
(
this
,
&
TranscodeTask
::
jobCanceled
,
m_jobProcess
.
get
(),
&
QProcess
::
kill
,
Qt
::
DirectConnection
);
QObject
::
connect
(
m_jobProcess
.
get
(),
&
QProcess
::
readyReadStandardError
,
this
,
&
TranscodeTask
::
processLogInfo
);
m_jobProcess
->
start
(
KdenliveSettings
::
rendererpath
(),
mltParameters
);
m_jobProcess
->
waitForFinished
(
-
1
);
result
=
m_jobProcess
->
exitStatus
()
==
QProcess
::
NormalExit
;
}
else
{
m_isFfmpegJob
=
true
;
QStringList
parameters
;
if
(
KdenliveSettings
::
ffmpegpath
().
isEmpty
())
{
// FFmpeg not detected, cannot process the Job
m_errorMessage
.
prepend
(
i18n
(
"Failed to create proxy. FFmpeg not found, please set path in Kdenlive's settings Environment"
));
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
return
;
}
m_jobDuration
=
int
(
binClip
->
duration
().
seconds
());
parameters
<<
QStringLiteral
(
"-y"
);
if
(
m_inPoint
>
-
1
)
{
parameters
<<
QStringLiteral
(
"-ss"
)
<<
QString
::
number
(
GenTime
(
m_inPoint
,
pCore
->
getCurrentFps
()).
seconds
());
}
parameters
<<
QStringLiteral
(
"-stats"
)
<<
QStringLiteral
(
"-i"
)
<<
source
;
if
(
m_outPoint
>
-
1
)
{
parameters
<<
QStringLiteral
(
"-to"
)
<<
QString
::
number
(
GenTime
(
m_outPoint
-
m_inPoint
,
pCore
->
getCurrentFps
()).
seconds
());
}
// Only output error data
parameters
<<
QStringLiteral
(
"-v"
)
<<
QStringLiteral
(
"error"
);
QStringList
params
=
m_transcodeParams
.
split
(
QLatin1Char
(
' '
));
QStringList
finalParams
{
QStringLiteral
(
"-i"
),
source
};
for
(
const
QString
&
s
:
qAsConst
(
params
))
{
QString
t
=
s
.
simplified
();
if
(
t
.
startsWith
(
QLatin1String
(
"%1"
)))
{
parameters
<<
t
.
replace
(
QLatin1String
(
"%1"
),
destUrl
);
}
else
{
parameters
<<
t
;
}
}
qDebug
()
<<
"/// FULL PROXY PARAMS:
\n
"
<<
parameters
<<
"
\n
------"
;
m_jobProcess
.
reset
(
new
QProcess
);
// m_jobProcess->setProcessChannelMode(QProcess::MergedChannels);
QObject
::
connect
(
this
,
&
TranscodeTask
::
jobCanceled
,
m_jobProcess
.
get
(),
&
QProcess
::
kill
,
Qt
::
DirectConnection
);
QObject
::
connect
(
m_jobProcess
.
get
(),
&
QProcess
::
readyReadStandardError
,
this
,
&
TranscodeTask
::
processLogInfo
);
m_jobProcess
->
start
(
KdenliveSettings
::
ffmpegpath
(),
parameters
,
QIODevice
::
ReadOnly
);
m_jobProcess
->
waitForFinished
(
-
1
);
result
=
m_jobProcess
->
exitStatus
()
==
QProcess
::
NormalExit
;
}
destUrl
.
append
(
transcoderExt
);
// remove temporary playlist if it exists
m_progress
=
100
;
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
QMetaObject
::
invokeMethod
(
m_object
,
"updateJobProgress"
);
if
(
result
)
{
if
(
QFileInfo
(
destUrl
).
size
()
==
0
)
{
QFile
::
remove
(
destUrl
);
// File was not created
m_errorMessage
.
append
(
i18n
(
"Failed to create file."
));
}
else
{
QString
id
=
QString
::
number
(
m_owner
.
second
);
auto
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
id
);
if
(
m_replaceProducer
&&
binClip
)
{
QMap
<
QString
,
QString
>
sourceProps
;
QMap
<
QString
,
QString
>
newProps
;
sourceProps
.
insert
(
QStringLiteral
(
"resource"
),
binClip
->
url
());
sourceProps
.
insert
(
QStringLiteral
(
"kdenlive:clipname"
),
binClip
->
clipName
());
newProps
.
insert
(
QStringLiteral
(
"resource"
),
destUrl
);
newProps
.
insert
(
QStringLiteral
(
"kdenlive:clipname"
),
QFileInfo
(
destUrl
).
fileName
());
pCore
->
bin
()
->
slotEditClipCommand
(
id
,
sourceProps
,
newProps
);
}
else
{
QString
folder
=
QStringLiteral
(
"-1"
);
if
(
binClip
)
{
auto
containingFolder
=
std
::
static_pointer_cast
<
ProjectFolder
>
(
binClip
->
parent
());
if
(
containingFolder
)
{
folder
=
containingFolder
->
clipId
();
}
}
QMetaObject
::
invokeMethod
(
pCore
->
window
(),
"addProjectClip"
,
Qt
::
QueuedConnection
,
Q_ARG
(
const
QString
&
,
destUrl
),
Q_ARG
(
const
QString
&
,
folder
));
//id = ClipCreator::createClipFromFile(destUrl, folderId, pCore->projectItemModel());
}
}
}
else
{
// Proxy process crashed
QFile
::
remove
(
destUrl
);;
m_errorMessage
.
append
(
QString
::
fromUtf8
(
m_jobProcess
->
readAll
()));
}
}
void
TranscodeTask
::
processLogInfo
()
{
const
QString
buffer
=
QString
::
fromUtf8
(
m_jobProcess
->
readAllStandardError
());
m_logDetails
.
append
(
buffer
);
int
progress
=
0
;
if
(
m_isFfmpegJob
)
{
// Parse FFmpeg output
if
(
m_jobDuration
==
0
)
{
if
(
buffer
.
contains
(
QLatin1String
(
"Duration:"
)))
{
QString
data
=
buffer
.
section
(
QStringLiteral
(
"Duration:"
),
1
,
1
).
section
(
QLatin1Char
(
','
),
0
,
0
).
simplified
();
if
(
!
data
.
isEmpty
())
{
QStringList
numbers
=
data
.
split
(
QLatin1Char
(
':'
));
if
(
numbers
.
size
()
<
3
)
{
return
;
}
m_jobDuration
=
numbers
.
at
(
0
).
toInt
()
*
3600
+
numbers
.
at
(
1
).
toInt
()
*
60
+
numbers
.
at
(
2
).
toInt
();
}
}
}
else
if
(
buffer
.
contains
(
QLatin1String
(
"time="
)))
{
QString
time
=
buffer
.
section
(
QStringLiteral
(
"time="
),
1
,
1
).
simplified
().
section
(
QLatin1Char
(
' '
),
0
,
0
);
if
(
!
time
.
isEmpty
())
{
QStringList
numbers
=
time
.
split
(
QLatin1Char
(
':'
));
if
(
numbers
.
size
()
<
3
)
{
progress
=
time
.
toInt
();
if
(
progress
==
0
)
{
return
;
}
}
else
{
progress
=
numbers
.
at
(
0
).
toInt
()
*
3600
+
numbers
.
at
(
1
).
toInt
()
*
60
+
qRound
(
numbers
.
at
(
2
).
toDouble
());
}
}
m_progress
=
100
*
progress
/
m_jobDuration
;
QMetaObject
::
invokeMethod
(
m_object
,
"updateJobProgress"
);
//emit jobProgress(int(100.0 * progress / m_jobDuration));
}
}
else
{
// Parse MLT output
if
(
buffer
.
contains
(
QLatin1String
(
"percentage:"
)))
{
m_progress
=
buffer
.
section
(
QStringLiteral
(
"percentage:"
),
1
).
simplified
().
section
(
QLatin1Char
(
' '
),
0
,
0
).
toInt
();
QMetaObject
::
invokeMethod
(
m_object
,
"updateJobProgress"
);
//emit jobProgress(progress);
}
}
}
src/jobs/transcodetask.h
0 → 100644
View file @
00f0c3a5
/***************************************************************************
* *
* Copyright (C) 2021 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 *
***************************************************************************/
#ifndef TRANSCODETASK_H
#define TRANSCODETASK_H
#include "abstracttask.h"
class
QProcess
;
class
TranscodeTask
:
public
AbstractTask
{
public:
TranscodeTask
(
const
ObjectId
&
owner
,
QString
params
,
int
in
,
int
out
,
bool
replaceProducer
,
QObject
*
object
);
static
void
start
(
const
ObjectId
&
owner
,
QString
params
,
int
in
,
int
out
,
bool
replaceProducer
,
QObject
*
object
,
bool
force
=
false
);
protected:
void
run
()
override
;
private
slots
:
void
processLogInfo
();
private:
int
m_jobDuration
;
bool
m_isFfmpegJob
;
QString
m_transcodeParams
;
bool
m_replaceProducer
;
int
m_inPoint
;
int
m_outPoint
;
std
::
unique_ptr
<
QProcess
>
m_jobProcess
;
QString
m_errorMessage
;
QString
m_logDetails
;
};
#endif
src/mainwindow.cpp
View file @
00f0c3a5
...
...
@@ -41,7 +41,7 @@
#include "jobs/scenesplitjob.hpp"
#include "jobs/speedjob.hpp"
#include "jobs/stabilizejob.hpp"
#include "jobs/transcode
clipjob
.h"
#include "jobs/transcode
task
.h"
#include "jobs/audiolevelstask.h"
#include "kdenlivesettings.h"
#include "layoutmanagement.h"
...
...
@@ -2179,7 +2179,7 @@ void MainWindow::setRenderingFinished(const QString &url, int status, const QStr
}
}
void
MainWindow
::
addProjectClip
(
const
QString
&
url
)
void
MainWindow
::
addProjectClip
(
const
QString
&
url
,
const
QString
&
folder
)
{
if
(
pCore
->
currentDoc
())
{
QStringList
ids
=
pCore
->
projectItemModel
()
->
getClipByUrl
(
QFileInfo
(
url
));
...
...
@@ -2187,8 +2187,7 @@ void MainWindow::addProjectClip(const QString &url)
// Clip is already in project bin, abort
return
;
}
ClipCreator
::
createClipFromFile
(
url
,
pCore
->
projectItemModel
()
->
getRootFolder
()
->
clipId
(),
pCore
->
projectItemModel
());
ClipCreator
::
createClipFromFile
(
url
,
folder
,
pCore
->
projectItemModel
());
}
}
...
...
@@ -3597,7 +3596,12 @@ void MainWindow::buildDynamicActions()
}
connect
(
a
,
&
QAction
::
triggered
,
[
&
,
a
]()
{
QStringList
transcodeData
=
a
->
data
().
toStringList
();
emit
pCore
->
jobManager
()
->
startJob
<
TranscodeJob
>
(
pCore
->
bin
()
->
selectedClipsIds
(
true
),
-
1
,
QString
(),
transcodeData
.
first
(),
false
);
//emit pCore->jobManager()->startJob<TranscodeJob>(pCore->bin()->selectedClipsIds(true), -1, QString(), transcodeData.first(), false);
std
::
vector
<
QString
>
ids
=
pCore
->
bin
()
->
selectedClipsIds
(
true
);
for
(
QString
id
:
ids
)
{
std
::
shared_ptr
<
ProjectClip
>
clip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
id
);
TranscodeTask
::
start
({
ObjectType
::
BinClip
,
id
.
toInt
()},
transcodeData
.
first
(),
-
1
,
-
1
,
false
,
clip
.
get
());
}
});
if
(
transList
.
count
()
>
2
&&
transList
.
at
(
2
)
==
QLatin1String
(
"audio"
))
{
// This is an audio transcoding action
...
...
src/mainwindow.h
View file @
00f0c3a5
...
...
@@ -287,7 +287,7 @@ public slots:
void
slotReloadEffects
(
const
QStringList
&
paths
);
Q_SCRIPTABLE
void
setRenderingProgress
(
const
QString
&
url
,
int
progress
,
int
frame
);
Q_SCRIPTABLE
void
setRenderingFinished
(
const
QString
&
url
,
int
status
,
const
QString
&
error
);
Q_SCRIPTABLE
void
addProjectClip
(
const
QString
&
url
);
Q_SCRIPTABLE
void
addProjectClip
(
const
QString
&
url
,
const
QString
&
folder
=
QStringLiteral
(
"-1"
)
);
Q_SCRIPTABLE
void
addTimelineClip
(
const
QString
&
url
);
Q_SCRIPTABLE
void
addEffect
(
const
QString
&
effectId
);
Q_SCRIPTABLE
void
scriptRender
(
const
QString
&
url
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment