Git commit 3fc4464f474a5acecbed42ff70d390b0874f5ce0 by Nicolas Carion.
Committed on 30/04/2018 at 22:27.
Pushed by alcinos into branch 'refactoring_timeline'.
Steps towards better gestion of the master producers in the projectClip
M +146 -19 src/bin/projectclip.cpp
M +31 -4 src/bin/projectclip.h
M +16 -0 src/definitions.cpp
M +5 -3 src/definitions.h
M +65 -93 src/effects/effectstack/model/effectstackmodel.cpp
M +13 -6 src/effects/effectstack/model/effectstackmodel.hpp
M +2 -8 src/mainwindow.cpp
M +20 -39 src/mltcontroller/clipcontroller.cpp
M +7 -7 src/mltcontroller/clipcontroller.h
M +43 -8 src/timeline2/model/builders/meltBuilder.cpp
M +69 -52 src/timeline2/model/clipmodel.cpp
M +18 -12 src/timeline2/model/clipmodel.hpp
M +8 -8 src/timeline2/model/timelinefunctions.cpp
M +3 -3 src/timeline2/model/timelinefunctions.hpp
M +2 -2 src/timeline2/model/timelineitemmodel.cpp
M +3 -2 src/timeline2/model/timelinemodel.cpp
M +1 -1 src/timeline2/model/timelinemodel.hpp
M +2 -2 src/timeline2/view/timelinecontroller.cpp
M +1 -1 src/timeline2/view/timelinecontroller.h
M +2 -2 tests/groupstest.cpp
M +31 -31 tests/modeltest.cpp
M +2 -2 tests/regressions.cpp
https://commits.kde.org/kdenlive/3fc4464f474a5acecbed42ff70d390b0874f5ce0
diff --git a/src/bin/projectclip.cpp b/src/bin/projectclip.cpp
index f5504310e..b67acbb1d 100644
--- a/src/bin/projectclip.cpp
+++ b/src/bin/projectclip.cpp
@@ -28,9 +28,8 @@ along with this program. If not, see .
#include "doc/kthumb.h"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "jobs/jobmanager.h"
-#include "jobs/thumbjob.hpp"
#include "jobs/loadjob.hpp"
-#include
+#include "jobs/thumbjob.hpp"
#include "kdenlivesettings.h"
#include "lib/audio/audioStreamInfo.h"
#include "mltcontroller/clip.h"
@@ -49,6 +48,7 @@ along with this program. If not, see .
#include "utils/thumbnailcache.hpp"
#include "xml/xml.hpp"
#include
+#include
#include
=
#include "kdenlive_debug.h"
@@ -77,7 +77,8 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &=
thumb, std::shared_ptr<
} else {
m_thumbnail =3D thumb;
}
- if (m_clipType =3D=3D ClipType::AV || m_clipType =3D=3D ClipType::Audi=
o || m_clipType =3D=3D ClipType::Image || m_clipType =3D=3D ClipType::Video=
|| m_clipType =3D=3D ClipType::Playlist || m_clipType =3D=3D ClipType::Tex=
tTemplate) {
+ if (m_clipType =3D=3D ClipType::AV || m_clipType =3D=3D ClipType::Audi=
o || m_clipType =3D=3D ClipType::Image || m_clipType =3D=3D ClipType::Video=
||
+ m_clipType =3D=3D ClipType::Playlist || m_clipType =3D=3D ClipType=
::TextTemplate) {
pCore->bin()->addWatchFile(id, clipUrl());
}
// Make sure we have a hash for this clip
@@ -138,7 +139,8 @@ std::shared_ptr ProjectClip::construct(con=
st QString &id, const QDo
ProjectClip::~ProjectClip()
{
// controller is deleted in bincontroller
- if (m_clipType =3D=3D ClipType::AV || m_clipType =3D=3D ClipType::Audi=
o || m_clipType =3D=3D ClipType::Image || m_clipType =3D=3D ClipType::Video=
|| m_clipType =3D=3D ClipType::Playlist || m_clipType =3D=3D ClipType::Tex=
tTemplate) {
+ if (m_clipType =3D=3D ClipType::AV || m_clipType =3D=3D ClipType::Audi=
o || m_clipType =3D=3D ClipType::Image || m_clipType =3D=3D ClipType::Video=
||
+ m_clipType =3D=3D ClipType::Playlist || m_clipType =3D=3D ClipType=
::TextTemplate) {
pCore->bin()->removeWatchFile(clipId(), clipUrl());
}
m_thumbMutex.lock();
@@ -146,11 +148,6 @@ ProjectClip::~ProjectClip()
m_thumbMutex.unlock();
m_thumbThread.waitForFinished();
audioFrameCache.clear();
- // delete all timeline producers
- std::map>::iterator itr =3D m_time=
lineProducers.begin();
- while (itr !=3D m_timelineProducers.end()) {
- itr =3D m_timelineProducers.erase(itr);
- }
}
=
void ProjectClip::connectEffectStack()
@@ -301,7 +298,7 @@ void ProjectClip::reloadProducer(bool refreshOnly)
pCore->jobManager()->startJob({clipId()}, loadjobId, QSt=
ring(), 150, -1, true, true);
=
} else {
- //TODO: check if another load job is running?
+ // TODO: check if another load job is running?
QDomDocument doc;
QDomElement xml =3D toXml(doc);
if (!xml.isNull()) {
@@ -341,7 +338,8 @@ void ProjectClip::setThumbnail(const QImage &img)
}
m_thumbnail =3D QIcon(thumb);
if (auto ptr =3D m_model.lock()) {
- std::static_pointer_cast(ptr)->onItemUpdated(std=
::static_pointer_cast(shared_from_this()), AbstractProjectItem=
::DataThumbnail);
+ std::static_pointer_cast(ptr)->onItemUpdated(std=
::static_pointer_cast(shared_from_this()),
+ Abs=
tractProjectItem::DataThumbnail);
}
}
=
@@ -379,11 +377,13 @@ bool ProjectClip::setProducer(std::shared_ptr producer, bool repl
if (auto ptr =3D m_model.lock()) emit std::static_pointer_cast(ptr)->refreshPanel(m_binId);
}
if (auto ptr =3D m_model.lock()) {
- std::static_pointer_cast(ptr)->onItemUpdated(std=
::static_pointer_cast(shared_from_this()), AbstractProjectItem=
::DataDuration);
+ std::static_pointer_cast(ptr)->onItemUpdated(std=
::static_pointer_cast(shared_from_this()),
+ Abs=
tractProjectItem::DataDuration);
}
// Make sure we have a hash for this clip
getFileHash();
- if (m_clipType =3D=3D ClipType::AV || m_clipType =3D=3D ClipType::Audi=
o || m_clipType =3D=3D ClipType::Image || m_clipType =3D=3D ClipType::Video=
|| m_clipType =3D=3D ClipType::Playlist || m_clipType =3D=3D ClipType::Tex=
tTemplate) {
+ if (m_clipType =3D=3D ClipType::AV || m_clipType =3D=3D ClipType::Audi=
o || m_clipType =3D=3D ClipType::Image || m_clipType =3D=3D ClipType::Video=
||
+ m_clipType =3D=3D ClipType::Playlist || m_clipType =3D=3D ClipType=
::TextTemplate) {
pCore->bin()->addWatchFile(clipId(), clipUrl());
}
// set parent again (some info need to be stored in producer)
@@ -405,7 +405,7 @@ std::shared_ptr ProjectClip::thumbProduc=
er()
return nullptr;
}
if (KdenliveSettings::gpu_accel()) {
- //TODO: when the original producer changes, we must reload this th=
umb producer
+ // TODO: when the original producer changes, we must reload this t=
humb producer
Clip clip(*prod.get());
m_thumbsProducer =3D std::make_shared(clip.softClon=
e(ClipController::getPassPropertiesList()));
Mlt::Filter scaler(*prod->profile(), "swscale");
@@ -418,6 +418,127 @@ std::shared_ptr ProjectClip::thumbProd=
ucer()
return m_thumbsProducer;
}
=
+void ProjectClip::createVideoMasterProducer()
+{
+ if (!m_videoProducer) {
+ m_videoProducer =3D cloneProducer(&pCore->getCurrentProfile()->pro=
file());
+ // disable audio but activate video
+ m_videoProducer->set("set.test_audio", 1);
+ m_videoProducer->set("set.test_image", 0);
+ }
+}
+std::shared_ptr ProjectClip::getTimelineProducer(int clipId=
, PlaylistState state, double speed)
+{
+ if (qFuzzyCompare(speed, 1.0)) {
+ // we are requesting a normal speed producer
+ // We can first cleen the speed producers we have for the current =
id
+ m_timewarpProducers.erase(clipId);
+ if (state =3D=3D PlaylistState::AudioOnly) {
+ // We need to get an audio producer, if none exists
+ if (m_audioProducers.count(clipId) =3D=3D 0) {
+ m_audioProducers[clipId] =3D cloneProducer(&pCore->getCurr=
entProfile()->profile());
+ m_audioProducers[clipId]->set("set.test_audio", 0);
+ m_audioProducers[clipId]->set("set.test_image", 1);
+ }
+ return std::shared_ptr(m_audioProducers[clipId]=
->cut());
+ }
+ // we return the video producer
+ m_audioProducers.erase(clipId);
+ createVideoMasterProducer();
+ return std::shared_ptr(m_videoProducer->cut());
+ }
+
+ // in that case, we need to create a warp producer, if we don't have o=
ne
+ m_audioProducers.erase(clipId);
+
+ std::shared_ptr warpProducer;
+ if (m_timewarpProducers.count(clipId) > 0) {
+ if (qFuzzyCompare(m_timewarpProducers[clipId]->get_double("warp_sp=
eed"), speed)) {
+ // the producer we have is good, use it !
+ warpProducer =3D m_timewarpProducers[clipId];
+ }
+ }
+ if (!warpProducer) {
+ QString resource =3D QString("timewarp:%1:%2").arg(speed).arg(orig=
inalProducer()->get("resource"));
+ warpProducer.reset(new Mlt::Producer(*originalProducer()->profile(=
), resource.toUtf8().constData()));
+ }
+
+ warpProducer->set("set.test_audio", 1);
+ warpProducer->set("set.test_image", 1);
+ if (state =3D=3D PlaylistState::AudioOnly) {
+ warpProducer->set("set.test_audio", 0);
+ }
+ if (state =3D=3D PlaylistState::VideoOnly) {
+ warpProducer->set("set.test_image", 0);
+ }
+ m_timewarpProducers[clipId] =3D warpProducer;
+ return warpProducer;
+}
+
+std::pair, bool> ProjectClip::giveMasterAnd=
GetTimelineProducer(int clipId, std::shared_ptr master,
+ =
PlaylistState state)
+{
+ int in =3D master->get_in();
+ int out =3D master->get_out();
+ if (master->parent().is_valid()) {
+ // in that case, we have a cut
+ // check whether it's a timewarp
+ double speed =3D 1.0;
+ double timeWarp =3D false;
+ if (QString::fromUtf8(master->parent().get("mlt_service")) =3D=3D =
QLatin1String("timewarp")) {
+ speed =3D master->parent().get_double("warp_speed");
+ timeWarp =3D true;
+ }
+ if (master->parent().get_int("loaded") =3D=3D 1) {
+ // we already have a clip that shares the same master
+
+ if (state =3D=3D PlaylistState::AudioOnly || timeWarp) {
+ // In that case, we must create copies
+ return {getTimelineProducer(clipId, state, speed), false};
+ }
+ // if it's a video or disabled clip, we must make sure that it=
s master clip matches our video master
+ if (!m_videoProducer) {
+ qDebug() << "Warning: weird, we found a video clip whose m=
aster is already loaded but we don't have any yet";
+ createVideoMasterProducer();
+ return {std::shared_ptr(m_videoProducer->cu=
t(in, out)), false};
+ }
+ if (QString::fromUtf8(m_videoProducer->get("id")) !=3D QString=
::fromUtf8(master->parent().get("id"))) {
+ qDebug() << "Warning: weird, we found a video clip whose m=
aster is already loaded but doesn't match ours";
+ return {std::shared_ptr(m_videoProducer->cu=
t(in, out)), false};
+ }
+ // We have a good id, this clip can be used
+ return {master, true};
+ } else {
+ master->parent().set("loaded", 1);
+ if (state =3D=3D PlaylistState::AudioOnly) {
+ m_audioProducers[clipId] =3D std::shared_ptr(&master->parent());
+ return {master, true};
+ }
+ if (timeWarp) {
+ m_timewarpProducers[clipId] =3D std::shared_ptr(&master->parent());
+ return {master, true};
+ }
+ if (!m_videoProducer) {
+ // good, we found a master video producer, and we didn't h=
ave any
+ m_videoProducer.reset(&master->parent());
+ return {master, true};
+ }
+ qDebug() << "Warning: weird, we found a video clip whose maste=
r is not loaded but we already have a master";
+ return {std::shared_ptr(m_videoProducer->cut(in=
, out)), false};
+ }
+ } else if (master->is_valid()) {
+ // in that case, we have a master
+ qDebug() << "Warning: weird, we received a master clip in lieue of=
a cut";
+ double speed =3D 1.0;
+ if (QString::fromUtf8(master->get("mlt_service")) =3D=3D QLatin1St=
ring("timewarp")) {
+ speed =3D master->get_double("warp_speed");
+ }
+ return {getTimelineProducer(clipId, state, speed), false};
+ }
+ // we have a problem
+ return {std::shared_ptr(ClipController::mediaUnavailabl=
e->cut()), false};
+}
+/*
std::shared_ptr ProjectClip::timelineProducer(PlaylistState=
::ClipState state, int track)
{
if (!m_service.startsWith(QLatin1String("avformat"))) {
@@ -452,7 +573,7 @@ std::shared_ptr ProjectClip::timelinePro=
ducer(PlaylistState::Clip
std::shared_ptr normalProd =3D cloneProducer();
m_timelineProducers[track] =3D normalProd;
return std::shared_ptr(normalProd->cut());
-}
+}*/
=
std::shared_ptr ProjectClip::cloneProducer(Mlt::Profile *de=
stProfile)
{
@@ -554,7 +675,7 @@ const QString ProjectClip::getFileHash()
break;
}
if (fileHash.isEmpty()) {
- qDebug()<<"// WARNING EMPTY CLIP HASH: ";
+ qDebug() << "// WARNING EMPTY CLIP HASH: ";
return QString();
}
QString result =3D fileHash.toHex();
@@ -585,7 +706,8 @@ void ProjectClip::setProperties(const QMap &properties, bool r
if (properties.contains(QStringLiteral("templatetext"))) {
m_description =3D properties.value(QStringLiteral("templatetext"));
if (auto ptr =3D m_model.lock())
- std::static_pointer_cast(ptr)->onItemUpdated=
(std::static_pointer_cast(shared_from_this()), AbstractProject=
Item::ClipStatus);
+ std::static_pointer_cast(ptr)->onItemUpdated=
(std::static_pointer_cast(shared_from_this()),
+ =
AbstractProjectItem::ClipStatus);
refreshPanel =3D true;
}
timelineProperties << QStringLiteral("force_aspect_ratio") << QStringL=
iteral("video_index") << QStringLiteral("audio_index")
@@ -639,7 +761,8 @@ void ProjectClip::setProperties(const QMap &properties, bool r
if (properties.contains(QStringLiteral("length")) || properties.contai=
ns(QStringLiteral("kdenlive:duration"))) {
m_duration =3D getStringDuration();
if (auto ptr =3D m_model.lock())
- std::static_pointer_cast(ptr)->onItemUpdated=
(std::static_pointer_cast(shared_from_this()), AbstractProject=
Item::DataDuration);
+ std::static_pointer_cast(ptr)->onItemUpdated=
(std::static_pointer_cast(shared_from_this()),
+ =
AbstractProjectItem::DataDuration);
refreshOnly =3D false;
reload =3D true;
}
@@ -648,7 +771,8 @@ void ProjectClip::setProperties(const QMap &properties, bool r
m_name =3D properties.value(QStringLiteral("kdenlive:clipname"));
refreshPanel =3D true;
if (auto ptr =3D m_model.lock()) {
- std::static_pointer_cast(ptr)->onItemUpdated=
(std::static_pointer_cast(shared_from_this()), AbstractProject=
Item::DataName);
+ std::static_pointer_cast(ptr)->onItemUpdated=
(std::static_pointer_cast(shared_from_this()),
+ =
AbstractProjectItem::DataName);
}
// update timeline clips
updateTimelineClips(QVector() << TimelineModel::NameRole);
@@ -980,6 +1104,8 @@ bool ProjectClip::isIncludedInTimeline()
=
void ProjectClip::updateChildProducers()
{
+ // TODO refac: the effect should be managed by an effectstack on the m=
aster
+ /*
// pass effect stack on all child producers
QMutexLocker locker(&m_producerMutex);
for (const auto &clip : m_timelineProducers) {
@@ -989,6 +1115,7 @@ void ProjectClip::updateChildProducers()
clp.replaceEffects(*m_masterProducer);
}
}
+ */
}
=
void ProjectClip::replaceInTimeline()
diff --git a/src/bin/projectclip.h b/src/bin/projectclip.h
index 45210c2da..a46b438f5 100644
--- a/src/bin/projectclip.h
+++ b/src/bin/projectclip.h
@@ -49,7 +49,14 @@ class Properties;
/**
* @class ProjectClip
* @brief Represents a clip in the project (not timeline).
- *
+ * It will be displayed as a bin item that can be dragged onto the timelin=
e.
+ * A single bin clip can be inserted several times on the timeline, and th=
e ProjectClip
+ * keeps track of all the ids of the corresponding ClipModel.
+ * Note that because of a limitation in melt and AvFilter, it is currently=
difficult to
+ * mix the audio of two producers that are cut from the same master produc=
er
+ * (that produces small but noticeable clicking artifacts)
+ * To workaround this, we need to have a master clip for each instance of =
the audio clip in the timeline. This class is tracking them all. This track=
also holds
+ * a master clip for each clip where the timewarp producer has been applied
*/
=
class ProjectClip : public AbstractProjectItem, public ClipController
@@ -140,7 +147,7 @@ public:
bool isReady() const;
=
/** @brief Returns this clip's producer. */
- std::shared_ptr< Mlt::Producer > thumbProducer();
+ std::shared_ptr thumbProducer();
=
/** @brief Recursively disable/enable bin effects. */
void setBinEffectsEnabled(bool enabled) override;
@@ -188,7 +195,19 @@ public:
bool isIncludedInTimeline() override;
/** @brief Returns a list of all timeline clip ids for this bin clip */
QList timelineInstances() const;
- std::shared_ptr timelineProducer(PlaylistState::ClipSta=
te state =3D PlaylistState::Original, int track =3D 1);
+ /** @brief This function returns a cut to the master producer associat=
ed to the timeline clip with given ID.
+ Each clip must have a different master producer (see comment of th=
e class)
+ */
+ std::shared_ptr getTimelineProducer(int clipId, Playlis=
tState st, double speed =3D 1.0);
+
+ /* @brief This function should only be used at loading. It takes a pro=
ducer that was read from mlt, and checks whether the master producer is alr=
eady in
+ use. If yes, then we must create a new one, because of the mixing b=
ug. In any case, we return a cut of the master that can be used in the time=
line The
+ bool returned has the following sementic:
+ - if true, then the returned cut still possibly has effect on i=
t. You need to rebuild the effectStack based on this
+ - if false, the the returned cut don't have effects anymore (it=
's a fresh one), so you need to reload effects from the old producer
+ */
+ std::pair, bool> giveMasterAndGetTimeli=
neProducer(int clipId, std::shared_ptr master, PlaylistState=
state);
+
std::shared_ptr cloneProducer(Mlt::Profile *destProfile=
=3D nullptr);
void updateTimelineClips(QVector roles);
=
@@ -234,8 +253,16 @@ private:
const QString geometryWithOffset(const QString &data, int offset);
void doExtractImage();
=
+ // This is a helper function that creates the video producer. This is =
a clone of the original one, with audio disabled
+ void createVideoMasterProducer();
+
std::map> m_registeredClips;
- std::map> m_timelineProducers;
+
+ // 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 valu=
es
+ std::unordered_map> m_audioProduce=
rs;
+ std::unordered_map> m_timewarpProd=
ucers;
+ std::shared_ptr m_videoProducer;
=
signals:
void producerChanged(const QString &, const std::shared_ptr &);
diff --git a/src/definitions.cpp b/src/definitions.cpp
index 1736abb30..bf55e8768 100644
--- a/src/definitions.cpp
+++ b/src/definitions.cpp
@@ -158,3 +158,19 @@ GroupType groupTypeFromStr(const QString &s)
Q_ASSERT(false);
return GroupType::Normal;
}
+
+std::pair stateToBool(PlaylistState state)
+{
+ return {state =3D=3D PlaylistState::VideoOnly, state =3D=3D PlaylistSt=
ate::AudioOnly};
+}
+PlaylistState stateFromBool(std::pair av)
+{
+ assert(!av.first || !av.second);
+ if (av.first) {
+ return PlaylistState::VideoOnly;
+ } else if (av.second) {
+ return PlaylistState::AudioOnly;
+ } else {
+ return PlaylistState::Disabled;
+ }
+}
diff --git a/src/definitions.h b/src/definitions.h
index ea53f6777..7158b1c20 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -77,10 +77,12 @@ enum OperationType {
ZoomTimeline
};
=
-namespace PlaylistState {
+enum class PlaylistState { VideoOnly =3D 1, AudioOnly =3D 2, Disabled =3D =
3 };
+Q_DECLARE_METATYPE(PlaylistState)
=
-enum ClipState { Original =3D 0, VideoOnly =3D 1, AudioOnly =3D 2, Disable=
d =3D 3 };
-}
+// returns a pair corresponding to (video, audio)
+std::pair stateToBool(PlaylistState state);
+PlaylistState stateFromBool(std::pair av);
=
namespace TimelineMode {
enum EditMode { NormalEdit =3D 0, OverwriteEdit =3D 1, InsertEdit =3D 2 };
diff --git a/src/effects/effectstack/model/effectstackmodel.cpp b/src/effec=
ts/effectstack/model/effectstackmodel.cpp
index dc968ed0c..0e2f1ef06 100644
--- a/src/effects/effectstack/model/effectstackmodel.cpp
+++ b/src/effects/effectstack/model/effectstackmodel.cpp
@@ -48,36 +48,6 @@ std::shared_ptr EffectStackModel::cons=
truct(std::weak_ptrfilter_count(); i++) {
- if (ptr->filter(i)->get("kdenlive_id") =3D=3D nullptr) {
- // don't consider internal MLT stuff
- continue;
- }
- QString effectId =3D ptr->filter(i)->get("kdenlive_id");
- auto effect =3D EffectItemModel::construct(ptr->filter(i), sha=
red_from_this());
- // effect->setParameters
- Fun redo =3D addItem_lambda(effect, rootItem->getId());
- redo();
- if (effectId =3D=3D QLatin1String("fadein") || effectId =3D=3D=
QLatin1String("fade_from_black")) {
- fadeIns << effect->getId();
- } else if (effectId =3D=3D QLatin1String("fadeout") || effectI=
d =3D=3D QLatin1String("fade_to_black")) {
- fadeOuts << effect->getId();
- }
- connect(effect.get(), &AssetParameterModel::modelChanged, this=
, &EffectStackModel::modelChanged);
- connect(effect.get(), &AssetParameterModel::replugEffect, this=
, &EffectStackModel::replugEffect, Qt::DirectConnection);
- }
- } else {
- qDebug() << "// CANNOT LOCK CLIP SEEVCE";
- }
- this->modelChanged();
- m_loadingExisting =3D false;
-}
-
void EffectStackModel::resetService(std::weak_ptr service)
{
m_service =3D std::move(service);
@@ -112,8 +82,8 @@ void EffectStackModel::removeEffect(std::shared_ptr effect)
if (res) {
int inFades =3D fadeIns.size();
int outFades =3D fadeOuts.size();
- fadeIns.removeAll(effect->getId());
- fadeOuts.removeAll(effect->getId());
+ fadeIns.erase(effect->getId());
+ fadeOuts.erase(effect->getId());
inFades =3D fadeIns.size() - inFades;
outFades =3D fadeOuts.size() - outFades;
QString effectName =3D EffectsRepository::get()->getName(effect->g=
etAssetId());
@@ -129,11 +99,10 @@ void EffectStackModel::removeEffect(std::shared_ptr effect)
emit dataChanged(ix, ix, QVector());
}
}
- //TODO: only update if effect is fade or keyframe
+ // TODO: only update if effect is fade or keyframe
if (inFades < 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadein")=
);
- }
- else if (outFades < 0) {
+ } else if (outFades < 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"=
));
}
pCore->updateItemKeyframes(m_ownerId);
@@ -146,23 +115,30 @@ void EffectStackModel::removeEffect(std::shared_ptr effect)
}
}
=
-void EffectStackModel::copyEffect(std::shared_ptr sour=
ceItem)
+void EffectStackModel::copyEffect(std::shared_ptr sour=
ceItem, bool logUndo)
{
if (sourceItem->childCount() > 0) {
// TODO: group
return;
}
std::shared_ptr sourceEffect =3D std::static_pointer_=
cast(sourceItem);
- QString effectId =3D sourceEffect->getAssetId();
+ const QString effectId =3D sourceEffect->getAssetId();
auto effect =3D EffectItemModel::construct(effectId, shared_from_this(=
));
effect->setParameters(sourceEffect->getAllParameters());
+ effect->filter().set("in", sourceEffect->filter().get_int("in"));
+ effect->filter().set("out", sourceEffect->filter().get_int("out"));
Fun undo =3D removeItem_lambda(effect->getId());
// TODO the parent should probably not always be the root
Fun redo =3D addItem_lambda(effect, rootItem->getId());
connect(effect.get(), &AssetParameterModel::modelChanged, this, &Effec=
tStackModel::modelChanged);
connect(effect.get(), &AssetParameterModel::replugEffect, this, &Effec=
tStackModel::replugEffect, Qt::DirectConnection);
+ if (effectId =3D=3D QLatin1String("fadein") || effectId =3D=3D QLatin1=
String("fade_from_black")) {
+ fadeIns.insert(effect->getId());
+ } else if (effectId =3D=3D QLatin1String("fadeout") || effectId =3D=3D=
QLatin1String("fade_to_black")) {
+ fadeOuts.insert(effect->getId());
+ }
bool res =3D redo();
- if (res) {
+ if (res && logUndo) {
QString effectName =3D EffectsRepository::get()->getName(effectId);
PUSH_UNDO(undo, redo, i18n("copy effect %1", effectName));
}
@@ -188,19 +164,18 @@ void EffectStackModel::appendEffect(const QString &ef=
fectId, bool makeCurrent)
int inFades =3D 0;
int outFades =3D 0;
if (effectId =3D=3D QLatin1String("fadein") || effectId =3D=3D QLa=
tin1String("fade_from_black")) {
- fadeIns << effect->getId();
+ fadeIns.insert(effect->getId());
inFades++;
} else if (effectId =3D=3D QLatin1String("fadeout") || effectId =
=3D=3D QLatin1String("fade_to_black")) {
- fadeOuts << effect->getId();
- outFades ++;
+ fadeOuts.insert(effect->getId());
+ outFades++;
}
QString effectName =3D EffectsRepository::get()->getName(effectId);
Fun update =3D [this, inFades, outFades]() {
- //TODO: only update if effect is fade or keyframe
+ // TODO: only update if effect is fade or keyframe
if (inFades > 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadein")=
);
- }
- else if (outFades > 0) {
+ } else if (outFades > 0) {
pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"=
));
}
pCore->updateItemKeyframes(m_ownerId);
@@ -227,11 +202,11 @@ bool EffectStackModel::adjustStackLength(bool adjustF=
romEnd, int oldIn, int oldD
auto ptr =3D m_service.lock();
int out =3D newIn + duration;
for (int i =3D 0; i < rootItem->childCount(); ++i) {
- if (fadeInDuration > 0 && fadeIns.contains(std::static_pointer_cas=
t(rootItem->child(i))->getId())) {
+ if (fadeInDuration > 0 && fadeIns.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0) {
std::shared_ptr effect =3D std::static_pointe=
r_cast(rootItem->child(i));
int oldEffectIn =3D qMax(0, effect->filter().get_in());
int oldEffectOut =3D effect->filter().get_out();
- qDebug()<<"--previous effect: "<filter().get_length() - 1,=
duration);
indexes << getIndexFromItem(effect);
if (!adjustFromEnd && (oldIn !=3D newIn || duration !=3D oldDu=
ration)) {
@@ -239,7 +214,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFro=
mEnd, int oldIn, int oldD
Fun operation =3D [this, effect, newIn, effectDuration, lo=
gUndo]() {
effect->setParameter(QStringLiteral("in"), newIn, fals=
e);
effect->setParameter(QStringLiteral("out"), newIn + ef=
fectDuration, logUndo);
- qDebug()<<"--new effect: "<setParameter(QStringLiteral("out"), refere=
nceEffectOut, true);
- effect->filter().set("_refout", (char*) nullptr);
+ effect->filter().set("_refout", (char *)nullptr);
return true;
};
PUSH_LAMBDA(operation, redo);
PUSH_LAMBDA(reverse, undo);
}
}
- } else if (fadeOutDuration > 0 && fadeOuts.contains(std::static_po=
inter_cast(rootItem->child(i))->getId())) {
+ } else if (fadeOutDuration > 0 && fadeOuts.count(std::static_point=
er_cast(rootItem->child(i))->getId()) > 0) {
std::shared_ptr effect =3D std::static_pointe=
r_cast(rootItem->child(i));
int effectDuration =3D qMin(fadeOutDuration, duration);
int newFadeIn =3D out - effectDuration;
@@ -291,7 +266,7 @@ bool EffectStackModel::adjustStackLength(bool adjustFro=
mEnd, int oldIn, int oldD
Fun reverse =3D [this, effect, referenceEffectIn, oldOut](=
) {
effect->setParameter(QStringLiteral("in"), referenceEf=
fectIn, false);
effect->setParameter(QStringLiteral("out"), oldOut, tr=
ue);
- effect->filter().set("_refin", (char*) nullptr);
+ effect->filter().set("_refin", (char *)nullptr);
return true;
};
PUSH_LAMBDA(operation, redo);
@@ -322,7 +297,7 @@ bool EffectStackModel::adjustFadeLength(int duration, b=
ool fromStart, bool audio
{
if (fromStart) {
// Fade in
- if (fadeIns.isEmpty()) {
+ if (fadeIns.empty()) {
if (audioFade) {
appendEffect(QStringLiteral("fadein"));
}
@@ -336,9 +311,9 @@ bool EffectStackModel::adjustFadeLength(int duration, b=
ool fromStart, bool audio
if (ptr) {
in =3D ptr->get_int("in");
}
- qDebug()<<"//// SETTING CLIP FADIN: "<childCount(); ++i) {
- if (fadeIns.contains(std::static_pointer_cast(rootIt=
em->child(i))->getId())) {
+ if (fadeIns.count(std::static_pointer_cast(rootItem-=
>child(i))->getId()) > 0) {
std::shared_ptr effect =3D std::static_po=
inter_cast(rootItem->child(i));
effect->filter().set("in", in);
duration =3D qMin(pCore->getItemDuration(m_ownerId), durat=
ion);
@@ -352,7 +327,7 @@ bool EffectStackModel::adjustFadeLength(int duration, b=
ool fromStart, bool audio
}
} else {
// Fade out
- if (fadeOuts.isEmpty()) {
+ if (fadeOuts.empty()) {
if (audioFade) {
appendEffect(QStringLiteral("fadeout"));
}
@@ -368,7 +343,7 @@ bool EffectStackModel::adjustFadeLength(int duration, b=
ool fromStart, bool audio
int out =3D in + pCore->getItemDuration(m_ownerId);
QList indexes;
for (int i =3D 0; i < rootItem->childCount(); ++i) {
- if (fadeOuts.contains(std::static_pointer_cast(rootI=
tem->child(i))->getId())) {
+ if (fadeOuts.count(std::static_pointer_cast(rootItem=
->child(i))->getId()) > 0) {
std::shared_ptr effect =3D std::static_po=
inter_cast(rootItem->child(i));
effect->filter().set("out", out);
duration =3D qMin(pCore->getItemDuration(m_ownerId), durat=
ion);
@@ -377,7 +352,7 @@ bool EffectStackModel::adjustFadeLength(int duration, b=
ool fromStart, bool audio
}
}
if (!indexes.isEmpty()) {
- qDebug()<<"// UPDATING DATA INDEXES 2!!!";
+ qDebug() << "// UPDATING DATA INDEXES 2!!!";
emit dataChanged(indexes.first(), indexes.last(), QVector=
());
pCore->updateItemModel(m_ownerId, QStringLiteral("fadeout"));
}
@@ -388,21 +363,21 @@ bool EffectStackModel::adjustFadeLength(int duration,=
bool fromStart, bool audio
int EffectStackModel::getFadePosition(bool fromStart)
{
if (fromStart) {
- if (fadeIns.isEmpty()) {
+ if (fadeIns.empty()) {
return 0;
}
for (int i =3D 0; i < rootItem->childCount(); ++i) {
- if (fadeIns.first() =3D=3D std::static_pointer_cast(=
rootItem->child(i))->getId()) {
+ if (*(fadeIns.begin()) =3D=3D std::static_pointer_cast(rootItem->child(i))->getId()) {
std::shared_ptr effect =3D std::static_po=
inter_cast(rootItem->child(i));
return effect->filter().get_length();
}
}
} else {
- if (fadeOuts.isEmpty()) {
+ if (fadeOuts.empty()) {
return 0;
}
for (int i =3D 0; i < rootItem->childCount(); ++i) {
- if (fadeOuts.first() =3D=3D std::static_pointer_cast=
(rootItem->child(i))->getId()) {
+ if (*(fadeOuts.begin()) =3D=3D std::static_pointer_cast(rootItem->child(i))->getId()) {
std::shared_ptr effect =3D std::static_po=
inter_cast(rootItem->child(i));
return effect->filter().get_length();
}
@@ -413,20 +388,19 @@ int EffectStackModel::getFadePosition(bool fromStart)
=
bool EffectStackModel::removeFade(bool fromStart)
{
- QList toRemove;
+ std::vector toRemove;
for (int i =3D 0; i < rootItem->childCount(); ++i) {
- if ((fromStart && fadeIns.contains(std::static_pointer_cast(rootItem->child(i))->getId())) ||
- (!fromStart && fadeOuts.contains(std::static_pointer_cast(rootItem->child(i))->getId()))) {
- toRemove << i;
+ if ((fromStart && fadeIns.count(std::static_pointer_cast=
(rootItem->child(i))->getId()) > 0) ||
+ (!fromStart && fadeOuts.count(std::static_pointer_cast(rootItem->child(i))->getId()) > 0)) {
+ toRemove.push_back(i);
}
}
- qSort(toRemove.begin(), toRemove.end(), qGreater());
- foreach (int i, toRemove) {
+ for (int i : toRemove) {
std::shared_ptr effect =3D std::static_pointer_ca=
st(rootItem->child(i));
if (fromStart) {
- fadeIns.removeAll(rootItem->child(i)->getId());
+ fadeIns.erase(rootItem->child(i)->getId());
} else {
- fadeOuts.removeAll(rootItem->child(i)->getId());
+ fadeOuts.erase(rootItem->child(i)->getId());
}
removeEffect(effect);
}
@@ -507,28 +481,27 @@ void EffectStackModel::importEffects(std::shared_ptr<=
EffectStackModel> sourceSta
// TODO: manage fades, keyframes if clips don't have same size / in po=
int
for (int i =3D 0; i < sourceStack->rowCount(); i++) {
auto item =3D sourceStack->getEffectStackRow(i);
- if (item->childCount() > 0) {
- // TODO: group
- continue;
- }
- std::shared_ptr effect =3D std::static_pointer_ca=
st(item);
- auto clone =3D EffectItemModel::construct(effect->getAssetId(), sh=
ared_from_this());
- rootItem->appendChild(clone);
- clone->setParameters(effect->getAllParameters());
- clone->filter().set("in", effect->filter().get_int("in"));
- clone->filter().set("out", effect->filter().get_int("out"));
- const QString effectId =3D effect->getAssetId();
- if (effectId =3D=3D QLatin1String("fadein") || effectId =3D=3D QLa=
tin1String("fade_from_black")) {
- fadeIns << clone->getId();
- } else if (effectId =3D=3D QLatin1String("fadeout") || effectId =
=3D=3D QLatin1String("fade_to_black")) {
- fadeOuts << clone->getId();
+ copyEffect(item, false);
+ }
+ modelChanged();
+}
+
+void EffectStackModel::importEffects(std::weak_ptr service, =
bool alreadyExist)
+{
+ m_loadingExisting =3D alreadyExist;
+ if (auto ptr =3D service.lock()) {
+ for (int i =3D 0; i < ptr->filter_count(); i++) {
+ if (ptr->filter(i)->get("kdenlive_id") =3D=3D nullptr) {
+ // don't consider internal MLT stuff
+ continue;
+ }
+ QString effectId =3D ptr->filter(i)->get("kdenlive_id");
+ auto effect =3D EffectItemModel::construct(ptr->filter(i), sha=
red_from_this());
+ copyEffect(effect, false);
}
- // TODO parent should not always be root
- Fun redo =3D addItem_lambda(clone, rootItem->getId());
- connect(effect.get(), &AssetParameterModel::modelChanged, this, &E=
ffectStackModel::modelChanged);
- connect(effect.get(), &AssetParameterModel::replugEffect, this, &E=
ffectStackModel::replugEffect, Qt::DirectConnection);
- redo();
}
+ m_loadingExisting =3D false;
+ modelChanged();
}
=
void EffectStackModel::setActiveEffect(int ix)
@@ -683,19 +656,18 @@ void EffectStackModel::replugEffect(std::shared_ptr asset)
=
void EffectStackModel::cleanFadeEffects(bool outEffects, Fun &undo, Fun &r=
edo)
{
- QList toDelete =3D outEffects ? fadeOuts : fadeIns;
+ const auto &toDelete =3D outEffects ? fadeOuts : fadeIns;
for (int id : toDelete) {
auto effect =3D std::static_pointer_cast(getItemB=
yId(id));
Fun operation =3D removeItem_lambda(id);
if (operation()) {
Fun reverse =3D addItem_lambda(effect, rootItem->getId());
- PUSH_LAMBDA(operation, redo);
- PUSH_LAMBDA(reverse, undo);
+ UPDATE_UNDO_REDO(operation, reverse, undo, redo);
}
}
- if (!toDelete.isEmpty()) {
+ if (!toDelete.empty()) {
Fun update =3D [this]() {
- //TODO: only update if effect is fade or keyframe
+ // TODO: only update if effect is fade or keyframe
pCore->updateItemKeyframes(m_ownerId);
return true;
};
diff --git a/src/effects/effectstack/model/effectstackmodel.hpp b/src/effec=
ts/effectstack/model/effectstackmodel.hpp
index 0e81ddaa9..8a4f215e0 100644
--- a/src/effects/effectstack/model/effectstackmodel.hpp
+++ b/src/effects/effectstack/model/effectstackmodel.hpp
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
=
/* @brief This class an effect stack as viewed by the back-end.
It is responsible for planting and managing effects into the producer i=
t holds a pointer to.
@@ -51,7 +52,6 @@ public:
*/
static std::shared_ptr construct(std::weak_ptr service, ObjectId ownerId, std::weak_ptr undo_stack);
void resetService(std::weak_ptr service);
- void loadEffects();
=
protected:
EffectStackModel(std::weak_ptr service, ObjectId ownerId=
, std::weak_ptr undo_stack);
@@ -59,11 +59,18 @@ protected:
public:
/* @brief Add an effect at the bottom of the stack */
void appendEffect(const QString &effectId, bool makeCurrent =3D false);
- /* @brief Copy an existing effect and append it at the bottom of the s=
tack */
- void copyEffect(std::shared_ptr sourceItem);
+ /* @brief Copy an existing effect and append it at the bottom of the s=
tack
+ @param logUndo: if true, an undo/redo is created
+ */
+ void copyEffect(std::shared_ptr sourceItem, bool l=
ogUndo =3D true);
/* @brief Import all effects from the given effect stack
*/
void importEffects(std::shared_ptr sourceStack);
+ /* @brief Import all effects attached to a given service
+ @param alreadyExist: if true, the effect should be already attached=
to the service owned by this effectstack (it means we are in the process o=
f loading).
+ In that case, we need to build the stack but not replant the effects
+ */
+ void importEffects(std::weak_ptr service, bool alreadyEx=
ist =3D false);
bool removeFade(bool fromStart);
=
/* @brief This function change the global (timeline-wise) enabled stat=
e of the effects
@@ -120,13 +127,13 @@ protected:
=
private:
mutable QReadWriteLock m_lock;
- QList fadeIns;
- QList fadeOuts;
+ std::unordered_set fadeIns;
+ std::unordered_set fadeOuts;
+
/** @brief: When loading a project, we load filters/effects that are a=
lready planted
* in the producer, so we shouldn't plant them again. Setting=
this value to
* true will prevent planting in the producer */
bool m_loadingExisting;
-
private slots:
/** @brief: Some effects do not support dynamic changes like sox, and =
need to be unplugged / replugged on each param change
*/
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index d7b2584c7..dd5aef928 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1403,23 +1403,17 @@ void MainWindow::setupActions()
=
QAction *audioOnly =3D new QAction(KoIconUtils::themedIcon(QStringLite=
ral("document-new")), i18n("Audio Only"), this);
addAction(QStringLiteral("clip_audio_only"), audioOnly);
- audioOnly->setData(PlaylistState::AudioOnly);
+ audioOnly->setData(QVariant::fromValue(PlaylistState::AudioOnly));
audioOnly->setCheckable(true);
=
QAction *videoOnly =3D new QAction(KoIconUtils::themedIcon(QStringLite=
ral("document-new")), i18n("Video Only"), this);
addAction(QStringLiteral("clip_video_only"), videoOnly);
- videoOnly->setData(PlaylistState::VideoOnly);
+ videoOnly->setData(QVariant::fromValue(PlaylistState::VideoOnly));
videoOnly->setCheckable(true);
=
- QAction *audioAndVideo =3D new QAction(KoIconUtils::themedIcon(QString=
Literal("document-new")), i18n("Audio and Video"), this);
- addAction(QStringLiteral("clip_audio_and_video"), audioAndVideo);
- audioAndVideo->setData(PlaylistState::Original);
- audioAndVideo->setCheckable(true);
-
m_clipTypeGroup =3D new QActionGroup(this);
m_clipTypeGroup->addAction(audioOnly);
m_clipTypeGroup->addAction(videoOnly);
- m_clipTypeGroup->addAction(audioAndVideo);
connect(m_clipTypeGroup, &QActionGroup::triggered, this, &MainWindow::=
slotUpdateClipType);
m_clipTypeGroup->setEnabled(false);
=
diff --git a/src/mltcontroller/clipcontroller.cpp b/src/mltcontroller/clipc=
ontroller.cpp
index a08a08cd2..7f7a83992 100644
--- a/src/mltcontroller/clipcontroller.cpp
+++ b/src/mltcontroller/clipcontroller.cpp
@@ -57,7 +57,7 @@ ClipController::ClipController(const QString clipId, std:=
:shared_ptr &pro
m_producerLock.unlock();
qCDebug(KDENLIVE_LOG) << "// WARNING, USING INVALID PRODUCER";
} else {
- checkAudio();
+ checkAudioVideo();
m_producerLock.unlock();
QString proxy =3D m_properties->get("kdenlive:proxy");
m_service =3D m_properties->get("mlt_service");
@@ -303,7 +303,7 @@ void ClipController::updateProducer(const std::shared_p=
tr &produc
passProperties.pass_list(*m_properties, getPassPropertiesList(m_usesPr=
oxy));
delete m_properties;
*m_masterProducer =3D producer.get();
- checkAudio();
+ checkAudioVideo();
m_properties =3D new Mlt::Properties(m_masterProducer->get_properties(=
));
// Pass properties from previous producer
m_properties->pass_list(passProperties, getPassPropertiesList(m_usesPr=
oxy));
@@ -322,40 +322,6 @@ void ClipController::updateProducer(const std::shared_=
ptr &produc
qDebug() << "// replace finished: " << binId() << " : " << m_masterPro=
ducer->get("resource");
}
=
-Mlt::Producer *ClipController::getTrackProducer(const QString &trackName, =
PlaylistState::ClipState clipState, double speed)
-{
- // TODO Delete this
- Q_UNUSED(speed);
- Q_UNUSED(trackName);
- Q_UNUSED(clipState);
- return m_masterProducer.get();
- /*
- //TODO
- Q_UNUSED(speed)
-
- if (trackName.isEmpty()) {
- return m_masterProducer;
- }
- if (m_clipType !=3D AV && m_clipType !=3D Audio && m_clipType !=3D Pla=
ylist) {
- // Only producers with audio need a different producer for each tr=
ack (or we have an audio crackle bug)
- return new Mlt::Producer(m_masterProducer->parent());
- }
- QString clipWithTrackId =3D clipId();
- clipWithTrackId.append(QLatin1Char('_') + trackName);
-
- //TODO handle audio / video only producers and framebuffer
- if (clipState =3D=3D PlaylistState::AudioOnly) {
- clipWithTrackId.append(QStringLiteral("_audio"));
- } else if (clipState =3D=3D PlaylistState::VideoOnly) {
- clipWithTrackId.append(QStringLiteral("_video"));
- }
-
- Mlt::Producer *clone =3D m_binController->cloneProducer(*m_masterProdu=
cer);
- clone->set("id", clipWithTrackId.toUtf8().constData());
- //m_binController->replaceBinPlaylistClip(clipWithTrackId, clone->pare=
nt());
- return clone;
- */
-}
=
const QString ClipController::getStringDuration()
{
@@ -569,12 +535,27 @@ bool ClipController::hasAudio() const
{
return m_hasAudio;
}
-void ClipController::checkAudio()
+void ClipController::checkAudioVideo()
{
m_masterProducer->seek(0);
Mlt::Frame *frame =3D m_masterProducer->get_frame();
- // test_audio returns 1 if there is NO audio (strange but at the time =
this code is written)
+ // test_audio returns 1 if there is NO audio (strange but true at the =
time this code is written)
m_hasAudio =3D frame->get_int("test_audio") =3D=3D 0;
+ m_hasVideo =3D frame->get_int("test_image") =3D=3D 0;
+}
+bool ClipController::hasVideo() const
+{
+ return m_hasVideo;
+}
+PlaylistState ClipController::defaultState() const
+{
+ if (hasVideo()) {
+ return PlaylistState::VideoOnly;
+ }
+ if (hasAudio()) {
+ return PlaylistState::AudioOnly;
+ }
+ return PlaylistState::Disabled;
}
=
QPixmap ClipController::pixmap(int framePosition, int width, int height)
diff --git a/src/mltcontroller/clipcontroller.h b/src/mltcontroller/clipcon=
troller.h
index cbdf05dc1..1fde5ade5 100644
--- a/src/mltcontroller/clipcontroller.h
+++ b/src/mltcontroller/clipcontroller.h
@@ -145,11 +145,6 @@ public:
=
/** @brief Holds index of currently selected master clip effect. */
int selectedEffectIndex;
- /** @brief Get a clone of master producer for a specific track. Retrie=
ve it if it already exists
- * in our list, otherwise we create it.
- * Deprecated, track logic should be handled in timeline/track.cpp */
- Q_DECL_DEPRECATED Mlt::Producer *getTrackProducer(const QString &track=
Name, PlaylistState::ClipState clipState =3D PlaylistState::Original,
- double speed =3D 1.0=
);
=
/** @brief Sets the master producer for this clip when we build the co=
ntroller without master clip. */
void addMasterProducer(const std::shared_ptr &producer);
@@ -173,6 +168,10 @@ public:
bool hasEffects() const;
/** @brief Returns true if the clip contains at least one audio stream=
*/
bool hasAudio() const;
+ /** @brief Returns true if the clip contains at least one video stream=
*/
+ bool hasVideo() const;
+ /** @brief Returns the default state a clip should be in. If the clips=
contains both video and audio, this defaults to video */
+ PlaylistState defaultState() const;
/** @brief Returns info about clip audio */
const std::unique_ptr &audioInfo() const;
/** @brief Returns true if audio thumbnails for this clip are cached */
@@ -204,8 +203,8 @@ protected:
virtual void emitProducerChanged(const QString &, const std::shared_pt=
r &){};
virtual void connectEffectStack(){};
=
- // This is the helper function that checks the clip for audio and stor=
es the result
- void checkAudio();
+ // This is the helper function that checks if the clip has audio and v=
ideo and stores the result
+ void checkAudioVideo();
=
std::shared_ptr m_masterProducer;
Mlt::Properties *m_properties;
@@ -223,6 +222,7 @@ protected:
std::shared_ptr m_effectStack;
std::shared_ptr m_markerModel;
bool m_hasAudio;
+ bool m_hasVideo;
=
private:
QMutex m_producerLock;
diff --git a/src/timeline2/model/builders/meltBuilder.cpp b/src/timeline2/m=
odel/builders/meltBuilder.cpp
index b1f344bfc..17963f5bb 100644
--- a/src/timeline2/model/builders/meltBuilder.cpp
+++ b/src/timeline2/model/builders/meltBuilder.cpp
@@ -35,7 +35,8 @@
#include
#include
=
-bool constructTrackFromMelt(const std::shared_ptr &time=
line, int tid, Mlt::Tractor &track, const std::unordered_map &binIdCorresp,Fun &undo, Fun &redo);
+bool constructTrackFromMelt(const std::shared_ptr &time=
line, int tid, Mlt::Tractor &track,
+ const std::unordered_map &bi=
nIdCorresp, Fun &undo, Fun &redo);
bool constructTrackFromMelt(const std::shared_ptr &time=
line, int tid, Mlt::Playlist &track,
const std::unordered_map &bi=
nIdCorresp, Fun &undo, Fun &redo);
=
@@ -103,7 +104,7 @@ bool constructTimelineFromMelt(const std::shared_ptr &timelin
=
// Loading compositions
QScopedPointer service(tractor.producer());
- QList compositions;
+ QList compositions;
while ((service !=3D nullptr) && service->is_valid()) {
if (service->type() =3D=3D transition_type) {
Mlt::Transition t((mlt_transition)service->get_service());
@@ -124,16 +125,17 @@ bool constructTimelineFromMelt(const std::shared_ptr<=
TimelineItemModel> &timelin
if (!compositions.isEmpty()) {
std::sort(compositions.begin(), compositions.end(), [](Mlt::Transi=
tion *a, Mlt::Transition *b) { return a->get_b_track() < b->get_b_track(); =
});
while (!compositions.isEmpty()) {
- QScopedPointert(compositions.takeFirst());
+ QScopedPointer t(compositions.takeFirst());
Mlt::Properties transProps(t->get_properties());
QString id(t->get("kdenlive_id"));
int compoId;
- ok =3D timeline->requestCompositionInsertion(id, timeline->get=
TrackIndexFromPosition(t->get_b_track() - 1), t->get_a_track(), t->get_in()=
, t->get_length(), &transProps, compoId, undo, redo);
+ ok =3D timeline->requestCompositionInsertion(id, timeline->get=
TrackIndexFromPosition(t->get_b_track() - 1), t->get_a_track(), t->get_in(),
+ t->get_length(), &t=
ransProps, compoId, undo, redo);
if (!ok) {
qDebug() << "ERROR : failed to insert composition in track=
" << t->get_b_track() << ", position" << t->get_in();
break;
}
- qDebug() << "Inserted composition in track " << t->get_b_track=
() << ", position" << t->get_in()<<"/"<< t->get_out();
+ qDebug() << "Inserted composition in track " << t->get_b_track=
() << ", position" << t->get_in() << "/" << t->get_out();
}
}
=
@@ -149,7 +151,8 @@ bool constructTimelineFromMelt(const std::shared_ptr &timelin
return true;
}
=
-bool constructTrackFromMelt(const std::shared_ptr &time=
line, int tid, Mlt::Tractor &track, const std::unordered_map &binIdCorresp,Fun &undo, Fun &redo)
+bool constructTrackFromMelt(const std::shared_ptr &time=
line, int tid, Mlt::Tractor &track,
+ const std::unordered_map &bi=
nIdCorresp, Fun &undo, Fun &redo)
{
if (track.count() !=3D 2) {
// we expect a tractor with two tracks (a "fake" track)
@@ -186,6 +189,37 @@ bool constructTrackFromMelt(const std::shared_ptr