From kde-commits Mon Apr 30 22:26:41 2018 From: Nicolas Carion Date: Mon, 30 Apr 2018 22:26:41 +0000 To: kde-commits Subject: [kdenlive/refactoring_timeline] /: Steps towards better gestion of the master producers in the proje Message-Id: X-MARC-Message: https://marc.info/?l=kde-commits&m=152512721700603 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 &timeline, return true; } = +namespace { + +// This function tries to recover the state of the producer (audio or vide= o or both) +PlaylistState inferState(std::shared_ptr prod) +{ + auto getProperty =3D [prod](const QString &name) { + if (prod->parent().is_valid()) { + return QString::fromUtf8(prod->parent().get(name.toUtf8().cons= tData())); + } + return QString::fromUtf8(prod->get(name.toUtf8().constData())); + }; + auto getIntProperty =3D [prod](const QString &name) { + if (prod->parent().is_valid()) { + return prod->parent().get_int(name.toUtf8().constData()); + } + return prod->get_int(name.toUtf8().constData()); + }; + QString service =3D getProperty("mlt_service"); + std::pair VidAud{true, true}; + VidAud.first =3D getIntProperty("set.test_image") =3D=3D 0; + VidAud.second =3D getIntProperty("set.test_audio") =3D=3D 0; + if (service.contains(QStringLiteral("avformat")) && getIntProperty(QSt= ringLiteral("video_index")) =3D=3D -1) { + VidAud.first =3D false; + } + if (service.contains(QStringLiteral("avformat")) && getIntProperty(QSt= ringLiteral("audio_index")) =3D=3D -1) { + VidAud.second =3D false; + } + return stateFromBool(VidAud); +} +} // namespace + bool constructTrackFromMelt(const std::shared_ptr &time= line, int tid, Mlt::Playlist &track, const std::unordered_map &bi= nIdCorresp, Fun &undo, Fun &redo) { @@ -198,7 +232,7 @@ bool constructTrackFromMelt(const std::shared_ptr &timeline, switch (clip->type()) { case unknown_type: case producer_type: { - //qDebug() << "Looking for clip clip "<< clip->parent().get("k= denlive:id")<<" =3D "<parent().get("kdenlive:clipname"); + // qDebug() << "Looking for clip clip "<< clip->parent().get("= kdenlive:id")<<" =3D "<parent().get("kdenlive:clipname"); QString binId; if (clip->parent().get_int("_kdenlive_processed") =3D=3D 1) { // This is a bin clip, already processed no need to change= id @@ -216,7 +250,8 @@ bool constructTrackFromMelt(const std::shared_ptr &timeline, } bool ok =3D false; if (pCore->bin()->getBinClip(binId)) { - int cid =3D ClipModel::construct(timeline, binId, clip); + PlaylistState st =3D inferState(clip); + int cid =3D ClipModel::construct(timeline, binId, clip, st= ); ok =3D timeline->requestClipMove(cid, tid, position, true,= false, undo, redo); } else { qDebug() << "// Cannot find bin clip: " << binId << " - " = << clip->get("id"); diff --git a/src/timeline2/model/clipmodel.cpp b/src/timeline2/model/clipmo= del.cpp index 473b20b9a..24fa750b1 100644 --- a/src/timeline2/model/clipmodel.cpp +++ b/src/timeline2/model/clipmodel.cpp @@ -52,19 +52,47 @@ ClipModel::ClipModel(std::shared_ptr par= ent, std::shared_ptr &parent, con= st QString &binClipId, int id, PlaylistState::ClipState state) +int ClipModel::construct(const std::shared_ptr &parent, con= st QString &binClipId, int id, PlaylistState state) { + id =3D (id =3D=3D -1 ? TimelineModel::getNextId() : id); std::shared_ptr binClip =3D pCore->projectItemModel()->ge= tClipByBinID(binClipId); - std::shared_ptr cutProducer =3D binClip->timelineProduc= er(state); - return construct(parent, binClipId, cutProducer, id); + + // We refine the state according to what the clip can actually produce + std::pair videoAudio =3D stateToBool(state); + videoAudio.first =3D videoAudio.first && binClip->hasVideo(); + videoAudio.second =3D videoAudio.second && binClip->hasAudio(); + state =3D stateFromBool(videoAudio); + + std::shared_ptr cutProducer =3D binClip->getTimelinePro= ducer(id, state, 1.); + + std::shared_ptr clip(new ClipModel(parent, cutProducer, bin= ClipId, id)); + clip->setClipState(state); + parent->registerClip(clip); + return id; } = -int ClipModel::construct(const std::shared_ptr &parent, con= st QString &binClipId, std::shared_ptr producer, int id) +int ClipModel::construct(const std::shared_ptr &parent, con= st QString &binClipId, std::shared_ptr producer, PlaylistSta= te state) { - std::shared_ptr clip(new ClipModel(parent, producer, binCli= pId, id)); - id =3D clip->m_id; + + // we hand the producer to the bin clip, and in return we get a cut to= a good master producer + // We might not be able to use directly the producer that we receive a= s an argument, because it cannot share the same master producer with any ot= her + // clipModel (due to a mlt limitation, see ProjectClip doc) + + int id =3D (id =3D=3D -1 ? TimelineModel::getNextId() : id); + std::shared_ptr binClip =3D pCore->projectItemModel()->ge= tClipByBinID(binClipId); + + // We refine the state according to what the clip can actually produce + std::pair videoAudio =3D stateToBool(state); + videoAudio.first =3D videoAudio.first && binClip->hasVideo(); + videoAudio.second =3D videoAudio.second && binClip->hasAudio(); + state =3D stateFromBool(videoAudio); + + auto result =3D binClip->giveMasterAndGetTimelineProducer(id, producer= , state); + std::shared_ptr clip(new ClipModel(parent, result.first, bi= nClipId, id)); + clip->m_effectStack->importEffects(producer, result.second); + clip->setClipState(state); parent->registerClip(clip); - clip->m_effectStack->loadEffects(); + return id; } = @@ -84,13 +112,11 @@ void ClipModel::deregisterClipToBin() binClip->deregisterTimelineClip(m_id); } = -ClipModel::~ClipModel() -{ -} +ClipModel::~ClipModel() {} = bool ClipModel::requestResize(int size, bool right, Fun &undo, Fun &redo, = bool logUndo) { - qDebug()<<"++++++++++ PERFORMAING CLIP RESIZE=3D=3D=3D=3D"; + qDebug() << "++++++++++ PERFORMAING CLIP RESIZE=3D=3D=3D=3D"; QWriteLocker locker(&m_lock); // qDebug() << "RESIZE CLIP" << m_id << "target size=3D" << size << "r= ight=3D" << right << "endless=3D" << m_endlessResize << "total length" << // m_producer->get_length() << "current length" << getPlaytime(); @@ -154,7 +180,7 @@ bool ClipModel::requestResize(int size, bool right, Fun= &undo, Fun &redo, bool l } return false; }; - qDebug()<<"// ADJUSTING EFFECT LENGTH, LOGUNDO "<get_playtime(); + qDebug() << "// ADJUSTING EFFECT LENGTH, LOGUNDO " << logUndo << "= , " << old_in << "/" << inPoint << ", " << m_producer->get_playtime(); if (logUndo) adjustEffectLength(right, old_in, inPoint, oldDuratio= n, m_producer->get_playtime(), reverse, operation, logUndo); UPDATE_UNDO_REDO(operation, reverse, undo, redo); return true; @@ -237,6 +263,13 @@ bool ClipModel::importEffects(std::shared_ptr stackModel) return true; } = +bool ClipModel::importEffects(std::weak_ptr service) +{ + QWriteLocker locker(&m_lock); + m_effectStack->importEffects(service); + return true; +} + bool ClipModel::removeFade(bool fromStart) { QWriteLocker locker(&m_lock); @@ -253,45 +286,34 @@ bool ClipModel::adjustEffectLength(bool adjustFromEnd= , int oldIn, int newIn, int bool ClipModel::adjustEffectLength(const QString &effectName, int duration= , int originalDuration, Fun &undo, Fun &redo) { QWriteLocker locker(&m_lock); - qDebug()<<".... ADJUSTING FADE LENGTH: "<adjustFadeLength(duration, effectName =3D=3D= QLatin1String("fadein") || effectName =3D=3D QLatin1String("fade_to_black"= ), hasAudio(), !isAudioOnly()); + return m_effectStack->adjustFadeLength(duration, effectName =3D=3D= QLatin1String("fadein") || effectName =3D=3D QLatin1String("fade_to_black"= ), audioEnabled(), + !isAudioOnly()); }; if (operation() && originalDuration > 0) { Fun reverse =3D [this, originalDuration, effectName]() { - return m_effectStack->adjustFadeLength(originalDuration, effec= tName =3D=3D QLatin1String("fadein") || effectName =3D=3D QLatin1String("fa= de_to_black"), hasAudio(), !isAudioOnly()); + return m_effectStack->adjustFadeLength(originalDuration, effec= tName =3D=3D QLatin1String("fadein") || effectName =3D=3D QLatin1String("fa= de_to_black"), + audioEnabled(), !isAudi= oOnly()); }; UPDATE_UNDO_REDO(operation, reverse, undo, redo); } return true; } = -bool ClipModel::hasAudio() const +bool ClipModel::audioEnabled() const { READ_LOCK(); - QString service =3D getProperty("mlt_service"); - if (service =3D=3D QLatin1String("xml")) { - // Playlist clip, assume audio - return true; - } - qDebug() << "checking audio:" << service - << ((service.contains(QStringLiteral("avformat")) || service = =3D=3D QLatin1String("timewarp")) && - (getIntProperty(QStringLiteral("audio_index")) > -1)); - if ((service.contains(QStringLiteral("avformat")) || service =3D=3D QL= atin1String("timewarp")) && (getIntProperty(QStringLiteral("audio_index")) = > -1)) { - return true; - } - std::shared_ptr binClip =3D pCore->projectItemModel()->ge= tClipByBinID(m_binClipId); - return binClip->hasAudio(); + return stateToBool(m_currentState).second; } = bool ClipModel::isAudioOnly() const { READ_LOCK(); - QString service =3D getProperty("mlt_service"); - return service.contains(QStringLiteral("avformat")) && (getIntProperty= (QStringLiteral("video_index")) =3D=3D -1); + return m_currentState =3D=3D PlaylistState::AudioOnly; } = -void ClipModel::refreshProducerFromBin(PlaylistState::ClipState state) +void ClipModel::refreshProducerFromBin(PlaylistState state) { QWriteLocker locker(&m_lock); if (getProperty("mlt_service") =3D=3D QLatin1String("timewarp")) { @@ -313,10 +335,9 @@ void ClipModel::refreshProducerFromBin(PlaylistState::= ClipState state) int in =3D getIn(); int out =3D getOut(); std::shared_ptr binClip =3D pCore->projectItemModel()->ge= tClipByBinID(m_binClipId); - std::shared_ptr binProducer =3D binClip->timelineProduc= er(state, m_currentTrackId); + std::shared_ptr binProducer =3D binClip->getTimelinePro= ducer(m_id, state); m_producer =3D std::move(binProducer); m_producer->set_in_and_out(in, out); - // m_producer.reset(binProducer->cut(in, out)); // replant effect stack in updated service m_effectStack->resetService(m_producer); m_producer->set("kdenlive:id", binClip->AbstractProjectItem::clipId().= toUtf8().constData()); @@ -324,6 +345,10 @@ void ClipModel::refreshProducerFromBin(PlaylistState::= ClipState state) m_endlessResize =3D !binClip->hasLimitedDuration(); } = +void ClipModel::refreshProducerFromBin() +{ + refreshProducerFromBin(m_currentState); +} = bool ClipModel::useTimewarpProducer(double speed, int extraSpace, Fun &und= o, Fun &redo) { @@ -357,7 +382,7 @@ Fun ClipModel::useTimewarpProducer_lambda(double speed,= int extraSpace) warp_out =3D out; } in =3D warp_in / speed; - out =3D qMin((int) (warp_out / speed), extraSpace); + out =3D qMin((int)(warp_out / speed), extraSpace); std::shared_ptr binClip =3D pCore->projectItemModel()->ge= tClipByBinID(m_binClipId); std::shared_ptr originalProducer =3D binClip->originalP= roducer(); bool limitedDuration =3D binClip->hasLimitedDuration(); @@ -442,29 +467,20 @@ void ClipModel::setShowKeyframes(bool show) service()->set("kdenlive:hide_keyframes", (int)!show); } = -bool ClipModel::setClipState(PlaylistState::ClipState state) +bool ClipModel::setClipState(PlaylistState state) { QWriteLocker locker(&m_lock); - if (!getProperty("mlt_service").startsWith(QStringLiteral("avformat"))= ) { - return false; - } - std::shared_ptr binClip =3D pCore->projectItemModel()->ge= tClipByBinID(m_binClipId); - std::shared_ptr binProducer =3D binClip->timelineProduc= er(state, m_currentTrackId); - int in =3D getIn(); - int out =3D getOut(); - m_producer =3D std::move(binProducer); - m_producer->set_in_and_out(in, out); - m_producer->set("kdenlive:id", m_binClipId.toUtf8().constData()); - m_producer->set("_kdenlive_cid", m_id); - - // replant effect stack in updated service - m_effectStack->resetService(m_producer); + std::pair VidAud =3D stateToBool(state); + m_producer->parent().set("set.test_image", int(VidAud.first ? 0 : 1)); + m_producer->parent().set("set.test_audio", int(VidAud.second ? 0 : 1)); return true; } = -PlaylistState::ClipState ClipModel::clipState() const +PlaylistState ClipModel::clipState() const { READ_LOCK(); + return m_currentState; + /* if (service()->parent().get_int("audio_index") =3D=3D -1) { if (service()->parent().get_int("video_index") =3D=3D -1) { return PlaylistState::Disabled; @@ -475,9 +491,10 @@ PlaylistState::ClipState ClipModel::clipState() const return PlaylistState::AudioOnly; } return PlaylistState::Original; + */ } = -void ClipModel::passTimelineProperties(std::shared_ptr other) +void ClipModel::passTimelineProperties(std::shared_ptr other) { READ_LOCK(); Mlt::Properties source(m_producer->get_properties()); diff --git a/src/timeline2/model/clipmodel.hpp b/src/timeline2/model/clipmo= del.hpp index 7026e92fc..9c6b32ad8 100644 --- a/src/timeline2/model/clipmodel.hpp +++ b/src/timeline2/model/clipmodel.hpp @@ -57,16 +57,14 @@ public: @param binClip is the id of the bin clip associated @param id Requested id of the clip. Automatic if -1 */ - static int construct(const std::shared_ptr &parent, con= st QString &binClipId, int id =3D -1, - PlaylistState::ClipState state =3D PlaylistState:= :Original); - /* @brief Creates a clip from an instance in MLT's playlist, + static int construct(const std::shared_ptr &parent, con= st QString &binClipId, int id, PlaylistState state); + + /* @brief Creates a clip, which references itself to the parent timeli= ne Returns the (unique) id of the created clip - @param parent is a pointer to the timeline - @param binClip is the id of the bin clip associated - @param producer is the producer to be inserted - @param id Requested id of the clip. Automatic if -1 + This variants assumes a producer is already known, which should typica= lly happen only at loading time. + Note that there is no guarantee that this producer is actually going t= o be used. It might be discarded. */ - static int construct(const std::shared_ptr &parent, con= st QString &binClipId, std::shared_ptr producer, int id =3D = -1); + static int construct(const std::shared_ptr &parent, con= st QString &binClipId, std::shared_ptr producer, PlaylistSta= te state); = /* @brief returns a property of the clip, or from it's parent if it's = a cut */ @@ -76,10 +74,11 @@ public: QSize getFrameSize() const; Q_INVOKABLE bool showKeyframes() const; Q_INVOKABLE void setShowKeyframes(bool show); + /** @brief Returns the timeline clip status (video / audio only) */ - PlaylistState::ClipState clipState() const; + PlaylistState clipState() const; /** @brief Sets the timeline clip status (video / audio only) */ - bool setClipState(PlaylistState::ClipState state); + bool setClipState(PlaylistState state); = /* @brief returns the length of the item on the timeline */ @@ -96,7 +95,11 @@ public: = bool addEffect(const QString &effectId); bool copyEffect(std::shared_ptr stackModel, int rowI= d); + /* @brief Import effects from a different stackModel */ bool importEffects(std::shared_ptr stackModel); + /* @brief Import effects from a service that contains some (another cl= ip?) */ + bool importEffects(std::weak_ptr service); + bool removeFade(bool fromStart); /** @brief Adjust effects duration. Should be called after each resize= / cut operation */ bool adjustEffectLength(bool adjustFromEnd, int oldIn, int newIn, int = oldDuration, int duration, Fun &undo, Fun &redo, bool logUndo); @@ -132,7 +135,8 @@ protected: void setTimelineEffectsEnabled(bool enabled); = /* @brief This functions should be called when the producer of the bin= Clip changes, to allow refresh */ - void refreshProducerFromBin(PlaylistState::ClipState state =3D Playlis= tState::Original); + void refreshProducerFromBin(PlaylistState state); + void refreshProducerFromBin(); /* @brief This functions replaces the current producer with a slowmoti= on one */ bool useTimewarpProducer(double speed, int extraSpace, Fun &undo, Fun = &redo); Fun useTimewarpProducer_lambda(double speed, int extraSpace); @@ -140,7 +144,7 @@ protected: /** @brief Returns the marker model associated with this clip */ std::shared_ptr getMarkerModel() const; = - bool hasAudio() const; + bool audioEnabled() const; bool isAudioOnly() const; double getSpeed() const; = @@ -154,6 +158,8 @@ protected: bool m_endlessResize; // Whether this clip can be freely resized = bool forceThumbReload; // Used to trigger a forced thumb reload, when = producer changes + + PlaylistState m_currentState; }; = #endif diff --git a/src/timeline2/model/timelinefunctions.cpp b/src/timeline2/mode= l/timelinefunctions.cpp index 2b81beee9..2ba24fc94 100644 --- a/src/timeline2/model/timelinefunctions.cpp +++ b/src/timeline2/model/timelinefunctions.cpp @@ -31,7 +31,7 @@ along with this program. If not, see . #include #include = -bool TimelineFunctions::copyClip(std::shared_ptr timeli= ne, int clipId, int &newId, PlaylistState::ClipState state, Fun &undo, Fun = &redo) +bool TimelineFunctions::copyClip(std::shared_ptr timeli= ne, int clipId, int &newId, PlaylistState state, Fun &undo, Fun &redo) { // Special case: slowmotion clips double clipSpeed =3D timeline->m_allClips[clipId]->getSpeed(); @@ -94,7 +94,7 @@ bool TimelineFunctions::processClipCut(std::shared_ptr timeli if (start > position || (start + duration) < position) { return false; } - PlaylistState::ClipState state =3D timeline->m_allClips[clipId]->clipS= tate(); + PlaylistState state =3D timeline->m_allClips[clipId]->clipState(); bool res =3D copyClip(timeline, clipId, newId, state, undo, redo); res =3D res && timeline->requestItemResize(clipId, position - start, t= rue, true, undo, redo); int newDuration =3D timeline->getClipPlaytime(clipId); @@ -344,7 +344,7 @@ bool TimelineFunctions::requestItemCopy(std::shared_ptr= timel for (int id : allIds) { int newId =3D -1; if (timeline->isClip(id)) { - PlaylistState::ClipState state =3D timeline->m_allClips[id]->c= lipState(); + PlaylistState state =3D timeline->m_allClips[id]->clipState(); res =3D copyClip(timeline, id, newId, state, undo, redo); res =3D res && (newId !=3D -1); } @@ -396,9 +396,9 @@ void TimelineFunctions::showCompositionKeyframes(std::s= hared_ptrdataChanged(modelIndex, modelIndex, {TimelineModel::Keyframe= sRole}); } = -bool TimelineFunctions::changeClipState(std::shared_ptr= timeline, int clipId, PlaylistState::ClipState status) +bool TimelineFunctions::changeClipState(std::shared_ptr= timeline, int clipId, PlaylistState status) { - PlaylistState::ClipState oldState =3D timeline->m_allClips[clipId]->cl= ipState(); + PlaylistState oldState =3D timeline->m_allClips[clipId]->clipState(); if (oldState =3D=3D status) { return true; } @@ -411,9 +411,9 @@ bool TimelineFunctions::changeClipState(std::shared_ptr= timel return result; } = -bool TimelineFunctions::changeClipState(std::shared_ptr= timeline, int clipId, PlaylistState::ClipState status, Fun &undo, Fun &red= o) +bool TimelineFunctions::changeClipState(std::shared_ptr= timeline, int clipId, PlaylistState status, Fun &undo, Fun &redo) { - PlaylistState::ClipState oldState =3D timeline->m_allClips[clipId]->cl= ipState(); + PlaylistState oldState =3D timeline->m_allClips[clipId]->clipState(); if (oldState =3D=3D status) { return true; } @@ -463,7 +463,7 @@ bool TimelineFunctions::requestSplitAudio(std::shared_p= tr tim // Now clear selection so we don't mess with groups pCore->clearSelection(); for (int cid : clips) { - if (!timeline->getClipPtr(cid)->hasAudio() || timeline->getClipPtr= (cid)->clipState() =3D=3D PlaylistState::AudioOnly) { + if (!timeline->getClipPtr(cid)->audioEnabled() || timeline->getCli= pPtr(cid)->clipState() =3D=3D PlaylistState::AudioOnly) { // clip without audio or audio only, skip continue; } diff --git a/src/timeline2/model/timelinefunctions.hpp b/src/timeline2/mode= l/timelinefunctions.hpp index 21fe11bd5..5efd9723b 100644 --- a/src/timeline2/model/timelinefunctions.hpp +++ b/src/timeline2/model/timelinefunctions.hpp @@ -50,7 +50,7 @@ struct TimelineFunctions static bool processClipCut(std::shared_ptr timeline= , int clipId, int position, int &newId, Fun &undo, Fun &redo); = /* @brief Makes a perfect copy of a given clip, but do not insert it */ - static bool copyClip(std::shared_ptr timeline, int = clipId, int &newId, PlaylistState::ClipState state, Fun &undo, Fun &redo); + static bool copyClip(std::shared_ptr timeline, int = clipId, int &newId, PlaylistState state, Fun &undo, Fun &redo); = /* @brief Request the addition of multiple clips to the timeline * If the addition of any of the clips fails, the entire operation is = undone. @@ -80,10 +80,10 @@ struct TimelineFunctions * @param status: target status of the clip This function creates an undo object and returns true on success */ - static bool changeClipState(std::shared_ptr timelin= e, int clipId, PlaylistState::ClipState status); + static bool changeClipState(std::shared_ptr timelin= e, int clipId, PlaylistState status); /* @brief Same function as above, but accumulates for undo/redo */ - static bool changeClipState(std::shared_ptr timelin= e, int clipId, PlaylistState::ClipState status, Fun &undo, Fun &redo); + static bool changeClipState(std::shared_ptr timelin= e, int clipId, PlaylistState status, Fun &undo, Fun &redo); = static bool requestSplitAudio(std::shared_ptr timel= ine, int clipId, int audioTarget); static void setCompositionATrack(std::shared_ptr ti= meline, int cid, int aTrack); diff --git a/src/timeline2/model/timelineitemmodel.cpp b/src/timeline2/mode= l/timelineitemmodel.cpp index d395ac25e..6c8ccf0f3 100644 --- a/src/timeline2/model/timelineitemmodel.cpp +++ b/src/timeline2/model/timelineitemmodel.cpp @@ -256,7 +256,7 @@ QVariant TimelineItemModel::data(const QModelIndex &ind= ex, int role) const case AudioLevelsRole: return clip->getAudioWaveform(); case HasAudio: - return clip->hasAudio(); + return clip->audioEnabled(); case IsAudioRole: return clip->isAudioOnly(); case MarkersRole: { @@ -266,7 +266,7 @@ QVariant TimelineItemModel::data(const QModelIndex &ind= ex, int role) const return QVariant::fromValue(clip->getKeyframeM= odel()); } case StatusRole: - return clip->clipState(); + return QVariant::fromValue(clip->clipState()); case StartRole: return clip->getPosition(); case DurationRole: diff --git a/src/timeline2/model/timelinemodel.cpp b/src/timeline2/model/ti= melinemodel.cpp index 627318da6..833b6628f 100644 --- a/src/timeline2/model/timelinemodel.cpp +++ b/src/timeline2/model/timelinemodel.cpp @@ -553,7 +553,7 @@ bool TimelineModel::requestClipInsertion(const QString = &binClipId, int trackId, return result; } = -bool TimelineModel::requestClipCreation(const QString &binClipId, int &id,= PlaylistState::ClipState state, Fun &undo, Fun &redo) +bool TimelineModel::requestClipCreation(const QString &binClipId, int &id,= PlaylistState state, Fun &undo, Fun &redo) { int clipId =3D TimelineModel::getNextId(); id =3D clipId; @@ -658,7 +658,8 @@ bool TimelineModel::requestClipInsertion(const QString = &binClipId, int trackId, } } } else { - res =3D requestClipCreation(binClipId, id, PlaylistState::Original= , local_undo, local_redo); + std::shared_ptr binClip =3D pCore->projectItemModel()= ->getClipByBinID(binClipId); + res =3D requestClipCreation(binClipId, id, binClip->defaultState()= , local_undo, local_redo); res =3D res && requestClipMove(id, trackId, position, refreshView,= logUndo, local_undo, local_redo); } if (!res) { diff --git a/src/timeline2/model/timelinemodel.hpp b/src/timeline2/model/ti= melinemodel.hpp index 50c8bf5c1..b7c0bb5fb 100644 --- a/src/timeline2/model/timelinemodel.hpp +++ b/src/timeline2/model/timelinemodel.hpp @@ -317,7 +317,7 @@ public: @param id: return parameter for the id of the newly created clip. @param state: The desired clip state (original, audio/video only). */ - bool requestClipCreation(const QString &binClipId, int &id, PlaylistSt= ate::ClipState state, Fun &undo, Fun &redo); + bool requestClipCreation(const QString &binClipId, int &id, PlaylistSt= ate state, Fun &undo, Fun &redo); = /* @brief Deletes the given clip or composition from the timeline This action is undoable Returns true on success. If it fails, nothing is diff --git a/src/timeline2/view/timelinecontroller.cpp b/src/timeline2/view= /timelinecontroller.cpp index 52e470b06..d8886e84d 100644 --- a/src/timeline2/view/timelinecontroller.cpp +++ b/src/timeline2/view/timelinecontroller.cpp @@ -1464,9 +1464,9 @@ void TimelineController::showCompositionKeyframes(int= clipId, bool value) TimelineFunctions::showCompositionKeyframes(m_model, clipId, value); } = -void TimelineController::setClipStatus(int clipId, int status) +void TimelineController::setClipStatus(int clipId, PlaylistState status) { - TimelineFunctions::changeClipState(m_model, clipId, (PlaylistState::Cl= ipState)status); + TimelineFunctions::changeClipState(m_model, clipId, status); } = void TimelineController::splitAudio(int clipId) diff --git a/src/timeline2/view/timelinecontroller.h b/src/timeline2/view/t= imelinecontroller.h index 34955f474..c66e0e81d 100644 --- a/src/timeline2/view/timelinecontroller.h +++ b/src/timeline2/view/timelinecontroller.h @@ -252,7 +252,7 @@ public: Q_INVOKABLE void removeSpace(int trackId =3D -1, int frame =3D -1, boo= l affectAllTracks =3D false); /* @brief Change a clip status (normal / audio only / video only) */ - Q_INVOKABLE void setClipStatus(int clipId, int status); + Q_INVOKABLE void setClipStatus(int clipId, PlaylistState status); = Q_INVOKABLE void requestClipCut(int clipId, int position); = diff --git a/tests/groupstest.cpp b/tests/groupstest.cpp index 126455a85..270d386f2 100644 --- a/tests/groupstest.cpp +++ b/tests/groupstest.cpp @@ -482,11 +482,11 @@ TEST_CASE("Undo/redo", "[GroupsModel]") = std::vector clips; for (int i =3D 0; i < 4; i++) { - clips.push_back(ClipModel::construct(timeline, binId)); + clips.push_back(ClipModel::construct(timeline, binId, -1, Playlist= State::VideoOnly)); } std::vector clips2; for (int i =3D 0; i < 4; i++) { - clips2.push_back(ClipModel::construct(timeline2, binId)); + clips2.push_back(ClipModel::construct(timeline2, binId, -1, Playli= stState::VideoOnly)); } int tid1 =3D TrackModel::construct(timeline); int tid2 =3D TrackModel::construct(timeline); diff --git a/tests/modeltest.cpp b/tests/modeltest.cpp index 363de3cf6..fbc42e62c 100644 --- a/tests/modeltest.cpp +++ b/tests/modeltest.cpp @@ -143,15 +143,15 @@ TEST_CASE("Basic creation/deletion of a clip", "[Clip= Model]") QString binId2 =3D createProducer(profile_model, "green", binModel); = REQUIRE(timeline->getClipsCount() =3D=3D 0); - int id1 =3D ClipModel::construct(timeline, binId); + int id1 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::V= ideoOnly); REQUIRE(timeline->getClipsCount() =3D=3D 1); REQUIRE(timeline->checkConsistency()); = - int id2 =3D ClipModel::construct(timeline, binId2); + int id2 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState::= VideoOnly); REQUIRE(timeline->getClipsCount() =3D=3D 2); REQUIRE(timeline->checkConsistency()); = - int id3 =3D ClipModel::construct(timeline, binId); + int id3 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::V= ideoOnly); REQUIRE(timeline->getClipsCount() =3D=3D 3); REQUIRE(timeline->checkConsistency()); = @@ -203,13 +203,13 @@ TEST_CASE("Clip manipulation", "[ClipModel]") QString binId2 =3D createProducer(profile_model, "blue", binModel); QString binId3 =3D createProducer(profile_model, "green", binModel); = - int cid1 =3D ClipModel::construct(timeline, binId); + int cid1 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); int tid1 =3D TrackModel::construct(timeline); int tid2 =3D TrackModel::construct(timeline); int tid3 =3D TrackModel::construct(timeline); - int cid2 =3D ClipModel::construct(timeline, binId2); - int cid3 =3D ClipModel::construct(timeline, binId3); - int cid4 =3D ClipModel::construct(timeline, binId2); + int cid2 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState:= :VideoOnly); + int cid3 =3D ClipModel::construct(timeline, binId3, -1, PlaylistState:= :VideoOnly); + int cid4 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState:= :VideoOnly); = Verify(Method(timMock, _resetView)).Exactly(3_Times); RESET(timMock); @@ -763,7 +763,7 @@ TEST_CASE("Clip manipulation", "[ClipModel]") SECTION("Group move with non-consecutive track ids") { int tid5 =3D TrackModel::construct(timeline); - int cid6 =3D ClipModel::construct(timeline, binId); + int cid6 =3D ClipModel::construct(timeline, binId, -1, PlaylistSta= te::VideoOnly); int tid6 =3D TrackModel::construct(timeline); REQUIRE(tid5 + 1 !=3D tid6); = @@ -785,7 +785,7 @@ TEST_CASE("Clip manipulation", "[ClipModel]") = SECTION("Clip copy") { - int cid6 =3D ClipModel::construct(timeline, binId); + int cid6 =3D ClipModel::construct(timeline, binId, -1, PlaylistSta= te::VideoOnly); int l =3D timeline->getClipPlaytime(cid6); REQUIRE(timeline->requestItemResize(cid6, l - 3, true, true, -1) = =3D=3D l - 3); REQUIRE(timeline->requestItemResize(cid6, l - 7, false, true, -1) = =3D=3D l - 7); @@ -794,7 +794,7 @@ TEST_CASE("Clip manipulation", "[ClipModel]") = std::function undo =3D []() { return true; }; std::function redo =3D []() { return true; }; - REQUIRE(TimelineFunctions::copyClip(timeline, cid6, newId, Playlis= tState::Original, undo, redo)); + REQUIRE(TimelineFunctions::copyClip(timeline, cid6, newId, Playlis= tState::VideoOnly, undo, redo)); REQUIRE(timeline->m_allClips[cid6]->binId() =3D=3D timeline->m_all= Clips[newId]->binId()); // TODO check effects } @@ -842,7 +842,7 @@ TEST_CASE("Check id unicity", "[ClipModel]") track_ids.push_back(tid); REQUIRE(timeline->getTracksCount() =3D=3D track_ids.size()); } else { - int cid =3D ClipModel::construct(timeline, binId); + int cid =3D ClipModel::construct(timeline, binId, -1, Playlist= State::VideoOnly); REQUIRE(all_ids.count(cid) =3D=3D 0); all_ids.insert(cid); REQUIRE(timeline->getClipsCount() =3D=3D all_ids.size() - trac= k_ids.size()); @@ -882,10 +882,10 @@ TEST_CASE("Undo and Redo", "[ClipModel]") QString binId =3D createProducer(profile_model, "red", binModel); QString binId2 =3D createProducer(profile_model, "blue", binModel); = - int cid1 =3D ClipModel::construct(timeline, binId); + int cid1 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); int tid1 =3D TrackModel::construct(timeline); int tid2 =3D TrackModel::construct(timeline); - int cid2 =3D ClipModel::construct(timeline, binId2); + int cid2 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState:= :VideoOnly); = timeline->m_allClips[cid1]->m_endlessResize =3D false; timeline->m_allClips[cid2]->m_endlessResize =3D false; @@ -900,7 +900,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]") int temp; Fun undo =3D []() { return true; }; Fun redo =3D []() { return true; }; - REQUIRE_FALSE(timeline->requestClipCreation("impossible bin id= ", temp, PlaylistState::Original, undo, redo)); + REQUIRE_FALSE(timeline->requestClipCreation("impossible bin id= ", temp, PlaylistState::VideoOnly, undo, redo)); } = auto state0 =3D [&]() { @@ -914,7 +914,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]") { Fun undo =3D []() { return true; }; Fun redo =3D []() { return true; }; - REQUIRE(timeline->requestClipCreation(binId3, cid3, PlaylistSt= ate::Original, undo, redo)); + REQUIRE(timeline->requestClipCreation(binId3, cid3, PlaylistSt= ate::VideoOnly, undo, redo)); pCore->pushUndo(undo, redo, QString()); } = @@ -931,7 +931,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]") { Fun undo =3D []() { return true; }; Fun redo =3D []() { return true; }; - REQUIRE(timeline->requestClipCreation(binId4, cid4, PlaylistSt= ate::Original, undo, redo)); + REQUIRE(timeline->requestClipCreation(binId4, cid4, PlaylistSt= ate::VideoOnly, undo, redo)); pCore->pushUndo(undo, redo, QString()); } = @@ -1358,7 +1358,7 @@ TEST_CASE("Undo and Redo", "[ClipModel]") { std::function undo =3D []() { return true; }; std::function redo =3D []() { return true; }; - REQUIRE(timeline->requestClipCreation(binId, cid6, PlaylistSta= te::Original, undo, redo)); + REQUIRE(timeline->requestClipCreation(binId, cid6, PlaylistSta= te::VideoOnly, undo, redo)); pCore->pushUndo(undo, redo, QString()); } int l =3D timeline->getClipPlaytime(cid6); @@ -1463,10 +1463,10 @@ TEST_CASE("Snapping", "[Snapping]") QString binId2 =3D createProducer(profile_model, "blue", binModel); = int tid1 =3D TrackModel::construct(timeline); - int cid1 =3D ClipModel::construct(timeline, binId); + int cid1 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); int tid2 =3D TrackModel::construct(timeline); - int cid2 =3D ClipModel::construct(timeline, binId2); - int cid3 =3D ClipModel::construct(timeline, binId2); + int cid2 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState:= :VideoOnly); + int cid3 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState:= :VideoOnly); = timeline->m_allClips[cid1]->m_endlessResize =3D false; timeline->m_allClips[cid2]->m_endlessResize =3D false; @@ -1582,20 +1582,20 @@ TEST_CASE("Advanced trimming operations", "[Trimmin= g]") QString binId2 =3D createProducer(profile_model, "blue", binModel); QString binId3 =3D createProducerWithSound(profile_model, binModel); = - int cid1 =3D ClipModel::construct(timeline, binId); + int cid1 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); int tid1 =3D TrackModel::construct(timeline); int tid2 =3D TrackModel::construct(timeline); int tid3 =3D TrackModel::construct(timeline); - int cid2 =3D ClipModel::construct(timeline, binId2); - int cid3 =3D ClipModel::construct(timeline, binId); - int cid4 =3D ClipModel::construct(timeline, binId); - int cid5 =3D ClipModel::construct(timeline, binId); - int cid6 =3D ClipModel::construct(timeline, binId); - int cid7 =3D ClipModel::construct(timeline, binId); - - int audio1 =3D ClipModel::construct(timeline, binId3); - int audio2 =3D ClipModel::construct(timeline, binId3); - int audio3 =3D ClipModel::construct(timeline, binId3); + int cid2 =3D ClipModel::construct(timeline, binId2, -1, PlaylistState:= :VideoOnly); + int cid3 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); + int cid4 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); + int cid5 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); + int cid6 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); + int cid7 =3D ClipModel::construct(timeline, binId, -1, PlaylistState::= VideoOnly); + + int audio1 =3D ClipModel::construct(timeline, binId3, -1, PlaylistStat= e::VideoOnly); + int audio2 =3D ClipModel::construct(timeline, binId3, -1, PlaylistStat= e::VideoOnly); + int audio3 =3D ClipModel::construct(timeline, binId3, -1, PlaylistStat= e::VideoOnly); = timeline->m_allClips[cid1]->m_endlessResize =3D false; timeline->m_allClips[cid2]->m_endlessResize =3D false; diff --git a/tests/regressions.cpp b/tests/regressions.cpp index 1b755ab30..9d84262e6 100644 --- a/tests/regressions.cpp +++ b/tests/regressions.cpp @@ -31,7 +31,7 @@ TEST_CASE("Regression") undoStack->redo(); undoStack->undo(); QString binId0 =3D createProducer(reg_profile, "red", binModel); - int c =3D ClipModel::construct(timeline, binId0); + int c =3D ClipModel::construct(timeline, binId0, -1, PlaylistState::Vi= deoOnly); timeline->m_allClips[c]->m_endlessResize =3D false; TrackModel::construct(timeline); REQUIRE(timeline->getTrackById(1)->checkConsistency()); @@ -144,7 +144,7 @@ TEST_CASE("Regression2") REQUIRE(timeline->getTrackById(4)->checkConsistency()); { QString binId0 =3D createProducer(reg_profile, "red", binModel); - int c =3D ClipModel::construct(timeline, binId0); + int c =3D ClipModel::construct(timeline, binId0, -1, PlaylistState= ::VideoOnly); timeline->m_allClips[c]->m_endlessResize =3D false; } REQUIRE(timeline->getTrackById(0)->checkConsistency());