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
1a7d2eee
Commit
1a7d2eee
authored
Nov 08, 2019
by
Jean-Baptiste Mardelle
Browse files
Improve audio clips display: proper thumbnails and monitor view
parent
3fb2ba13
Pipeline
#10102
passed with stage
in 26 minutes and 57 seconds
Changes
23
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/bin/bin.cpp
View file @
1a7d2eee
...
...
@@ -71,6 +71,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QUrl>
#include <QVBoxLayout>
#include <utility>
/**
* @class BinItemDelegate
* @brief This class is responsible for drawing items in the QTreeView.
...
...
@@ -699,7 +700,6 @@ Bin::Bin(std::shared_ptr<ProjectItemModel> model, QWidget *parent)
setFocusPolicy
(
Qt
::
ClickFocus
);
connect
(
m_itemModel
.
get
(),
&
ProjectItemModel
::
refreshPanel
,
this
,
&
Bin
::
refreshPanel
);
connect
(
m_itemModel
.
get
(),
&
ProjectItemModel
::
refreshAudioThumbs
,
this
,
&
Bin
::
doRefreshAudioThumbs
);
connect
(
m_itemModel
.
get
(),
&
ProjectItemModel
::
refreshClip
,
this
,
&
Bin
::
refreshClip
);
connect
(
m_itemModel
.
get
(),
&
ProjectItemModel
::
emitMessage
,
this
,
&
Bin
::
emitMessage
);
...
...
@@ -2197,13 +2197,6 @@ void Bin::refreshClip(const QString &id)
}
}
void
Bin
::
doRefreshAudioThumbs
(
const
QString
&
id
)
{
if
(
m_monitor
->
activeClipId
()
==
id
)
{
slotSendAudioThumb
(
id
);
}
}
void
Bin
::
slotCreateProjectClip
()
{
auto
*
act
=
qobject_cast
<
QAction
*>
(
sender
());
...
...
@@ -3085,16 +3078,6 @@ QStringList Bin::getProxyHashList()
return
list
;
}
void
Bin
::
slotSendAudioThumb
(
const
QString
&
id
)
{
std
::
shared_ptr
<
ProjectClip
>
clip
=
m_itemModel
->
getClipByBinID
(
id
);
if
((
clip
!=
nullptr
)
&&
clip
->
audioThumbCreated
())
{
m_monitor
->
prepareAudioThumb
(
clip
->
audioChannels
(),
clip
->
audioFrameCache
);
}
else
{
m_monitor
->
prepareAudioThumb
(
0
);
}
}
bool
Bin
::
isEmpty
()
const
{
if
(
m_itemModel
->
getRootFolder
()
==
nullptr
)
{
...
...
src/bin/bin.h
View file @
1a7d2eee
...
...
@@ -304,9 +304,6 @@ private slots:
/** @brief Rename a Bin Item. */
void
slotRenameItem
();
void
doRefreshPanel
(
const
QString
&
id
);
/** @brief Send audio thumb data to monitor for display. */
void
slotSendAudioThumb
(
const
QString
&
id
);
void
doRefreshAudioThumbs
(
const
QString
&
id
);
/** @brief Enable item view and hide message */
void
slotMessageActionTriggered
();
/** @brief Request editing of title or slideshow clip */
...
...
src/bin/projectclip.cpp
View file @
1a7d2eee
...
...
@@ -197,9 +197,6 @@ void ProjectClip::updateAudioThumbnail(QList<double> audioLevels)
{
audioFrameCache
=
audioLevels
;
m_audioThumbCreated
=
true
;
if
(
auto
ptr
=
m_model
.
lock
())
{
emit
std
::
static_pointer_cast
<
ProjectItemModel
>
(
ptr
)
->
refreshAudioThumbs
(
m_binId
);
}
}
bool
ProjectClip
::
audioThumbCreated
()
const
...
...
@@ -1195,7 +1192,7 @@ void ProjectClip::discardAudioThumb()
pCore
->
jobManager
()
->
discardJobs
(
clipId
(),
AbstractClipJob
::
AUDIOTHUMBJOB
);
}
const
QString
ProjectClip
::
getAudioThumbPath
()
const
QString
ProjectClip
::
getAudioThumbPath
(
bool
miniThumb
)
{
if
(
audioInfo
()
==
nullptr
)
{
return
QString
();
...
...
@@ -1211,6 +1208,10 @@ const QString ProjectClip::getAudioThumbPath()
return
QString
();
}
QString
audioPath
=
thumbFolder
.
absoluteFilePath
(
clipHash
);
if
(
miniThumb
)
{
audioPath
.
append
(
QStringLiteral
(
".png"
));
return
audioPath
;
}
if
(
audioStream
>
0
)
{
audioPath
.
append
(
QLatin1Char
(
'_'
)
+
QString
::
number
(
audioInfo
()
->
audio_index
()));
}
...
...
src/bin/projectclip.h
View file @
1a7d2eee
...
...
@@ -185,7 +185,7 @@ public:
/** @brief Delete cached audio thumb - needs to be recreated */
void
discardAudioThumb
();
/** @brief Get path for this clip's audio thumbnail */
const
QString
getAudioThumbPath
();
const
QString
getAudioThumbPath
(
bool
miniThumb
=
false
);
/** @brief Returns true if this producer has audio and can be splitted on timeline*/
bool
isSplittable
()
const
;
...
...
@@ -283,6 +283,7 @@ signals:
void
thumbReady
(
int
,
const
QImage
&
);
/** @brief Clip is ready, load properties. */
void
loadPropertiesPanel
();
void
audioThumbReady
();
};
#endif
src/jobs/audiothumbjob.cpp
View file @
1a7d2eee
...
...
@@ -106,117 +106,135 @@ bool AudioThumbJob::computeWithMlt()
bool
AudioThumbJob
::
computeWithFFMPEG
()
{
m_audioLevels
.
clear
();
QStringList
args
;
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)
QString
filePath
=
m_prod
->
get
(
"kdenlive:originalurl"
);
if
(
filePath
.
isEmpty
())
{
filePath
=
m_prod
->
get
(
"resource"
);
}
args
<<
QStringLiteral
(
"-i"
)
<<
QUrl
::
fromLocalFile
(
filePath
).
toLocalFile
();
// Output progress info
args
<<
QStringLiteral
(
"-progress"
);
m_ffmpegProcess
.
reset
(
new
QProcess
);
if
(
!
m_thumbInCache
)
{
QStringList
args
;
args
<<
QStringLiteral
(
"-hide_banner"
)
<<
QStringLiteral
(
"-y"
)
<<
QStringLiteral
(
"-i"
)
<<
QUrl
::
fromLocalFile
(
filePath
).
toLocalFile
()
<<
QStringLiteral
(
"-filter_complex:a"
);
args
<<
QString
(
"showwavespic=s=%1x%2:split_channels=1:scale=cbrt:colors=0xffdddd|0xddffdd"
).
arg
(
m_thumbSize
.
width
()).
arg
(
m_thumbSize
.
height
());
args
<<
QStringLiteral
(
"-frames:v"
)
<<
QStringLiteral
(
"1"
);
args
<<
m_binClip
->
getAudioThumbPath
(
true
);
connect
(
m_ffmpegProcess
.
get
(),
&
QProcess
::
readyReadStandardOutput
,
this
,
&
AudioThumbJob
::
updateFfmpegProgress
);
m_ffmpegProcess
->
start
(
KdenliveSettings
::
ffmpegpath
(),
args
);
m_ffmpegProcess
->
waitForFinished
(
-
1
);
if
(
m_ffmpegProcess
->
exitStatus
()
!=
QProcess
::
CrashExit
)
{
m_thumbInCache
=
true
;
if
(
m_dataInCache
)
{
m_done
=
true
;
return
true
;
}
else
{
// Next Processinĝ step can be long, already display audio thumb in monitor
m_binClip
->
audioThumbReady
();
}
}
}
if
(
!
m_dataInCache
)
{
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
(
"-"
);
args
<<
QStringLiteral
(
"-"
);
#else
args
<<
QStringLiteral
(
"/dev/stdout"
);
args
<<
QStringLiteral
(
"/dev/stdout"
);
#endif
bool
isFFmpeg
=
KdenliveSettings
::
ffmpegpath
().
contains
(
QLatin1String
(
"ffmpeg"
));
args
<<
QStringLiteral
(
"-filter_complex:a"
);
if
(
m_channels
==
1
)
{
//TODO: this does not correcty generate the correct stream data
args
<<
QStringLiteral
(
"aformat=channel_layouts=mono,%1=100"
).
arg
(
isFFmpeg
?
"aresample=async"
:
"sample_rates"
);
args
<<
QStringLiteral
(
"-map"
)
<<
QStringLiteral
(
"0:a%1"
).
arg
(
m_audioStream
>
0
?
":"
+
QString
::
number
(
m_audioStream
)
:
QString
())
<<
QStringLiteral
(
"-c:a"
)
<<
QStringLiteral
(
"pcm_s16le"
)
<<
QStringLiteral
(
"-y"
)
<<
QStringLiteral
(
"-f"
)
<<
QStringLiteral
(
"data"
)
bool
isFFmpeg
=
KdenliveSettings
::
ffmpegpath
().
contains
(
QLatin1String
(
"ffmpeg"
));
args
<<
QStringLiteral
(
"-filter_complex:a"
);
if
(
m_channels
==
1
)
{
//TODO: this does not correcty generate the correct stream data
args
<<
QStringLiteral
(
"aformat=channel_layouts=mono,%1=100"
).
arg
(
isFFmpeg
?
"aresample=async"
:
"sample_rates"
);
args
<<
QStringLiteral
(
"-map"
)
<<
QStringLiteral
(
"0:a%1"
).
arg
(
m_audioStream
>
0
?
":"
+
QString
::
number
(
m_audioStream
)
:
QString
())
<<
QStringLiteral
(
"-c:a"
)
<<
QStringLiteral
(
"pcm_s16le"
)
<<
QStringLiteral
(
"-frames:v"
)
<<
QStringLiteral
(
"1"
)
<<
QStringLiteral
(
"-y"
)
<<
QStringLiteral
(
"-f"
)
<<
QStringLiteral
(
"data"
)
<<
channelFiles
[
0
]
->
fileName
();
}
else
{
QString
aformat
=
QStringLiteral
(
"[0:a%1]%2=100,channelsplit=channel_layout=%3"
)
}
else
{
QString
aformat
=
QStringLiteral
(
"[0:a%1]%2=100,channelsplit=channel_layout=%3"
)
.
arg
(
m_audioStream
>
0
?
":"
+
QString
::
number
(
m_audioStream
)
:
QString
())
.
arg
(
isFFmpeg
?
"aresample=async"
:
"aformat=sample_rates="
)
.
arg
(
m_channels
>
2
?
"5.1"
:
"stereo"
);
for
(
int
i
=
0
;
i
<
m_channels
;
++
i
)
{
aformat
.
append
(
QStringLiteral
(
"[0:%1]"
).
arg
(
i
));
}
args
<<
aformat
;
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"
)
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
=
new
QProcess
;
m_ffmpegProcess
->
start
(
KdenliveSettings
::
ffmpegpath
(),
args
);
connect
(
m_ffmpegProcess
,
&
QProcess
::
readyReadStandardOutput
,
this
,
&
AudioThumbJob
::
updateFfmpegProgress
);
m_ffmpegProcess
->
waitForFinished
(
-
1
);
if
(
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
();
m_ffmpegProcess
->
start
(
KdenliveSettings
::
ffmpegpath
(),
args
);
m_ffmpegProcess
->
waitForFinished
(
-
1
);
if
(
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
((
const
qint16
*
)
res
.
constData
());
}
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
;
int
progress
=
0
;
std
::
vector
<
long
>
channelsData
;
double
offset
=
(
double
)
dataSize
/
(
2.0
*
m_lengthInFrames
);
int
intraOffset
=
1
;
if
(
offset
>
1000
)
{
intraOffset
=
offset
/
60
;
}
else
if
(
offset
>
250
)
{
intraOffset
=
offset
/
10
;
}
rawChannels
.
emplace_back
((
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
=
offset
/
60
;
}
else
if
(
offset
>
250
)
{
intraOffset
=
offset
/
10
;
}
double
factor
=
800.0
/
32768
;
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
++
)
{
double
factor
=
800.0
/
32768
;
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
]);
}
}
}
for
(
long
&
k
:
channelsData
)
{
if
(
steps
!=
0
)
{
k
/=
steps
;
for
(
long
&
k
:
channelsData
)
{
if
(
steps
!=
0
)
{
k
/=
steps
;
}
m_audioLevels
<<
(
int
)((
double
)
k
*
factor
);
}
int
p
=
80
+
(
i
*
20
/
m_lengthInFrames
);
if
(
p
!=
progress
)
{
emit
jobProgress
(
p
);
progress
=
p
;
}
m_audioLevels
<<
(
int
)((
double
)
k
*
factor
);
}
int
p
=
80
+
(
i
*
20
/
m_lengthInFrames
);
if
(
p
!=
progress
)
{
emit
jobProgress
(
p
);
progress
=
p
;
}
m_done
=
true
;
return
true
;
}
m_done
=
true
;
return
true
;
}
QString
err
=
m_ffmpegProcess
->
readAllStandardError
();
delete
m_ffmpegProcess
;
// 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
---------------------"
;
...
...
@@ -242,6 +260,9 @@ bool AudioThumbJob::startJob()
if
(
m_done
)
{
return
true
;
}
m_dataInCache
=
false
;
m_thumbInCache
=
false
;
m_thumbSize
=
QSize
(
1000
,
1000
/
pCore
->
getCurrentDar
());
m_binClip
=
pCore
->
projectItemModel
()
->
getClipByBinID
(
m_clipId
);
if
(
m_binClip
->
audioChannels
()
==
0
||
m_binClip
->
audioThumbCreated
())
{
// nothing to do
...
...
@@ -281,15 +302,24 @@ bool AudioThumbJob::startJob()
}
}
if
(
!
m_audioLevels
.
isEmpty
())
{
m_dataInCache
=
true
;
}
// Check audio thumbnail image
if
(
ThumbnailCache
::
get
()
->
hasThumbnail
(
m_clipId
,
-
1
,
false
))
{
m_thumbInCache
=
true
;
}
if
(
m_thumbInCache
&&
m_dataInCache
)
{
m_done
=
true
;
m_successful
=
true
;
return
true
;
}
bool
ok
=
m_binClip
->
clipType
()
==
ClipType
::
Playlist
?
false
:
computeWithFFMPEG
();
ok
=
ok
?
ok
:
computeWithMlt
();
Q_ASSERT
(
ok
==
m_done
);
if
(
ok
&&
m_done
&&
!
m_audioLevels
.
isEmpty
())
{
if
(
ok
&&
m_done
&&
!
m_dataInCache
&&
!
m_audioLevels
.
isEmpty
())
{
// Put into an image for caching.
int
count
=
m_audioLevels
.
size
();
image
=
QImage
((
int
)
lrint
((
count
+
3
)
/
4.0
/
m_channels
),
m_channels
,
QImage
::
Format_ARGB32
);
...
...
@@ -312,6 +342,9 @@ bool AudioThumbJob::startJob()
image
.
save
(
m_cachePath
);
m_successful
=
true
;
return
true
;
}
else
if
(
ok
&&
m_thumbInCache
&&
m_done
)
{
m_successful
=
true
;
return
true
;
}
m_done
=
true
;
m_successful
=
false
;
...
...
@@ -321,6 +354,7 @@ bool AudioThumbJob::startJob()
bool
AudioThumbJob
::
commitResult
(
Fun
&
undo
,
Fun
&
redo
)
{
Q_ASSERT
(
!
m_resultConsumed
);
m_ffmpegProcess
.
reset
();
if
(
!
m_done
)
{
qDebug
()
<<
"ERROR: Trying to consume invalid results"
;
return
false
;
...
...
@@ -330,14 +364,22 @@ bool AudioThumbJob::commitResult(Fun &undo, Fun &redo)
return
false
;
}
QList
<
double
>
old
=
m_binClip
->
audioFrameCache
;
QImage
oldImage
=
m_binClip
->
thumbnail
(
m_thumbSize
.
width
(),
m_thumbSize
.
height
()).
toImage
();
QImage
result
=
ThumbnailCache
::
get
()
->
getAudioThumbnail
(
m_clipId
);
// note that the image is moved into lambda, it won't be available from this class anymore
auto
operation
=
[
clip
=
m_binClip
,
audio
=
std
::
move
(
m_audioLevels
)]()
{
auto
operation
=
[
clip
=
m_binClip
,
audio
=
std
::
move
(
m_audioLevels
)
,
image
=
std
::
move
(
result
)
]()
{
clip
->
updateAudioThumbnail
(
audio
);
if
(
!
image
.
isNull
()
&&
clip
->
clipType
()
==
ClipType
::
Audio
)
{
clip
->
setThumbnail
(
image
);
}
return
true
;
};
auto
reverse
=
[
clip
=
m_binClip
,
audio
=
std
::
move
(
old
)]()
{
auto
reverse
=
[
clip
=
m_binClip
,
audio
=
std
::
move
(
old
)
,
image
=
std
::
move
(
oldImage
)
]()
{
clip
->
updateAudioThumbnail
(
audio
);
if
(
!
image
.
isNull
()
&&
clip
->
clipType
()
==
ClipType
::
Audio
)
{
clip
->
setThumbnail
(
image
);
}
return
true
;
};
bool
ok
=
operation
();
...
...
src/jobs/audiothumbjob.hpp
View file @
1a7d2eee
...
...
@@ -24,6 +24,7 @@
#include "abstractclipjob.h"
#include <memory>
#include <QImage>
/* @brief This class represents the job that corresponds to computing the audio thumb of a clip (waveform)
*/
...
...
@@ -63,11 +64,14 @@ protected:
private:
std
::
shared_ptr
<
ProjectClip
>
m_binClip
;
std
::
shared_ptr
<
Mlt
::
Producer
>
m_prod
;
QString
m_miniThumbPath
;
QString
m_cachePath
;
QSize
m_thumbSize
;
bool
m_dataInCache
;
bool
m_thumbInCache
;
bool
m_done
{
false
},
m_successful
{
false
};
int
m_channels
,
m_frequency
,
m_lengthInFrames
,
m_audioStream
;
QList
<
double
>
m_audioLevels
;
QProcess
*
m_ffmpegProcess
;
std
::
unique_ptr
<
QProcess
>
m_ffmpegProcess
;
};
src/jobs/thumbjob.cpp
View file @
1a7d2eee
...
...
@@ -81,9 +81,9 @@ bool ThumbJob::startJob()
return
true
;
}
m_inCache
=
false
;
if
(
ThumbnailCache
::
get
()
->
hasThumbnail
(
m_
binClip
->
clipId
()
,
m_frameNumber
,
!
m_persistent
))
{
if
(
ThumbnailCache
::
get
()
->
hasThumbnail
(
m_clipId
,
m_frameNumber
,
!
m_persistent
))
{
m_done
=
true
;
m_result
=
ThumbnailCache
::
get
()
->
getThumbnail
(
m_
binClip
->
clipId
()
,
m_frameNumber
);
m_result
=
ThumbnailCache
::
get
()
->
getThumbnail
(
m_clipId
,
m_frameNumber
);
m_inCache
=
true
;
return
true
;
}
...
...
@@ -129,7 +129,7 @@ bool ThumbJob::commitResult(Fun &undo, Fun &redo)
p
.
setPen
(
Qt
::
white
);
p
.
drawText
(
0
,
0
,
m_fullWidth
,
m_imageHeight
,
Qt
::
AlignCenter
,
i18n
(
"Invalid"
));
}
else
{
ThumbnailCache
::
get
()
->
storeThumbnail
(
m_
binClip
->
clipId
()
,
m_frameNumber
,
m_result
,
m_persistent
);
ThumbnailCache
::
get
()
->
storeThumbnail
(
m_clipId
,
m_frameNumber
,
m_result
,
m_persistent
);
}
}
m_resultConsumed
=
true
;
...
...
src/kdenlivesettings.kcfg
View file @
1a7d2eee
...
...
@@ -354,7 +354,7 @@
<entry
name=
"window_background"
type=
"Color"
>
<label>
Background color for OpenGL monitor.
</label>
<default>
#
999999
</default>
<default>
#
535353
</default>
</entry>
<entry
name=
"volume"
type=
"Int"
>
...
...
src/monitor/glwidget.cpp
View file @
1a7d2eee
...
...
@@ -35,7 +35,6 @@
#include "kdenlivesettings.h"
#include "monitorproxy.h"
#include "profiles/profilemodel.hpp"
#include "qml/qmlaudiothumb.h"
#include "timeline2/view/qml/timelineitems.h"
#include <mlt++/Mlt.h>
...
...
@@ -95,7 +94,6 @@ GLWidget::GLWidget(int id, QObject *parent)
,
m_isZoneMode
(
false
)
,
m_isLoopMode
(
false
)
,
m_offset
(
QPoint
(
0
,
0
))
,
m_audioWaveDisplayed
(
false
)
,
m_fbo
(
nullptr
)
,
m_shareContext
(
nullptr
)
,
m_openGLSync
(
false
)
...
...
@@ -114,7 +112,6 @@ GLWidget::GLWidget(int id, QObject *parent)
qRegisterMetaType
<
Mlt
::
Frame
>
(
"Mlt::Frame"
);
qRegisterMetaType
<
SharedFrame
>
(
"SharedFrame"
);
qmlRegisterType
<
QmlAudioThumb
>
(
"AudioThumb"
,
1
,
0
,
"QmlAudioThumb"
);
setPersistentOpenGLContext
(
true
);
setPersistentSceneGraph
(
true
);
setClearBeforeRendering
(
false
);
...
...
@@ -138,8 +135,6 @@ GLWidget::GLWidget(int id, QObject *parent)
connect
(
this
,
&
QQuickWindow
::
sceneGraphInitialized
,
this
,
&
GLWidget
::
initializeGL
,
Qt
::
DirectConnection
);
connect
(
this
,
&
QQuickWindow
::
beforeRendering
,
this
,
&
GLWidget
::
paintGL
,
Qt
::
DirectConnection
);
connect
(
this
,
&
GLWidget
::
buildAudioThumb
,
this
,
&
GLWidget
::
setAudioThumb
);
registerTimelineItems
();
m_proxy
=
new
MonitorProxy
(
this
);
connect
(
m_proxy
,
&
MonitorProxy
::
seekRequestChanged
,
this
,
&
GLWidget
::
requestSeek
);
...
...
@@ -928,20 +923,6 @@ static void onThreadStopped(mlt_properties owner, GLWidget *self)
self
->
stopGlsl
();
}
void
GLWidget
::
slotSwitchAudioOverlay
(
bool
enable
)
{
KdenliveSettings
::
setDisplayAudioOverlay
(
enable
);
if
(
m_audioWaveDisplayed
&&
!
enable
)
{
if
(
m_producer
&&
m_producer
->
get_int
(
"video_index"
)
!=
-
1
)
{
// We have a video producer, disable filter
removeAudioOverlay
();
}
}
if
(
enable
&&
!
m_audioWaveDisplayed
&&
m_producer
)
{
createAudioOverlay
(
m_producer
->
get_int
(
"video_index"
)
==
-
1
);
}
}
int
GLWidget
::
setProducer
(
const
std
::
shared_ptr
<
Mlt
::
Producer
>
&
producer
,
bool
isActive
,
int
position
)
{
int
error
=
0
;
...
...
@@ -954,9 +935,6 @@ int GLWidget::setProducer(const std::shared_ptr<Mlt::Producer> &producer, bool i
if
(
currentId
==
QLatin1String
(
"black"
))
{
return
0
;
}
if
(
m_audioWaveDisplayed
)
{
removeAudioOverlay
();
}
m_producer
=
m_blackClip
;
// Reset markersModel
rootContext
()
->
setContextProperty
(
"markersModel"
,
0
);
...
...
@@ -983,27 +961,6 @@ int GLWidget::setProducer(const std::shared_ptr<Mlt::Producer> &producer, bool i
return
error
;
}
consumerPosition
=
m_consumer
->
position
();
if
(
m_producer
->
get_int
(
"video_index"
)
==
-
1
)
{
// This is an audio only clip, attach visualization filter. Currently, the filter crashes MLT when Movit accel is used
if
(
!
m_audioWaveDisplayed
)
{
createAudioOverlay
(
true
);
}
else
if
(
m_consumer
)
{
if
(
KdenliveSettings
::
gpu_accel
())
{
removeAudioOverlay
();
}
else
{
adjustAudioOverlay
(
true
);
}
}
}
else
if
(
m_audioWaveDisplayed
&&
(
m_consumer
!=
nullptr
))
{
// This is not an audio clip, hide wave
if
(
KdenliveSettings
::
displayAudioOverlay
())
{
adjustAudioOverlay
(
m_producer
->
get_int
(
"video_index"
)
==
-
1
);
}
else
{
removeAudioOverlay
();
}
}
else
if
(
KdenliveSettings
::
displayAudioOverlay
())
{
createAudioOverlay
(
false
);
}
if
(
position
==
-
1
&&
m_producer
->
parent
().
get
(
"kdenlive:id"
)
==
currentId
)
{
position
=
consumerPosition
;
}
...
...
@@ -1026,74 +983,6 @@ void GLWidget::resetDrops()
}
}
void
GLWidget
::
createAudioOverlay
(
bool
isAudio
)
{
if
(
!
m_consumer
)
{
return
;
}
if
(
isAudio
&&
KdenliveSettings
::
gpu_accel
())
{
// Audiowaveform filter crashes on Movit + audio clips)
return
;
}
Mlt
::
Filter
f
(
pCore
->
getCurrentProfile
()
->
profile
(),
"audiowaveform"
);
if
(
f
.
is_valid
())
{
// f.set("show_channel", 1);
f
.
set
(
"color.1"
,
"0xffff0099"
);
f
.
set
(
"fill"
,
1
);
if
(
isAudio
)
{
// Fill screen
f
.
set
(
"rect"
,
"0,0,100%,100%"
);
}
else
{
// Overlay on lower part of the screen
f
.
set
(
"rect"
,
"0,80%,100%,20%"
);
}
m_consumer
->
attach
(
f
);
m_audioWaveDisplayed
=
true
;
}
}
void
GLWidget
::
removeAudioOverlay
()
{
Mlt
::
Service
sourceService
(
m_consumer
->
get_service
());
// move all effects to the correct producer
int
ct
=
0
;
Mlt
::
Filter
*
filter
=
sourceService
.
filter
(
ct
);
while
(
filter
!=
nullptr
)
{
QString
srv
=
filter
->
get
(
"mlt_service"
);
if
(
srv
==
QLatin1String
(
"audiowaveform"
))
{
sourceService
.
detach
(
*
filter
);
delete
filter
;
break
;
}
else
{
ct
++
;
}