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
8db7e33c
Commit
8db7e33c
authored
May 14, 2021
by
Jean-Baptiste Mardelle
Browse files
Fix subclip thumbs, port stabilize and speed jobs to new task framework
parent
566d96bf
Changes
20
Hide whitespace changes
Inline
Side-by-side
src/bin/bin.cpp
View file @
8db7e33c
...
...
@@ -4056,7 +4056,7 @@ void Bin::reloadAllProducers(bool reloadThumbs)
clip
->
discardAudioThumb
();
// We need to set a temporary id before all outdated producers are replaced;
//int jobId = pCore->jobManager()->startJob<LoadJob>({clip->clipId()}, -1, QString(), xml);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
clip
->
clipId
().
toInt
()},
xml
,
false
,
this
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
clip
->
clipId
().
toInt
()},
xml
,
false
,
-
1
,
-
1
,
this
);
if
(
reloadThumbs
)
{
ThumbnailCache
::
get
()
->
invalidateThumbsForClip
(
clip
->
clipId
());
}
...
...
@@ -4510,3 +4510,37 @@ void Bin::requestTranscoding(const QString &url, const QString &id)
m_transcodingDialog
->
addUrl
(
url
,
id
);
m_transcodingDialog
->
show
();
}
bool
Bin
::
addProjectClipInFolder
(
const
QString
&
path
,
const
QString
&
parentFolder
,
const
QString
&
folderName
)
{
Fun
undo
=
[]()
{
return
true
;
};
Fun
redo
=
[]()
{
return
true
;
};
// Check if folder exists
QString
folderId
=
QStringLiteral
(
"-1"
);
bool
found
=
false
;
// We first try to see if it exists
std
::
shared_ptr
<
ProjectFolder
>
baseFolder
=
m_itemModel
->
getFolderByBinId
(
parentFolder
);
if
(
!
baseFolder
)
{
baseFolder
=
m_itemModel
->
getRootFolder
();
}
for
(
int
i
=
0
;
i
<
baseFolder
->
childCount
();
++
i
)
{
auto
currentItem
=
std
::
static_pointer_cast
<
AbstractProjectItem
>
(
baseFolder
->
child
(
i
));
if
(
currentItem
->
itemType
()
==
AbstractProjectItem
::
FolderItem
&&
currentItem
->
name
()
==
folderName
)
{
found
=
true
;
folderId
=
currentItem
->
clipId
();
break
;
}
}
if
(
!
found
)
{
// if it was not found, create folder
m_itemModel
->
requestAddFolder
(
folderId
,
folderName
,
parentFolder
,
undo
,
redo
);
}
auto
id
=
ClipCreator
::
createClipFromFile
(
path
,
folderId
,
m_itemModel
,
undo
,
redo
);
bool
ok
=
(
id
!=
QStringLiteral
(
"-1"
));
if
(
ok
)
{
pCore
->
pushUndo
(
undo
,
redo
,
i18nc
(
"@action"
,
"Add clip"
));
}
return
ok
;
}
src/bin/bin.h
View file @
8db7e33c
...
...
@@ -461,6 +461,8 @@ public slots:
*/
void
checkProjectAudioTracks
(
QString
clipId
,
int
minimumTracksCount
);
void
showTitleWidget
(
const
std
::
shared_ptr
<
ProjectClip
>
&
clip
);
/** @brief Add a clip in a specially named folder */
bool
addProjectClipInFolder
(
const
QString
&
path
,
const
QString
&
parentFolder
,
const
QString
&
folderName
);
protected:
/* This function is called whenever an item is selected to propagate signals
...
...
src/bin/projectclip.cpp
View file @
8db7e33c
...
...
@@ -123,7 +123,7 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, const std::share
connectEffectStack
();
if
(
m_clipStatus
==
FileStatus
::
StatusProxy
||
m_clipStatus
==
FileStatus
::
StatusReady
||
m_clipStatus
==
FileStatus
::
StatusProxyOnly
)
{
// Generate clip thumbnail
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
QDomElement
(),
true
,
this
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
QDomElement
(),
true
,
-
1
,
-
1
,
this
);
// Generate audio thumbnail
AudioLevelsTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
this
,
false
);
}
...
...
@@ -259,7 +259,7 @@ void ProjectClip::updateAudioThumbnail()
// Cache thumbnail
ThumbnailCache
::
get
()
->
storeThumbnail
(
m_binId
,
0
,
thumb
,
true
);
}
setThumbnail
(
thumb
);
setThumbnail
(
thumb
,
-
1
,
-
1
);
}
if
(
!
KdenliveSettings
::
audiothumbnails
())
{
return
;
...
...
@@ -381,7 +381,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
//pCore->jobManager()->discardJobs(clipId(), AbstractClipJob::THUMBJOB);
pCore
->
taskManager
.
discardJobs
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
AbstractTask
::
LOADJOB
);
m_thumbsProducer
.
reset
();
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
QDomElement
(),
true
,
this
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
QDomElement
(),
true
,
-
1
,
-
1
,
this
);
//emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadjobId, QString(), -1, true, true);
}
else
{
// If another load job is running?
...
...
@@ -413,7 +413,7 @@ void ProjectClip::reloadProducer(bool refreshOnly, bool isProxy, bool forceAudio
if
(
forceAudioReload
||
(
!
isProxy
&&
hashChanged
))
{
discardAudioThumb
();
}
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
xml
,
false
,
this
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
xml
,
false
,
-
1
,
-
1
,
this
);
//int loadJob = pCore->jobManager()->startJob<LoadJob>({clipId()}, loadjobId, QString(), xml);
//emit pCore->jobManager()->startJob<ThumbJob>({clipId()}, loadJob, QString(), -1, true, true);
}
...
...
@@ -435,11 +435,18 @@ QDomElement ProjectClip::toXml(QDomDocument &document, bool includeMeta, bool in
return
prod
;
}
void
ProjectClip
::
setThumbnail
(
const
QImage
&
img
)
void
ProjectClip
::
setThumbnail
(
const
QImage
&
img
,
int
in
,
int
out
)
{
if
(
img
.
isNull
())
{
return
;
}
if
(
in
>
-
1
)
{
std
::
shared_ptr
<
ProjectSubClip
>
sub
=
getSubClip
(
in
,
out
);
if
(
sub
)
{
sub
->
setThumbnail
(
img
);
}
return
;
}
QPixmap
thumb
=
roundedPixmap
(
QPixmap
::
fromImage
(
img
));
if
(
hasProxy
()
&&
!
thumb
.
isNull
())
{
// Overlay proxy icon
...
...
@@ -529,7 +536,7 @@ bool ProjectClip::setProducer(std::shared_ptr<Mlt::Producer> producer, bool repl
getFileHash
();
// set parent again (some info need to be stored in producer)
updateParent
(
parentItem
().
lock
());
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
QDomElement
(),
true
,
this
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
QDomElement
(),
true
,
-
1
,
-
1
,
this
);
AudioLevelsTask
::
start
({
ObjectType
::
BinClip
,
m_binId
.
toInt
()},
this
,
false
);
if
(
pCore
->
currentDoc
()
->
getDocumentProperty
(
QStringLiteral
(
"enableproxy"
)).
toInt
()
==
1
)
{
...
...
@@ -1781,7 +1788,7 @@ void ProjectClip::getThumbFromPercent(int percent)
int
framePos
=
duration
*
percent
/
100
;
framePos
-=
framePos
%
steps
;
if
(
ThumbnailCache
::
get
()
->
hasThumbnail
(
m_binId
,
framePos
))
{
setThumbnail
(
ThumbnailCache
::
get
()
->
getThumbnail
(
m_binId
,
framePos
));
setThumbnail
(
ThumbnailCache
::
get
()
->
getThumbnail
(
m_binId
,
framePos
)
,
-
1
,
-
1
);
}
else
{
// Generate percent thumbs
int
id
;
...
...
src/bin/projectclip.h
View file @
8db7e33c
...
...
@@ -277,7 +277,7 @@ public slots:
void
updateJobProgress
();
/** @brief Sets thumbnail for this clip. */
void
setThumbnail
(
const
QImage
&
);
void
setThumbnail
(
const
QImage
&
,
int
in
,
int
out
);
void
setThumbProducer
(
std
::
shared_ptr
<
Mlt
::
Producer
>
prod
);
/**
...
...
src/bin/projectitemmodel.cpp
View file @
8db7e33c
...
...
@@ -729,7 +729,7 @@ bool ProjectItemModel::requestAddBinClip(QString &id, const QDomElement &descrip
ProjectClip
::
construct
(
id
,
description
,
m_blankThumb
,
std
::
static_pointer_cast
<
ProjectItemModel
>
(
shared_from_this
()));
bool
res
=
addItem
(
new_clip
,
parentId
,
undo
,
redo
);
if
(
res
)
{
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
id
.
toInt
()},
description
,
false
,
this
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
id
.
toInt
()},
description
,
false
,
-
1
,
-
1
,
this
);
//int loadJob = emit pCore->jobManager()->startJob<LoadJob>({id}, -1, QString(), description, std::bind(readyCallBack, id));
int
loadJob
=
-
1
;
//emit pCore->jobManager()->startJob<ThumbJob>({id}, loadJob, QString(), 0, true);
...
...
src/bin/projectsubclip.cpp
View file @
8db7e33c
...
...
@@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "bincommands.h"
#include "jobs/jobmanager.h"
#include "jobs/cachejob.hpp"
#include "jobs/cliploadtask.h"
#include "utils/thumbnailcache.hpp"
#include <KLocalizedString>
...
...
@@ -59,8 +60,7 @@ ProjectSubClip::ProjectSubClip(const QString &id, const std::shared_ptr<ProjectC
m_tags
=
zoneProperties
.
value
(
QLatin1String
(
"tags"
));
qDebug
()
<<
"=== LOADING SUBCLIP WITH RATING: "
<<
m_rating
<<
", TAGS: "
<<
m_tags
;
m_clipStatus
=
FileStatus
::
StatusReady
;
// Save subclip in MLT
connect
(
parent
.
get
(),
&
ProjectClip
::
thumbReady
,
this
,
&
ProjectSubClip
::
gotThumb
);
ClipLoadTask
::
start
({
ObjectType
::
BinClip
,
m_parentClipId
.
toInt
()},
QDomElement
(),
true
,
in
,
out
,
this
);
}
std
::
shared_ptr
<
ProjectSubClip
>
ProjectSubClip
::
construct
(
const
QString
&
id
,
const
std
::
shared_ptr
<
ProjectClip
>
&
parent
,
...
...
src/effects/effectsrepository.cpp
View file @
8db7e33c
...
...
@@ -113,7 +113,6 @@ void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unor
}
}
QDomNodeList
effects
=
doc
.
elementsByTagName
(
QStringLiteral
(
"effect"
));
int
nbr_effect
=
effects
.
count
();
if
(
nbr_effect
==
0
)
{
qWarning
()
<<
"broken effect:"
<<
file_name
;
...
...
@@ -129,8 +128,10 @@ void EffectsRepository::parseCustomAssetFile(const QString &file_name, std::unor
Info
result
;
bool
ok
=
parseInfoFromXml
(
currentEffect
,
result
);
if
(
!
ok
)
{
qDebug
()
<<
"==== PARSING ABORTED FOR: "
<<
file_name
;
continue
;
}
if
(
customAssets
.
count
(
result
.
id
)
>
0
)
{
//qDebug() << "duplicate effect" << result.id;
}
...
...
src/jobs/CMakeLists.txt
View file @
8db7e33c
...
...
@@ -7,6 +7,8 @@ set(kdenlive_SRCS
jobs/audiolevelstask.cpp
jobs/cliploadtask.cpp
jobs/proxytask.cpp
jobs/stabilizetask.cpp
jobs/speedtask.cpp
jobs/transcodetask.cpp
jobs/filtertask.cpp
jobs/jobmanager.cpp
...
...
@@ -14,8 +16,6 @@ set(kdenlive_SRCS
jobs/loadjob.cpp
jobs/meltjob.cpp
jobs/scenesplitjob.cpp
jobs/speedjob.cpp
jobs/stabilizejob.cpp
jobs/thumbjob.cpp
jobs/transcodeclipjob.cpp
jobs/cutclipjob.cpp
...
...
src/jobs/cliploadtask.cpp
View file @
8db7e33c
...
...
@@ -47,9 +47,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KMessageWidget>
ClipLoadTask
::
ClipLoadTask
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
QObject
*
object
,
std
::
function
<
void
()
>
readyCallBack
)
ClipLoadTask
::
ClipLoadTask
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
int
in
,
int
out
,
QObject
*
object
,
std
::
function
<
void
()
>
readyCallBack
)
:
AbstractTask
(
owner
,
AbstractTask
::
LOADJOB
,
object
)
,
m_xml
(
xml
)
,
m_in
(
in
)
,
m_out
(
out
)
,
m_thumbOnly
(
thumbOnly
)
,
m_readyCallBack
(
std
::
move
(
readyCallBack
))
{
...
...
@@ -59,10 +61,10 @@ ClipLoadTask::~ClipLoadTask()
{
}
void
ClipLoadTask
::
start
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
QObject
*
object
,
bool
force
,
std
::
function
<
void
()
>
readyCallBack
)
void
ClipLoadTask
::
start
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
int
in
,
int
out
,
QObject
*
object
,
bool
force
,
std
::
function
<
void
()
>
readyCallBack
)
{
ClipLoadTask
*
task
=
new
ClipLoadTask
(
owner
,
xml
,
thumbOnly
,
object
,
readyCallBack
);
if
(
pCore
->
taskManager
.
hasPendingJob
(
owner
,
AbstractTask
::
LOADJOB
))
{
ClipLoadTask
*
task
=
new
ClipLoadTask
(
owner
,
xml
,
thumbOnly
,
in
,
out
,
object
,
readyCallBack
);
if
(
!
thumbOnly
&&
pCore
->
taskManager
.
hasPendingJob
(
owner
,
AbstractTask
::
LOADJOB
))
{
delete
task
;
task
=
0
;
}
...
...
@@ -234,13 +236,13 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip>binClip, std::s
{
// Fetch thumbnail
qDebug
()
<<
"=====
\n
READY FOR THUMB
\n\n
========="
;
int
frameNumber
=
qMax
(
0
,
binClip
->
getProducerIntProperty
(
QStringLiteral
(
"kdenlive:thumbnailFrame"
)));
int
frameNumber
=
m_in
>
-
1
?
m_in
:
qMax
(
0
,
binClip
->
getProducerIntProperty
(
QStringLiteral
(
"kdenlive:thumbnailFrame"
)));
if
(
binClip
->
clipType
()
!=
ClipType
::
Audio
)
{
if
(
ThumbnailCache
::
get
()
->
hasThumbnail
(
QString
::
number
(
m_owner
.
second
),
frameNumber
,
false
))
{
// Thumbnail found in cache
QImage
result
=
ThumbnailCache
::
get
()
->
getThumbnail
(
QString
::
number
(
m_owner
.
second
),
frameNumber
);
qDebug
()
<<
"=== FOUND THUMB IN CACHe"
;
QMetaObject
::
invokeMethod
(
binClip
.
get
(),
"setThumbnail"
,
Qt
::
QueuedConnection
,
Q_ARG
(
QImage
,
result
));
QMetaObject
::
invokeMethod
(
binClip
.
get
(),
"setThumbnail"
,
Qt
::
QueuedConnection
,
Q_ARG
(
QImage
,
result
)
,
Q_ARG
(
int
,
m_in
),
Q_ARG
(
int
,
m_out
)
);
}
else
{
QString
mltService
=
producer
->
get
(
"mlt_service"
);
const
QString
mltResource
=
producer
->
get
(
"resource"
);
...
...
@@ -282,9 +284,10 @@ void ClipLoadTask::generateThumbnail(std::shared_ptr<ProjectClip>binClip, std::s
QPainter
p
(
&
result
);
p
.
setPen
(
Qt
::
white
);
p
.
drawText
(
0
,
0
,
fullWidth
,
imageHeight
,
Qt
::
AlignCenter
,
i18n
(
"Invalid"
));
QMetaObject
::
invokeMethod
(
binClip
.
get
(),
"setThumbnail"
,
Qt
::
QueuedConnection
,
Q_ARG
(
QImage
,
result
));
QMetaObject
::
invokeMethod
(
binClip
.
get
(),
"setThumbnail"
,
Qt
::
QueuedConnection
,
Q_ARG
(
QImage
,
result
)
,
Q_ARG
(
int
,
m_in
),
Q_ARG
(
int
,
m_out
)
);
}
else
{
QMetaObject
::
invokeMethod
(
binClip
.
get
(),
"setThumbnail"
,
Qt
::
QueuedConnection
,
Q_ARG
(
QImage
,
result
));
qDebug
()
<<
"=== GOT THUMB FOR: "
<<
m_in
<<
"x"
<<
m_out
;
QMetaObject
::
invokeMethod
(
binClip
.
get
(),
"setThumbnail"
,
Qt
::
QueuedConnection
,
Q_ARG
(
QImage
,
result
),
Q_ARG
(
int
,
m_in
),
Q_ARG
(
int
,
m_out
));
ThumbnailCache
::
get
()
->
storeThumbnail
(
QString
::
number
(
m_owner
.
second
),
frameNumber
,
result
,
true
);
}
}
...
...
src/jobs/cliploadtask.h
View file @
8db7e33c
...
...
@@ -37,9 +37,9 @@ class ProjectClip;
class
ClipLoadTask
:
public
AbstractTask
{
public:
ClipLoadTask
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
QObject
*
object
,
std
::
function
<
void
()
>
readyCallBack
);
ClipLoadTask
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
int
in
,
int
out
,
QObject
*
object
,
std
::
function
<
void
()
>
readyCallBack
);
virtual
~
ClipLoadTask
();
static
void
start
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
QObject
*
object
,
bool
force
=
false
,
std
::
function
<
void
()
>
readyCallBack
=
[]()
{});
static
void
start
(
const
ObjectId
&
owner
,
const
QDomElement
&
xml
,
bool
thumbOnly
,
int
in
,
int
out
,
QObject
*
object
,
bool
force
=
false
,
std
::
function
<
void
()
>
readyCallBack
=
[]()
{});
static
ClipType
::
ProducerType
getTypeForService
(
const
QString
&
id
,
const
QString
&
path
);
std
::
shared_ptr
<
Mlt
::
Producer
>
loadResource
(
QString
resource
,
const
QString
&
type
);
std
::
shared_ptr
<
Mlt
::
Producer
>
loadPlaylist
(
QString
&
resource
);
...
...
@@ -54,6 +54,8 @@ protected:
private:
//QString cacheKey();
QDomElement
m_xml
;
int
m_in
;
int
m_out
;
bool
m_thumbOnly
;
std
::
function
<
void
()
>
m_readyCallBack
;
QString
m_errorMessage
;
...
...
src/jobs/filtertask.cpp
View file @
8db7e33c
...
...
@@ -60,16 +60,6 @@ void FilterTask::start(const ObjectId &owner, const QString &binId, std::weak_pt
}
}
void
FilterTask
::
updateProgress
(
int
prog
)
{
if
(
prog
!=
m_progress
)
{
m_progress
=
prog
;
if
(
auto
ptr
=
m_model
.
lock
())
{
QMetaObject
::
invokeMethod
(
ptr
.
get
(),
"setProgress"
,
Q_ARG
(
int
,
prog
));
}
}
}
void
FilterTask
::
run
()
{
if
(
m_isCanceled
)
{
...
...
src/jobs/filtertask.h
View file @
8db7e33c
...
...
@@ -42,7 +42,6 @@ class FilterTask : public AbstractTask
public:
FilterTask
(
const
ObjectId
&
owner
,
const
QString
&
binId
,
std
::
weak_ptr
<
AssetParameterModel
>
model
,
const
QString
&
assetId
,
int
in
,
int
out
,
QString
filterName
,
std
::
unordered_map
<
QString
,
QVariant
>
filterParams
,
std
::
unordered_map
<
QString
,
QString
>
filterData
,
const
QStringList
consumerArgs
,
QObject
*
object
);
static
void
start
(
const
ObjectId
&
owner
,
const
QString
&
binId
,
std
::
weak_ptr
<
AssetParameterModel
>
model
,
const
QString
&
assetId
,
int
in
,
int
out
,
QString
filterName
,
std
::
unordered_map
<
QString
,
QVariant
>
filterParams
,
std
::
unordered_map
<
QString
,
QString
>
filterData
,
const
QStringList
consumerArgs
,
QObject
*
object
,
bool
force
=
false
);
void
updateProgress
(
int
prog
);
int
length
;
private
slots
:
...
...
src/jobs/speedtask.cpp
0 → 100644
View file @
8db7e33c
/***************************************************************************
* *
* 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 *
***************************************************************************/
#include "speedtask.h"
#include "bin/bin.h"
#include "mainwindow.h"
#include "bin/projectclip.h"
#include "bin/projectfolder.h"
#include "project/clipstabilize.h"
#include "bin/projectitemmodel.h"
#include "profiles/profilemodel.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "core.h"
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
#include "macros.hpp"
#include "xml/xml.hpp"
#include <QThread>
#include <QProcess>
#include <KIO/RenameDialog>
#include <klocalizedstring.h>
#include <KLineEdit>
SpeedTask
::
SpeedTask
(
const
ObjectId
&
owner
,
const
QString
&
binId
,
const
QString
&
destination
,
int
in
,
int
out
,
std
::
unordered_map
<
QString
,
QVariant
>
filterParams
,
QObject
*
object
)
:
AbstractTask
(
owner
,
AbstractTask
::
SPEEDJOB
,
object
)
,
m_binId
(
binId
)
,
m_filterParams
(
filterParams
)
,
m_destination
(
destination
)
{
m_speed
=
filterParams
.
at
(
QStringLiteral
(
"warp_speed"
)).
toDouble
();
m_inPoint
=
qRound
(
in
/
m_speed
);
m_outPoint
=
qRound
(
out
/
m_speed
);
}
void
SpeedTask
::
start
(
QObject
*
object
,
bool
force
)
{
std
::
vector
<
QString
>
binIds
=
pCore
->
bin
()
->
selectedClipsIds
(
true
);
// Show config dialog
QDialog
d
(
qApp
->
activeWindow
());
d
.
setWindowTitle
(
i18n
(
"Clip Speed"
));
QDialogButtonBox
buttonBox
(
QDialogButtonBox
::
Cancel
|
QDialogButtonBox
::
Save
);
auto
*
l
=
new
QVBoxLayout
;
d
.
setLayout
(
l
);
QLabel
labUrl
(
&
d
);
KUrlRequester
fileUrl
(
&
d
);
auto
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
binIds
.
front
().
section
(
QLatin1Char
(
'/'
),
0
,
0
));
QDir
folder
=
QFileInfo
(
binClip
->
url
()).
absoluteDir
();
folder
.
mkpath
(
i18n
(
"Speed Change"
));
folder
.
cd
(
i18n
(
"Speed Change"
));
if
(
binIds
.
size
()
>
1
)
{
labUrl
.
setText
(
i18n
(
"Destination Folder"
));
fileUrl
.
setMode
(
KFile
::
Directory
);
fileUrl
.
setUrl
(
QUrl
::
fromLocalFile
(
folder
.
absolutePath
()));
}
else
{
labUrl
.
setText
(
i18n
(
"Destination File"
));
fileUrl
.
setMode
(
KFile
::
File
);
QString
filePath
=
QFileInfo
(
binClip
->
url
()).
fileName
().
section
(
QLatin1Char
(
'.'
),
0
,
-
2
);
filePath
.
append
(
QStringLiteral
(
".mlt"
));
fileUrl
.
setUrl
(
QUrl
::
fromLocalFile
(
folder
.
absoluteFilePath
(
filePath
)));
}
QFontMetrics
fm
=
fileUrl
.
lineEdit
()
->
fontMetrics
();
fileUrl
.
setMinimumWidth
(
int
(
fm
.
boundingRect
(
fileUrl
.
text
().
left
(
50
)).
width
()
*
1.4
));
QLabel
lab
(
&
d
);
lab
.
setText
(
i18n
(
"Percentage"
));
QDoubleSpinBox
speedInput
(
&
d
);
speedInput
.
setRange
(
-
100000
,
100000
);
speedInput
.
setValue
(
100
);
speedInput
.
setSuffix
(
QLatin1String
(
"%"
));
speedInput
.
setFocus
();
speedInput
.
selectAll
();
QCheckBox
cb
(
i18n
(
"Pitch compensation"
),
&
d
);
cb
.
setChecked
(
true
);
l
->
addWidget
(
&
labUrl
);
l
->
addWidget
(
&
fileUrl
);
l
->
addWidget
(
&
lab
);
l
->
addWidget
(
&
speedInput
);
l
->
addWidget
(
&
cb
);
l
->
addWidget
(
&
buttonBox
);
d
.
connect
(
&
buttonBox
,
&
QDialogButtonBox
::
rejected
,
&
d
,
&
QDialog
::
reject
);
d
.
connect
(
&
buttonBox
,
&
QDialogButtonBox
::
accepted
,
&
d
,
&
QDialog
::
accept
);
if
(
d
.
exec
()
!=
QDialog
::
Accepted
)
{
return
;
}
double
speed
=
speedInput
.
value
();
bool
warp_pitch
=
cb
.
isChecked
();
std
::
unordered_map
<
QString
,
QString
>
destinations
;
// keys are binIds, values are path to target files
std
::
unordered_map
<
QString
,
QVariant
>
filterParams
;
filterParams
[
QStringLiteral
(
"warp_speed"
)]
=
speed
/
100.0
;
if
(
warp_pitch
)
{
filterParams
[
QStringLiteral
(
"warp_pitch"
)]
=
1
;
}
for
(
const
auto
&
binId
:
binIds
)
{
QString
mltfile
;
if
(
binIds
.
size
()
==
1
)
{
// converting only 1 clip
mltfile
=
fileUrl
.
url
().
toLocalFile
();
}
else
{
QDir
dir
(
fileUrl
.
url
().
toLocalFile
());
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
binId
.
section
(
QLatin1Char
(
'/'
),
0
,
0
));
mltfile
=
QFileInfo
(
binClip
->
url
()).
fileName
().
section
(
QLatin1Char
(
'.'
),
0
,
-
2
);
mltfile
.
append
(
QString
(
"-%1.mlt"
).
arg
(
QString
::
number
(
int
(
speed
))));
mltfile
=
dir
.
absoluteFilePath
(
mltfile
);
}
// Filter several clips, destination points to a folder
if
(
QFile
::
exists
(
mltfile
))
{
KIO
::
RenameDialog
renameDialog
(
qApp
->
activeWindow
(),
i18n
(
"File already exists"
),
QUrl
::
fromLocalFile
(
mltfile
),
QUrl
::
fromLocalFile
(
mltfile
),
KIO
::
RenameDialog_Option
::
RenameDialog_Overwrite
);
if
(
renameDialog
.
exec
()
!=
QDialog
::
Rejected
)
{
QUrl
final
=
renameDialog
.
newDestUrl
();
if
(
final
.
isValid
())
{
mltfile
=
final
.
toLocalFile
();
}
}
else
{
return
;
}
}
destinations
[
binId
]
=
mltfile
;
}
for
(
auto
&
id
:
binIds
)
{
SpeedTask
*
task
=
nullptr
;
ObjectId
owner
;
if
(
id
.
contains
(
QLatin1Char
(
'/'
)))
{
QStringList
binData
=
id
.
split
(
QLatin1Char
(
'/'
));
if
(
binData
.
size
()
<
3
)
{
// Invalid subclip data
qDebug
()
<<
"=== INVALID SUBCLIP DATA: "
<<
id
;
continue
;
}
owner
=
ObjectId
(
ObjectType
::
BinClip
,
binData
.
first
().
toInt
());
auto
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
binData
.
first
());
if
(
binClip
)
{
task
=
new
SpeedTask
(
owner
,
binData
.
first
(),
destinations
.
at
(
id
),
binData
.
at
(
1
).
toInt
(),
binData
.
at
(
2
).
toInt
(),
filterParams
,
binClip
.
get
());
}
}
else
{
// Process full clip
owner
=
ObjectId
(
ObjectType
::
BinClip
,
id
.
toInt
());
auto
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
id
);
if
(
binClip
)
{
task
=
new
SpeedTask
(
owner
,
id
,
destinations
.
at
(
id
),
-
1
,
-
1
,
filterParams
,
binClip
.
get
());
}
}
if
(
task
)
{
// Otherwise, start a filter thread.
task
->
m_isForce
=
force
;
pCore
->
taskManager
.
startTask
(
owner
.
second
,
task
);
}
}
}
void
SpeedTask
::
run
()
{
if
(
m_isCanceled
)
{
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
return
;
}
m_running
=
true
;
qDebug
()
<<
" + + + + + + + + STARTING STAB TASK"
;
QString
url
;
auto
binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
m_binId
);
QStringList
producerArgs
=
{
QStringLiteral
(
"progress=1"
),
QStringLiteral
(
"-profile"
),
pCore
->
getCurrentProfilePath
()};
if
(
binClip
)
{
// Filter applied on a timeline or bin clip
url
=
binClip
->
url
();
if
(
url
.
isEmpty
())
{
m_errorMessage
.
append
(
i18n
(
"No producer for this clip."
));
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
return
;
}
producerArgs
<<
QString
(
"timewarp:%1:%2"
).
arg
(
m_speed
).
arg
(
url
);
if
(
m_inPoint
>
-
1
)
{
producerArgs
<<
QString
(
"in=%1"
).
arg
(
m_inPoint
);
}
if
(
m_outPoint
>
-
1
)
{
producerArgs
<<
QString
(
"out=%1"
).
arg
(
m_outPoint
);
}
}
else
{
// Filter applied on a track of master producer, leave config to source job
// We are on master or track, configure producer accordingly
// TODO
/*if (m_owner.first == ObjectType::Master) {
producer = pCore->getMasterProducerInstance();
} else if (m_owner.first == ObjectType::TimelineTrack) {
producer = pCore->getTrackProducerInstance(m_owner.second);
}
if ((producer == nullptr) || !producer->is_valid()) {
// Clip was removed or something went wrong, Notify user?
m_errorMessage.append(i18n("Invalid clip"));
pCore->taskManager.taskDone(m_owner.second, this);
return;
}*/
}
// Process filter params
for
(
const
auto
&
it
:
m_filterParams
)
{
qDebug
()
<<
". . ."
<<
it
.
first
<<
" = "
<<
it
.
second
;
if
(
it
.
second
.
type
()
==
QVariant
::
Double
)
{
producerArgs
<<
QString
(
"%1=%2"
).
arg
(
it
.
first
).
arg
(
it
.
second
.
toDouble
());
}
else
{
producerArgs
<<
QString
(
"%1=%2"
).
arg
(
it
.
first
).
arg
(
it
.
second
.
toString
());
}
}
// Start the MLT Process
QProcess
filterProcess
;
producerArgs
<<
QStringLiteral
(
"-consumer"
)
<<
QString
(
"xml:%1"
).
arg
(
m_destination
)
<<
QStringLiteral
(
"terminate_on_pause=1"
);
m_jobProcess
.
reset
(
new
QProcess
);
QMetaObject
::
invokeMethod
(
m_object
,
"updateJobProgress"
);
QObject
::
connect
(
this
,
&
AbstractTask
::
jobCanceled
,
m_jobProcess
.
get
(),
&
QProcess
::
kill
,
Qt
::
DirectConnection
);
QObject
::
connect
(
m_jobProcess
.
get
(),
&
QProcess
::
readyReadStandardError
,
this
,
&
SpeedTask
::
processLogInfo
);
qDebug
()
<<
"=== STARTING PROCESS: "
<<
producerArgs
;
m_jobProcess
->
start
(
KdenliveSettings
::
rendererpath
(),
producerArgs
);
m_jobProcess
->
waitForFinished
(
-
1
);
qDebug
()
<<
" + + + + + + + + SOURCE FILE PROCESSED: "
<<
m_jobProcess
->
exitStatus
();
bool
result
=
m_jobProcess
->
exitStatus
()
==
QProcess
::
NormalExit
;
m_progress
=
100
;
QMetaObject
::
invokeMethod
(
m_object
,
"updateJobProgress"
);
pCore
->
taskManager
.
taskDone
(
m_owner
.
second
,
this
);
if
(
m_isCanceled
||
!
result
)
{
return
;
}
QMetaObject
::
invokeMethod
(
pCore
->
bin
(),
"addProjectClipInFolder"
,
Qt
::
QueuedConnection
,
Q_ARG
(
const
QString
&
,
m_destination
),
Q_ARG
(
const
QString
&
,
binClip
->
parent
()
->
clipId
()),
Q_ARG
(
const
QString
&
,
i18n
(
"Speed Change"
)));
return
;
}
void
SpeedTask
::
processLogInfo
()
{
const
QString
buffer
=
QString
::
fromUtf8
(
m_jobProcess
->
readAllStandardError
());
m_logDetails
.
append
(
buffer
);
// Parse MLT output
if
(
buffer
.
contains
(
QLatin1String
(
"percentage:"
)))
{
int
progress
=
buffer
.
section
(
QStringLiteral
(
"percentage:"
),
1
).
simplified
().
section
(
QLatin1Char
(
' '
),
0
,
0
).
toInt
();
if
(
progress
==
m_progress
)
{
return
;
}
m_progress
=
progress
;
QMetaObject
::
invokeMethod
(
m_object
,
"updateJobProgress"
);
}
}
src/jobs/speedtask.h
0 → 100644
View file @
8db7e33c
/***************************************************************************
* *
* 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., *