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
8ddfc9ef
Commit
8ddfc9ef
authored
May 14, 2021
by
Jean-Baptiste Mardelle
Browse files
Delete remaining jobmanager stuff
parent
83003d02
Changes
34
Expand all
Hide whitespace changes
Inline
Side-by-side
src/bin/abstractprojectitem.cpp
View file @
8ddfc9ef
...
...
@@ -26,10 +26,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "macros.hpp"
#include "projectitemmodel.h"
#include "jobs/audiothumbjob.hpp"
#include "jobs/loadjob.hpp"
#include "jobs/thumbjob.hpp"
#include <QPainter>
#include <QPainterPath>
#include <QVariant>
...
...
src/bin/bin.cpp
View file @
8ddfc9ef
...
...
@@ -29,10 +29,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "doc/docundostack.hpp"
#include "doc/kdenlivedoc.h"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "jobs/audiothumbjob.hpp"
#include "jobs/transcodetask.h"
#include "jobs/taskmanager.h"
#include "jobs/thumbjob.hpp"
#include "jobs/cliploadtask.h"
#include "kdenlive_debug.h"
#include "kdenlivesettings.h"
...
...
@@ -2864,7 +2862,7 @@ void Bin::setupMenu()
m_proxyAction
=
new
QAction
(
i18n
(
"Proxy Clip"
),
pCore
->
window
());
pCore
->
window
()
->
addAction
(
QStringLiteral
(
"proxy_clip"
),
m_proxyAction
);
m_proxyAction
->
setData
(
QStringList
()
<<
QString
::
number
(
static_cast
<
int
>
(
Abstract
ClipJob
::
PROXYJOB
)));
m_proxyAction
->
setData
(
QStringList
()
<<
QString
::
number
(
static_cast
<
int
>
(
Abstract
Task
::
PROXYJOB
)));
m_proxyAction
->
setCheckable
(
true
);
m_proxyAction
->
setChecked
(
false
);
...
...
src/bin/projectclip.cpp
View file @
8ddfc9ef
...
...
@@ -49,7 +49,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "utils/thumbnailcache.hpp"
#include "xml/xml.hpp"
#include <QPainter>
#include <jobs/proxyclipjob.h>
#include <kimagecache.h>
#include "kdenlive_debug.h"
...
...
src/jobs/CMakeLists.txt
View file @
8ddfc9ef
set
(
kdenlive_SRCS
${
kdenlive_SRCS
}
jobs/abstractclipjob.cpp
jobs/abstracttask.cpp
jobs/taskmanager.cpp
jobs/audiolevelstask.cpp
...
...
src/jobs/abstractclipjob.cpp
deleted
100644 → 0
View file @
83003d02
/***************************************************************************
* *
* 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 "abstractclipjob.h"
#include "doc/kdenlivedoc.h"
#include "kdenlivesettings.h"
#include <utility>
AbstractClipJob
::
AbstractClipJob
(
JOBTYPE
type
,
QString
id
,
ObjectId
owner
,
QObject
*
parent
)
:
QObject
(
parent
)
,
m_clipId
(
std
::
move
(
id
))
,
m_jobType
(
type
)
,
m_inPoint
(
-
1
)
,
m_outPoint
(
-
1
)
,
m_owner
(
std
::
move
(
owner
))
{
if
(
m_clipId
.
count
(
QStringLiteral
(
"/"
))
==
2
)
{
m_inPoint
=
m_clipId
.
section
(
QLatin1Char
(
'/'
),
1
,
1
).
toInt
();
m_outPoint
=
m_clipId
.
section
(
QLatin1Char
(
'/'
),
2
).
toInt
();
m_clipId
=
m_clipId
.
section
(
QLatin1Char
(
'/'
),
0
,
0
);
}
}
AbstractClipJob
::~
AbstractClipJob
()
=
default
;
const
QString
AbstractClipJob
::
clipId
()
const
{
return
m_clipId
;
}
const
QString
AbstractClipJob
::
getErrorMessage
()
const
{
return
m_errorMessage
;
}
const
QString
AbstractClipJob
::
getLogDetails
()
const
{
return
m_logDetails
;
}
// static
bool
AbstractClipJob
::
execute
(
const
std
::
shared_ptr
<
AbstractClipJob
>
&
job
)
{
return
job
->
startJob
();
}
AbstractClipJob
::
JOBTYPE
AbstractClipJob
::
jobType
()
const
{
return
m_jobType
;
}
const
ObjectId
AbstractClipJob
::
owner
()
{
return
m_owner
;
}
src/jobs/abstractclipjob.h
deleted
100644 → 0
View file @
83003d02
/***************************************************************************
* *
* 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 *
***************************************************************************/
#ifndef ABSTRACTCLIPJOB
#define ABSTRACTCLIPJOB
#include <QObject>
#include "definitions.h"
#include "undohelper.hpp"
#include <memory>
struct
Job_t
;
/** @class AbstractClipJob
@brief This is the base class for all Kdenlive clip jobs.
*/
class
AbstractClipJob
:
public
QObject
{
Q_OBJECT
public:
enum
JOBTYPE
{
NOJOBTYPE
=
0
,
PROXYJOB
=
1
,
CUTJOB
=
2
,
STABILIZEJOB
=
3
,
TRANSCODEJOB
=
4
,
FILTERCLIPJOB
=
5
,
THUMBJOB
=
6
,
ANALYSECLIPJOB
=
7
,
LOADJOB
=
8
,
AUDIOTHUMBJOB
=
9
,
SPEEDJOB
=
10
,
CACHEJOB
=
11
};
AbstractClipJob
(
JOBTYPE
type
,
QString
id
,
ObjectId
owner
,
QObject
*
parent
=
nullptr
);
~
AbstractClipJob
()
override
;
template
<
typename
T
,
typename
...
Args
>
static
std
::
shared_ptr
<
T
>
make
(
const
QString
&
binId
,
Args
&&
...
args
)
{
auto
m
=
std
::
make_shared
<
T
>
(
binId
,
std
::
forward
<
Args
>
(
args
)...);
return
m
;
}
/** @brief Returns the id of the bin clip that this job is working on. */
const
QString
clipId
()
const
;
const
QString
getErrorMessage
()
const
;
const
QString
getLogDetails
()
const
;
virtual
const
QString
getDescription
()
const
=
0
;
virtual
bool
startJob
()
=
0
;
/** @brief This is to be called after the job finished.
By design, the job should store the result of the computation but not share it with the rest of the code. This happens when we call commitResult
This methods return true on success
*/
virtual
bool
commitResult
(
Fun
&
undo
,
Fun
&
redo
)
=
0
;
/** @brief run a given job */
static
bool
execute
(
const
std
::
shared_ptr
<
AbstractClipJob
>
&
job
);
/** @brief return the type of this job */
JOBTYPE
jobType
()
const
;
/** @brief return the owner of this job */
const
ObjectId
owner
();
protected:
QString
m_clipId
;
QString
m_errorMessage
;
QString
m_logDetails
;
int
m_addClipToProject
;
JOBTYPE
m_jobType
;
int
m_inPoint
;
int
m_outPoint
;
const
ObjectId
m_owner
;
bool
m_resultConsumed
{
false
};
signals:
/** @brief send an int between 0 and 100 to reflect computation progress */
void
jobProgress
(
int
);
void
jobCanceled
();
};
#endif
src/jobs/audiothumbjob.cpp
deleted
100644 → 0
View file @
83003d02
/***************************************************************************
* Copyright (C) 2017 by Nicolas Carion *
* This file is part of Kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) version 3 or any later version accepted by the *
* membership of KDE e.V. (or its successor approved by the membership *
* of KDE e.V.), which shall act as a proxy defined in Section 14 of *
* version 3 of the license. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "audiothumbjob.hpp"
#include "bin/projectclip.h"
#include "bin/projectitemmodel.h"
#include "core.h"
#include "doc/kdenlivedoc.h"
#include "doc/kthumb.h"
#include "kdenlivesettings.h"
#include "klocalizedstring.h"
#include "lib/audio/audioStreamInfo.h"
#include "macros.hpp"
#include "utils/thumbnailcache.hpp"
#include <QScopedPointer>
#include <QTemporaryFile>
#include <QProcess>
#include <memory>
#include <mlt++/MltProducer.h>
AudioThumbJob
::
AudioThumbJob
(
const
QString
&
binId
)
:
AbstractClipJob
(
AUDIOTHUMBJOB
,
binId
,
{
ObjectType
::
BinClip
,
binId
.
toInt
()})
,
m_ffmpegProcess
(
nullptr
)
{
}
const
QString
AudioThumbJob
::
getDescription
()
const
{
return
i18n
(
"Extracting audio thumb from clip %1"
,
m_clipId
);
}
bool
AudioThumbJob
::
computeWithMlt
()
{
m_audioLevels
.
clear
();
m_errorMessage
.
clear
();
// MLT audio thumbs: slower but safer
QString
service
=
m_prod
->
get
(
"mlt_service"
);
if
(
service
==
QLatin1String
(
"avformat-novalidate"
))
{
service
=
QStringLiteral
(
"avformat"
);
}
else
if
(
service
.
startsWith
(
QLatin1String
(
"xml"
)))
{
service
=
QStringLiteral
(
"xml-nogl"
);
}
QScopedPointer
<
Mlt
::
Producer
>
audioProducer
(
new
Mlt
::
Producer
(
*
m_prod
->
profile
(),
service
.
toUtf8
().
constData
(),
m_prod
->
get
(
"resource"
)));
if
(
!
audioProducer
->
is_valid
())
{
m_errorMessage
.
append
(
i18n
(
"Audio thumbs: cannot open file %1"
,
m_prod
->
get
(
"resource"
)));
return
false
;
}
audioProducer
->
set
(
"video_index"
,
"-1"
);
Mlt
::
Filter
chans
(
*
m_prod
->
profile
(),
"audiochannels"
);
Mlt
::
Filter
converter
(
*
m_prod
->
profile
(),
"audioconvert"
);
Mlt
::
Filter
levels
(
*
m_prod
->
profile
(),
"audiolevel"
);
audioProducer
->
attach
(
chans
);
audioProducer
->
attach
(
converter
);
audioProducer
->
attach
(
levels
);
int
last_val
=
0
;
double
framesPerSecond
=
audioProducer
->
get_fps
();
mlt_audio_format
audioFormat
=
mlt_audio_s16
;
QStringList
keys
;
keys
.
reserve
(
m_channels
);
for
(
int
i
=
0
;
i
<
m_channels
;
i
++
)
{
keys
<<
"meta.media.audio_level."
+
QString
::
number
(
i
);
}
double
maxLevel
=
1
;
QVector
<
double
>
mltLevels
;
for
(
int
z
=
0
;
z
<
m_lengthInFrames
;
++
z
)
{
int
val
=
int
(
100.0
*
z
/
m_lengthInFrames
);
if
(
last_val
!=
val
)
{
emit
jobProgress
(
val
);
last_val
=
val
;
}
QScopedPointer
<
Mlt
::
Frame
>
mltFrame
(
audioProducer
->
get_frame
());
if
((
mltFrame
!=
nullptr
)
&&
mltFrame
->
is_valid
()
&&
(
mltFrame
->
get_int
(
"test_audio"
)
==
0
))
{
int
samples
=
mlt_sample_calculator
(
float
(
framesPerSecond
),
m_frequency
,
z
);
mltFrame
->
get_audio
(
audioFormat
,
m_frequency
,
m_channels
,
samples
);
for
(
int
channel
=
0
;
channel
<
m_channels
;
++
channel
)
{
double
lev
=
mltFrame
->
get_double
(
keys
.
at
(
channel
).
toUtf8
().
constData
());
mltLevels
<<
lev
;
maxLevel
=
qMax
(
lev
,
maxLevel
);
}
}
else
if
(
!
mltLevels
.
isEmpty
())
{
for
(
int
channel
=
0
;
channel
<
m_channels
;
channel
++
)
{
mltLevels
<<
mltLevels
.
last
();
}
}
}
// Normalize
for
(
double
&
v
:
mltLevels
)
{
m_audioLevels
<<
uchar
(
255
*
v
/
maxLevel
);
}
m_done
=
true
;
return
true
;
}
bool
AudioThumbJob
::
computeWithFFMPEG
()
{
if
(
!
KdenliveSettings
::
audiothumbnails
())
{
// We only wanted the thumb generation
return
m_done
;
}
QString
filePath
=
m_binClip
->
getOriginalUrl
();
if
(
!
QFile
::
exists
(
filePath
))
{
return
false
;
}
int
audioMax
=
m_binClip
->
getProducerIntProperty
(
QStringLiteral
(
"kdenlive:audio_max"
));
if
(
audioMax
==
0
)
{
// Calculate max audio level with ffmpeg
QProcess
ffmpeg
;
QStringList
args
=
{
QStringLiteral
(
"-i"
),
filePath
,
QStringLiteral
(
"-vn"
),
QStringLiteral
(
"-af"
),
QStringLiteral
(
"volumedetect"
),
QStringLiteral
(
"-f"
),
QStringLiteral
(
"null"
)};
#ifdef Q_OS_WIN
args
<<
QStringLiteral
(
"-"
);
#else
args
<<
QStringLiteral
(
"/dev/stdout"
);
#endif
QObject
::
connect
(
&
ffmpeg
,
&
QProcess
::
readyReadStandardOutput
,
[
&
ffmpeg
,
this
]()
{
QString
output
=
ffmpeg
.
readAllStandardOutput
();
if
(
output
.
contains
(
QLatin1String
(
"max_volume"
)))
{
output
=
output
.
section
(
QLatin1String
(
"max_volume:"
),
1
).
simplified
();
output
=
output
.
section
(
QLatin1Char
(
' '
),
0
,
0
);
bool
ok
;
double
maxVolume
=
output
.
toDouble
(
&
ok
);
if
(
ok
)
{
int
aMax
=
qMax
(
1
,
qAbs
(
qRound
(
maxVolume
)));
m_binClip
->
setProducerProperty
(
QStringLiteral
(
"kdenlive:audio_max"
),
aMax
);
QMetaObject
::
invokeMethod
(
pCore
.
get
(),
"setDocumentModified"
,
Qt
::
QueuedConnection
);
}
else
{
m_binClip
->
setProducerProperty
(
QStringLiteral
(
"kdenlive:audio_max"
),
-
1
);
}
}
});
ffmpeg
.
setProcessChannelMode
(
QProcess
::
MergedChannels
);
ffmpeg
.
start
(
KdenliveSettings
::
ffmpegpath
(),
args
);
ffmpeg
.
waitForFinished
(
-
1
);
}
int
audioStreamIndex
=
m_binClip
->
getAudioStreamFfmpegIndex
(
m_audioStream
);
if
(
!
QFile
::
exists
(
m_cachePath
)
&&
!
m_dataInCache
)
{
// Generate timeline audio thumbnail data
m_audioLevels
.
clear
();
std
::
vector
<
std
::
unique_ptr
<
QTemporaryFile
>>
channelFiles
;
for
(
int
i
=
0
;
i
<
m_channels
;
i
++
)
{
std
::
unique_ptr
<
QTemporaryFile
>
channelTmpfile
(
new
QTemporaryFile
());
if
(
!
channelTmpfile
->
open
())
{
m_errorMessage
.
append
(
i18n
(
"Audio thumbs: cannot create temporary file, check disk space and permissions
\n
"
));
return
false
;
}
channelTmpfile
->
close
();
channelFiles
.
emplace_back
(
std
::
move
(
channelTmpfile
));
}
// Always create audio thumbs from the original source file, because proxy
// can have a different audio config (channels / mono/ stereo)
QStringList
args
{
QStringLiteral
(
"-hide_banner"
),
QStringLiteral
(
"-i"
),
QUrl
::
fromLocalFile
(
filePath
).
toLocalFile
(),
QStringLiteral
(
"-progress"
)};
#ifdef Q_OS_WIN
args
<<
QStringLiteral
(
"-"
);
#else
args
<<
QStringLiteral
(
"/dev/stdout"
);
#endif
bool
isFFmpeg
=
KdenliveSettings
::
ffmpegpath
().
contains
(
QLatin1String
(
"ffmpeg"
));
args
<<
QStringLiteral
(
"-filter_complex"
);
if
(
m_channels
==
1
)
{
if
(
m_audioStream
>=
0
)
{
args
<<
QStringLiteral
(
"[a:%1]aformat=channel_layouts=mono,%2=100"
).
arg
(
audioStreamIndex
).
arg
(
isFFmpeg
?
"aresample=1500:async"
:
"sample_rates"
);
}
else
{
args
<<
QStringLiteral
(
"[a]aformat=channel_layouts=mono,%1=100"
).
arg
(
isFFmpeg
?
"aresample=1500:async"
:
"sample_rates"
);
}
/*args << QStringLiteral("-map") << QStringLiteral("0:a%1").arg(m_audioStream > 0 ? ":" + QString::number(audioStreamIndex) : QString())*/
args
<<
QStringLiteral
(
"-c:a"
)
<<
QStringLiteral
(
"pcm_s16le"
)
<<
QStringLiteral
(
"-frames:v"
)
<<
QStringLiteral
(
"1"
)
<<
QStringLiteral
(
"-y"
)
<<
QStringLiteral
(
"-f"
)
<<
QStringLiteral
(
"data"
)
<<
channelFiles
[
0
]
->
fileName
();
}
else
{
QString
aformat
=
QStringLiteral
(
"[a%1]%2=100,channelsplit=channel_layout=%3"
)
.
arg
(
audioStreamIndex
>=
0
?
":"
+
QString
::
number
(
audioStreamIndex
)
:
QString
(),
isFFmpeg
?
"aresample=1500:async"
:
"aformat=sample_rates"
,
m_channels
>
2
?
"5.1"
:
"stereo"
);
for
(
int
i
=
0
;
i
<
m_channels
;
++
i
)
{
aformat
.
append
(
QStringLiteral
(
"[0:%1]"
).
arg
(
i
));
}
args
<<
aformat
;
args
<<
QStringLiteral
(
"-frames:v"
)
<<
QStringLiteral
(
"1"
);
for
(
int
i
=
0
;
i
<
m_channels
;
i
++
)
{
// Channel 1
args
<<
QStringLiteral
(
"-map"
)
<<
QStringLiteral
(
"[0:%1]"
).
arg
(
i
)
<<
QStringLiteral
(
"-c:a"
)
<<
QStringLiteral
(
"pcm_s16le"
)
<<
QStringLiteral
(
"-y"
)
<<
QStringLiteral
(
"-f"
)
<<
QStringLiteral
(
"data"
)
<<
channelFiles
[
size_t
(
i
)]
->
fileName
();
}
}
m_ffmpegProcess
=
std
::
make_unique
<
QProcess
>
();
connect
(
m_ffmpegProcess
.
get
(),
&
QProcess
::
readyReadStandardOutput
,
this
,
&
AudioThumbJob
::
updateFfmpegProgress
,
Qt
::
UniqueConnection
);
connect
(
this
,
&
AudioThumbJob
::
jobCanceled
,
[
&
]()
{
if
(
m_ffmpegProcess
)
{
disconnect
(
m_ffmpegProcess
.
get
(),
&
QProcess
::
readyReadStandardOutput
,
this
,
&
AudioThumbJob
::
updateFfmpegProgress
);
m_ffmpegProcess
->
kill
();
}
m_audioLevels
.
clear
();
m_done
=
true
;
m_successful
=
false
;
});
m_ffmpegProcess
->
start
(
KdenliveSettings
::
ffmpegpath
(),
args
);
m_ffmpegProcess
->
waitForFinished
(
-
1
);
disconnect
(
m_ffmpegProcess
.
get
(),
&
QProcess
::
readyReadStandardOutput
,
this
,
&
AudioThumbJob
::
updateFfmpegProgress
);
if
(
m_successful
&&
m_ffmpegProcess
->
exitStatus
()
!=
QProcess
::
CrashExit
)
{
int
dataSize
=
0
;
std
::
vector
<
const
qint16
*>
rawChannels
;
std
::
vector
<
QByteArray
>
sourceChannels
;
for
(
auto
&
channelFile
:
channelFiles
)
{
channelFile
->
open
();
sourceChannels
.
emplace_back
(
channelFile
->
readAll
());
QByteArray
&
res
=
sourceChannels
.
back
();
channelFile
->
close
();
if
(
dataSize
==
0
)
{
dataSize
=
res
.
size
();
}
if
(
res
.
isEmpty
()
||
res
.
size
()
!=
dataSize
)
{
// Something went wrong, abort
m_errorMessage
.
append
(
i18n
(
"Audio thumbs: error reading audio thumbnail created with FFmpeg
\n
"
));
return
false
;
}
rawChannels
.
emplace_back
(
reinterpret_cast
<
const
qint16
*>
(
res
.
constData
()));
}
int
progress
=
0
;
std
::
vector
<
long
>
channelsData
;
double
offset
=
double
(
dataSize
)
/
(
2.0
*
m_lengthInFrames
);
int
intraOffset
=
1
;
if
(
offset
>
1000
)
{
intraOffset
=
int
(
offset
/
60
);
}
else
if
(
offset
>
250
)
{
intraOffset
=
int
(
offset
/
10
);
}
long
maxAudioLevel
=
1
;
if
(
!
m_successful
)
{
m_done
=
true
;
return
true
;
}
std
::
vector
<
long
>
ffmpegLevels
;
for
(
int
i
=
0
;
i
<
m_lengthInFrames
;
i
++
)
{
channelsData
.
resize
(
size_t
(
rawChannels
.
size
()));
std
::
fill
(
channelsData
.
begin
(),
channelsData
.
end
(),
0
);
int
pos
=
int
(
i
*
offset
);
int
steps
=
0
;
for
(
int
j
=
0
;
j
<
int
(
offset
)
&&
(
pos
+
j
<
dataSize
);
j
+=
intraOffset
)
{
steps
++
;
for
(
size_t
k
=
0
;
k
<
rawChannels
.
size
();
k
++
)
{
channelsData
[
k
]
+=
abs
(
rawChannels
[
k
][
pos
+
j
]);
}
}
steps
=
qMax
(
steps
,
1
);
for
(
long
&
k
:
channelsData
)
{
if
(
!
m_successful
)
{
break
;
}
k
/=
steps
;
maxAudioLevel
=
qMax
(
k
,
maxAudioLevel
);
}
int
p
=
80
+
(
i
*
20
/
m_lengthInFrames
);
if
(
p
!=
progress
)
{
emit
jobProgress
(
p
);
progress
=
p
;
}
ffmpegLevels
.
insert
(
ffmpegLevels
.
end
(),
channelsData
.
begin
(),
channelsData
.
end
());
}
if
(
!
m_successful
)
{
m_done
=
true
;
return
true
;
}
for
(
long
&
v
:
ffmpegLevels
)
{
m_audioLevels
<<
uint8_t
(
255
*
v
/
maxAudioLevel
);
}
m_done
=
true
;
return
true
;
}
else
if
(
m_ffmpegProcess
)
{
QString
err
=
m_ffmpegProcess
->
readAllStandardError
();
// m_errorMessage += err;
// m_errorMessage.append(i18n("Failed to create FFmpeg audio thumbnails, we now try to use MLT"));
qWarning
()
<<
"Failed to create FFmpeg audio thumbs:
\n
"
<<
err
<<
"
\n
---------------------"
;
}
}
else
{
m_done
=
true
;
}
return
m_done
;
}
void
AudioThumbJob
::
updateFfmpegProgress
()
{
if
(
m_ffmpegProcess
==
nullptr
)
{
return
;
}
const
QString
result
=
m_ffmpegProcess
->
readAllStandardOutput
();
const
QStringList
lines
=
result
.
split
(
QLatin1Char
(
'\n'
));
for
(
const
QString
&
data
:
lines
)
{
if
(
data
.
startsWith
(
QStringLiteral
(
"out_time_ms"
)))
{
double
ms
=
data
.
section
(
QLatin1Char
(
'='
),
1
).
toDouble
();
emit
jobProgress
(
int
(
ms
/
m_binClip
->
duration
().
ms
()
/
10
));
}
else
{
m_logDetails
+=
data
+
QStringLiteral
(
"
\n
"
);
}
}
}
bool
AudioThumbJob
::
startJob
()
{
if
(
m_done
)
{
return
true
;
}
m_dataInCache
=
false
;
m_binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
m_clipId
);
if
(
m_binClip
==
nullptr
)
{
// Clip was deleted
return
false
;
}
m_successful
=
true
;
if
(
m_binClip
->
audioChannels
()
==
0
||
m_binClip
->
audioThumbCreated
())
{
// nothing to do
m_done
=
true
;
return
true
;
}
m_prod
=
m_binClip
->
originalProducer
();
if
((
m_prod
==
nullptr
)
||
!
m_prod
->
is_valid
())
{
m_errorMessage
.
append
(
i18n
(
"Audio thumbs: cannot open project file %1"
,
m_binClip
->
url
()));
m_done
=
true
;
m_successful
=
false
;
return
false
;
}
m_lengthInFrames
=
m_prod
->
get_length
();
// Multiply this if we want more than 1 sample per frame
if
(
m_lengthInFrames
==
INT_MAX
)
{
// This is a broken file or live feed, don't attempt to generate audio thumbnails
m_done
=
true
;
m_successful
=
false
;
return
false
;
}
m_frequency
=
m_binClip
->
audioInfo
()
->
samplingRate
();
m_frequency
=
m_frequency
<=
0
?
48000
:
m_frequency
;
m_channels
=
m_binClip
->
audioInfo
()
->
channels
();
m_channels
=
m_channels
<=
0
?
2
:
m_channels
;
QMap
<
int
,
QString
>
streams
=
m_binClip
->
audioInfo
()
->
streams
();
QMap
<
int
,
int
>
audioChannels
=
m_binClip
->
audioInfo
()
->
streamChannels
();
QMapIterator
<
int
,
QString
>
st
(
streams
);
m_done
=
true
;
ClipType
::
ProducerType
type
=
m_binClip
->
clipType
();
while
(
st
.
hasNext
())
{
st
.
next
();
int
stream
=
st
.
key
();
if
(
audioChannels
.
contains
(
stream
))
{
m_channels
=
audioChannels
.
value
(
stream
);
}
// Generate one thumb per stream
m_audioStream
=
stream
;
m_cachePath
=
m_binClip
->
getAudioThumbPath
(
stream
);
if
(
QFile
::
exists
(
m_cachePath
))
{
// Audio thumb already exists
continue
;
}