Fix storing of audio stream index can lead to stream confusion in timeline (a8a09fad) · Commits · Multimedia / Kdenlive · GitLab
Admin message
Join us at
Akademy
to celebrate KDE's 30th anniversary!
Travel support requests
are open till May 31st.
Register now
Verified
Commit
a8a09fad
authored
Nov 18, 2025
by
Jean-Baptiste Mardelle
Browse files
parent
d5597f40
Loading
Loading
Loading
Loading
Changes
Pipelines
Loading
Original line number
Diff line number
Diff line
@@ -978,34 +978,28 @@ std::shared_ptr
if
state
==
PlaylistState
::
AudioOnly
// We need to get an audio producer, if none exists
if
audioStream
if
trackId
>=
trackId
+=
100
audioStream
else
trackId
-=
100
audioStream
// second playlist producers use negative trackId
if
secondPlaylist
trackId
trackId
if
m_audioProducers
count
trackId
==
const
QString
audioTrackId
QString
"%1#%2"
).
arg
trackId
).
arg
audioStream
);
if
m_audioProducers
count
audioTrackId
==
std
::
shared_ptr
Mlt
::
Producer
finalProducer
if
m_clipType
==
ClipType
::
Timeline
std
::
shared_ptr
Mlt
::
Producer
prod
m_masterProducer
->
cut
maxDuration
));
m_audioProducers
trackId
prod
finalProducer
std
::
shared_ptr
Mlt
::
Producer
m_masterProducer
->
cut
maxDuration
));
else
m_audio
Producer
trackId
cloneProducer
true
true
);
final
Producer
cloneProducer
true
true
);
m_audio
Producer
trackId
->
set
"set.test_audio"
);
m_audio
Producer
trackId
->
set
"set.test_image"
);
final
Producer
->
set
"set.test_audio"
);
final
Producer
->
set
"set.test_image"
);
if
m_streamEffects
contains
audioStream
))
QStringList
effects
m_streamEffects
value
audioStream
);
for
const
QString
effect
std
::
as_const
effects
))
Mlt
::
Filter
filt
m_audio
Producer
trackId
->
get_profile
(),
effect
toUtf8
().
constData
());
Mlt
::
Filter
filt
final
Producer
->
get_profile
(),
effect
toUtf8
().
constData
());
if
filt
is_valid
())
// Add stream effect markup
filt
set
"kdenlive:stream"
);
m_audio
Producer
trackId
->
attach
filt
);
final
Producer
->
attach
filt
);
@@ -1014,20 +1008,21 @@ std::shared_ptr
if
newAudioStreamIndex
/** If the audioStreamIndex is not found, for example when replacing a clip with another one using different indexes,
default to first audio stream */
m_audio
Producer
trackId
->
set
"audio_index"
audioStream
);
final
Producer
->
set
"audio_index"
audioStream
);
else
newAudioStreamIndex
if
newAudioStreamIndex
audioStreamsCount
()
newAudioStreamIndex
m_audio
Producer
trackId
->
set
"astream"
newAudioStreamIndex
);
final
Producer
->
set
"astream"
newAudioStreamIndex
);
m_effectStack
->
addService
m_audioProducers
trackId
]);
m_audioProducers
audioTrackId
finalProducer
m_effectStack
->
addService
finalProducer
);
std
::
shared_ptr
Mlt
::
Producer
prod
m_audioProducers
rackId
->
cut
());
if
m_clipType
==
ClipType
::
Timeline
&&
m_audioProducers
rackId
->
parent
().
property_exists
"kdenlive:maxduration"
))
int
max
m_audioProducers
rackId
->
parent
().
get_int
"kdenlive:maxduration"
);
std
::
shared_ptr
Mlt
::
Producer
prod
m_audioProducers
audioT
rackId
->
cut
());
if
m_clipType
==
ClipType
::
Timeline
&&
m_audioProducers
audioT
rackId
->
parent
().
property_exists
"kdenlive:maxduration"
))
int
max
m_audioProducers
audioT
rackId
->
parent
().
get_int
"kdenlive:maxduration"
);
prod
->
set
"kdenlive:maxduration"
max
);
prod
->
set
"length"
max
);
@@ -1041,20 +1036,21 @@ std::shared_ptr
trackId
trackId
if
m_videoProducers
count
trackId
==
std
::
shared_ptr
Mlt
::
Producer
finalProducer
if
m_clipType
==
ClipType
::
Timeline
std
::
shared_ptr
Mlt
::
Producer
prod
m_masterProducer
->
cut
maxDuration
));
m_videoProducers
trackId
prod
finalProducer
std
::
shared_ptr
Mlt
::
Producer
m_masterProducer
->
cut
maxDuration
));
else
m_video
Producer
trackId
cloneProducer
true
true
);
final
Producer
cloneProducer
true
true
);
if
m_masterProducer
->
property_exists
"kdenlive:maxduration"
))
m_video
Producer
trackId
->
set
"kdenlive:maxduration"
m_masterProducer
->
get_int
"kdenlive:maxduration"
));
final
Producer
->
set
"kdenlive:maxduration"
m_masterProducer
->
get_int
"kdenlive:maxduration"
));
// Let audio enabled so that we can use audio visualization filters ?
m_videoProducers
trackId
->
set
"set.test_audio"
);
m_videoProducers
trackId
->
set
"set.test_image"
);
m_effectStack
->
addService
m_videoProducers
trackId
]);
finalProducer
->
set
"set.test_audio"
);
finalProducer
->
set
"set.test_image"
);
m_videoProducers
trackId
finalProducer
m_effectStack
->
addService
finalProducer
);
int
duration
m_masterProducer
->
time_to_frames
m_masterProducer
->
get
"kdenlive:duration"
));
return
std
::
shared_ptr
Mlt
::
Producer
m_videoProducers
trackId
->
cut
duration
duration
));
@@ -1251,20 +1247,18 @@ std::pair
if
state
==
PlaylistState
::
AudioOnly
int
audioStream
master
->
parent
().
get_int
"audio_index"
);
if
audioStream
tid
+=
100
audioStream
if
secondPlaylist
tid
tid
if
m_audioProducers
find
tid
!=
m_audioProducers
end
())
const
QString
audioTrackId
QString
"%1#%2"
).
arg
tid
).
arg
audioStream
);
if
m_audioProducers
find
audioTrackId
!=
m_audioProducers
end
())
// Buggy project, all clips in a track should use the same track producer, fix
qDebug
()
<<
"/// FOUND INCORRECT PRODUCER ON AUDIO TRACK; FIXING"
std
::
shared_ptr
Mlt
::
Producer
prod
getTimelineProducer
tid
clipId
state
master
->
parent
().
get_int
"audio_index"
speed
->
cut
in
out
));
std
::
shared_ptr
Mlt
::
Producer
prod
getTimelineProducer
tid
clipId
state
audioStream
speed
->
cut
in
out
));
return
prod
false
};
m_audioProducers
ti
std
::
make_shared
Mlt
::
Producer
master
->
parent
());
m_effectStack
->
loadService
m_audioProducers
at
ti
));
m_audioProducers
audioTrackI
std
::
make_shared
Mlt
::
Producer
master
->
parent
());
m_effectStack
->
loadService
m_audioProducers
at
audioTrackI
));
return
master
true
};
if
state
==
PlaylistState
::
VideoOnly
@@ -2208,19 +2202,30 @@ void ProjectClip::setBinEffectsEnabled(bool enabled)
ClipController
::
setBinEffectsEnabled
enabled
);
void
ProjectClip
::
registerService
std
::
weak_ptr
TimelineModel
timeline
int
clipId
const
std
::
shared_ptr
Mlt
::
Producer
service
bool
forceRegister
void
ProjectClip
::
registerService
std
::
weak_ptr
TimelineModel
timeline
int
clipId
const
std
::
shared_ptr
Mlt
::
Producer
service
ClipModel
::
TimelineClipInfo
info
bool
forceRegister
if
service
->
is_cut
()
||
forceRegister
if
info
speed
!=
1.
if
m_timewarpProducers
count
clipId
==
m_timewarpProducers
clipId
service
m_effectStack
->
addService
m_timewarpProducers
clipId
]);
else
int
hasAudio
service
->
get_int
"set.test_audio"
==
int
hasVideo
service
->
get_int
"set.test_image"
==
if
hasVideo
&&
m_videoProducers
count
clip
Id
==
if
hasVideo
&&
m_videoProducers
count
info
track
Id
==
// This is an undo producer, register it!
m_videoProducers
clipId
service
m_effectStack
->
addService
m_videoProducers
clipId
]);
else
if
hasAudio
&&
m_audioProducers
count
clipId
==
m_videoProducers
info
trackId
service
m_effectStack
->
addService
m_videoProducers
info
trackId
]);
else
if
hasAudio
const
QString
audioTrackId
QString
"%1#%2"
).
arg
info
trackId
).
arg
info
audioStream
);
if
m_audioProducers
count
audioTrackId
==
// This is an undo producer, register it!
m_audioProducers
clipId
service
m_effectStack
->
addService
m_audioProducers
clipId
]);
m_audioProducers
audioTrackId
service
m_effectStack
->
addService
m_audioProducers
audioTrackId
]);
registerTimelineClip
std
::
move
timeline
),
clipId
);
@@ -2291,18 +2296,24 @@ void ProjectClip::refreshBounds()
Q_EMIT
boundsChanged
boundaries
);
void
ProjectClip
::
deregisterTimelineClip
int
clipId
bool
audioClip
const
QUuid
uuid
void
ProjectClip
::
deregisterTimelineClip
int
clipId
bool
audioClip
ClipModel
::
TimelineClipInfo
info
const
QUuid
uuid
if
m_hasAudio
&&
audioClip
m_AudioUsage
--
if
m_videoProducers
count
clipId
m_effectStack
->
removeService
m_videoProducers
clipId
]);
m_videoProducers
erase
clipId
);
if
info
speed
!=
1.
&&
m_timewarpProducers
count
clipId
m_effectStack
->
removeService
m_timewarpProducers
clipId
]);
m_timewarpProducers
erase
clipId
);
else
if
audioClip
&&
m_videoProducers
count
info
trackId
m_effectStack
->
removeService
m_videoProducers
info
trackId
]);
m_videoProducers
erase
info
trackId
);
const
QString
audioTrackId
QString
"%1#%2"
).
arg
info
trackId
).
arg
info
audioStream
);
if
audioClip
&&
m_audioProducers
count
audioTrackId
m_effectStack
->
removeService
m_audioProducers
audioTrackId
]);
m_audioProducers
erase
audioTrackId
);
if
m_audioProducers
count
clipId
m_effectStack
->
removeService
m_audioProducers
clipId
]);
m_audioProducers
erase
clipId
);
// Clip might already have been deregistered
if
m_registeredClipsByUuid
contains
uuid
))
@@ -2871,7 +2882,7 @@ void ProjectClip::addAudioStreamEffect(int streamIndex, const QString effectName
m_streamEffects
insert
streamIndex
effects
);
setProducerProperty
QStringLiteral
"kdenlive:stream:%1"
).
arg
streamIndex
),
effects
join
QLatin1Char
'#'
)));
for
auto
m_audioProducers
int
stream
first
100
int
stream
first
section
QLatin1Char
'#'
),
).
toInt
()
if
stream
==
streamIndex
// Remove existing effects with same name
int
max
second
->
filter_count
();
@@ -2925,7 +2936,7 @@ void ProjectClip::removeAudioStreamEffect(int streamIndex, QString effectName)
return
for
auto
m_audioProducers
int
stream
first
100
int
stream
first
section
QLatin1Char
'#'
),
).
toInt
()
if
stream
==
streamIndex
int
max
second
->
filter_count
();
for
int
max
++
Original line number
Diff line number
Diff line
@@ -11,6 +11,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include
"abstractprojectitem.h"
#include
"definitions.h"
#include
"mltcontroller/clipcontroller.h"
#include
"timeline2/model/clipmodel.hpp"
#include
"timeline2/model/timelinemodel.hpp"
#include
@@ -327,7 +328,7 @@ protected:
QMutex
m_thumbMutex
/** @brief the following holds a producer for each audio clip in the timeline
* keys are the id of the clips in the timeline, values are their values */
std
::
unordered_map
in
std
::
shared_ptr
Mlt
::
Producer
>>
m_audioProducers
std
::
unordered_map
QStr
in
std
::
shared_ptr
Mlt
::
Producer
>>
m_audioProducers
std
::
unordered_map
int
std
::
shared_ptr
Mlt
::
Producer
>>
m_videoProducers
std
::
unordered_map
int
std
::
shared_ptr
Mlt
::
Producer
>>
m_timewarpProducers
std
::
shared_ptr
Mlt
::
Producer
m_disabledProducer
@@ -344,7 +345,8 @@ protected:
@param clipId id of the inserted clip
*/
void
registerTimelineClip
std
::
weak_ptr
TimelineModel
timeline
int
clipId
);
void
registerService
std
::
weak_ptr
TimelineModel
timeline
int
clipId
const
std
::
shared_ptr
Mlt
::
Producer
service
bool
forceRegister
false
);
void
registerService
std
::
weak_ptr
TimelineModel
timeline
int
clipId
const
std
::
shared_ptr
Mlt
::
Producer
service
ClipModel
::
TimelineClipInfo
info
bool
forceRegister
false
);
/** @brief update the producer to reflect new parent folder */
void
updateParent
std
::
shared_ptr
TreeItem
parent
override
@@ -352,7 +354,7 @@ protected:
/** @brief This is a call-back called by a ClipModel when it is deleted
@param clipId id of the deleted clip
*/
void
deregisterTimelineClip
int
clipId
bool
audioClip
const
QUuid
uuid
);
void
deregisterTimelineClip
int
clipId
bool
audioClip
ClipModel
::
TimelineClipInfo
info
const
QUuid
uuid
);
void
replaceInTimeline
();
void
limitMaxDuration
int
maxDuration
);
void
connectEffectStack
()
override
Original line number
Diff line number
Diff line
@@ -86,7 +86,6 @@ int ClipModel::construct(const std::shared_ptr
if
qFuzzyCompare
speed
1.
))
cutProducer
->
parent
().
set
"warp_pitch"
warp_pitch
);
qDebug
()
<<
"==== BUILT CLIP STREAM: "
<<
clip
->
audioStream
();
TRACE_CONSTR
clip
get
(),
parent
binClipId
id
state
speed
);
clip
->
setClipState_lambda
state
)();
parent
->
registerClip
clip
);
@@ -149,13 +148,23 @@ void ClipModel::registerClipToBin(std::shared_ptr
qDebug
()
<<
"Error : Bin clip for id: "
<<
m_binClipId
<<
" NOT AVAILABLE!!!"
qDebug
()
<<
"REGISTRATION "
<<
m_id
<<
"ptr count"
<<
m_parent
use_count
();
binClip
->
registerService
m_parent
m_id
std
::
move
service
),
registerProducer
);
binClip
->
registerService
m_parent
m_id
std
::
move
service
),
getClipInfo
(),
registerProducer
);
ClipModel
::
TimelineClipInfo
ClipModel
::
getClipInfo
()
TimelineClipInfo
info
info
trackId
getCurrentTrackId
();
info
audioStream
audioStream
();
info
speed
m_speed
info
pitchShift
m_producer
->
parent
().
get_int
"warp_pitch"
);
return
info
void
ClipModel
::
deregisterClipToBin
const
QUuid
uuid
std
::
shared_ptr
ProjectClip
binClip
pCore
->
projectItemModel
()
->
getClipByBinID
m_binClipId
);
binClip
->
deregisterTimelineClip
m_id
isAudioOnly
(),
uuid
);
binClip
->
deregisterTimelineClip
m_id
isAudioOnly
(),
getClipInfo
(),
uuid
);
ClipModel
::~
ClipModel
()
default
@@ -1138,7 +1147,7 @@ int ClipModel::audioStream() const
int
ClipModel
::
audioStreamIndex
()
const
READ_LOCK
();
return
pCore
->
projectItemModel
()
->
getClipByBinID
m_binClipId
->
audioStreamIndex
m_producer
->
parent
().
get_int
"audio_index"
))
return
pCore
->
projectItemModel
()
->
getClipByBinID
m_binClipId
->
audioStreamIndex
audioStream
))
int
ClipModel
::
fadeIn
()
const
Original line number
Diff line number
Diff line
@@ -37,6 +37,13 @@ protected:
public:
friend
class
KdenliveTests
struct
TimelineClipInfo
int
trackId
};
int
audioStream
};
double
speed
1.
};
bool
pitchShift
false
};
};
ClipModel
()
override
/** @brief Creates a clip, which references itself to the parent timeline
Returns the (unique) id of the created clip
@@ -128,6 +135,8 @@ protected:
Fun
setClipState_lambda
PlaylistState
::
ClipState
state
);
/** @brief Returns a clip hash, useful for regression testing */
QString
clipHash
()
const
/** @brief Get basic info to identify producer */
TimelineClipInfo
getClipInfo
();
public
/** @brief returns the length of the item on the timeline
Original line number
Diff line number
Diff line
@@ -777,6 +777,7 @@ TimelineModel::MoveResult TimelineModel::requestClipMove(int clipId, int trackId
qWarning
()
<<
"clip type mismatch 3"
return
MoveErrorType
int
sourceIndex
m_allClips
clipId
->
audioStreamIndex
();
std
::
function
bool
void
local_undo
[]()
return
true
};
std
::
function
bool
void
local_redo
[]()
return
true
};
bool
ok
true
@@ -1831,8 +1832,8 @@ bool TimelineModel::requestClipCreation(const QString &binClipId, int &id, Playl
Fun
local_redo
clip
this
state
audioStream
speed
warp_pitch
]()
// We capture a shared_ptr to the clip, which means that as long as this undo object lives, the clip object is not deleted. To insert it back it is
// sufficient to register it.
registerClip
clip
true
);
clip
->
refreshProducerFromBin
state
audioStream
speed
warp_pitch
);
registerClip
clip
true
);
return
true
};
Loading
US