Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Use a new copy of the bin producer on each insertion in timeline to allow...

Use a new copy of the bin producer on each insertion in timeline to allow audio only / video only clips and avoid issues on audio mix
parent 3addf6e3
......@@ -35,6 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "model/markerlistmodel.hpp"
#include "profiles/profilemodel.hpp"
#include "project/projectcommands.h"
#include "effects/effectstack/model/effectstackmodel.hpp"
#include "project/projectmanager.h"
#include "projectfolder.h"
#include "projectitemmodel.h"
......@@ -80,6 +81,7 @@ ProjectClip::ProjectClip(const QString &id, const QIcon &thumb, std::shared_ptr<
connect(m_markerModel.get(), &MarkerListModel::modelChanged, [&](){
setProducerProperty(QStringLiteral("kdenlive:markers"), m_markerModel->toJson());
});
connectEffectStack();
QString markers = getProducerProperty(QStringLiteral("kdenlive:markers"));
if (!markers.isEmpty()) {
QMetaObject::invokeMethod(m_markerModel.get(), "importFromJson", Qt::QueuedConnection, Q_ARG(const QString &, markers), Q_ARG(bool, true), Q_ARG(bool, false));
......@@ -125,6 +127,7 @@ ProjectClip::ProjectClip(const QString &id, const QDomElement &description, cons
connect(m_markerModel.get(), &MarkerListModel::modelChanged, [&](){
setProducerProperty(QStringLiteral("kdenlive:markers"), m_markerModel->toJson());
});
connectEffectStack();
}
std::shared_ptr<ProjectClip> ProjectClip::construct(const QString &id, const QDomElement &description, const QIcon &thumb, std::shared_ptr<ProjectItemModel> model)
......@@ -147,6 +150,14 @@ ProjectClip::~ProjectClip()
audioFrameCache.clear();
}
void ProjectClip::connectEffectStack()
{
connect(m_effectStack.get(), &EffectStackModel::dataChanged, [&](QModelIndex,QModelIndex,QVector<int>){
replaceInTimeline();
});
}
void ProjectClip::abortAudioThumbs()
{
m_abortAudioThumb = true;
......@@ -392,6 +403,32 @@ Mlt::Producer *ProjectClip::thumbProducer()
return m_thumbsProducer;
}
Mlt::Producer *ProjectClip::cloneProducer()
{
Mlt::Consumer c(*m_masterProducer->profile(), "xml", "string");
Mlt::Service s(m_masterProducer->get_service());
int ignore = s.get_int("ignore_points");
if (ignore) {
s.set("ignore_points", 0);
}
c.connect(s);
c.set("time_format", "frames");
c.set("no_meta", 1);
c.set("no_root", 1);
c.set("no_profile", 1);
c.set("root", "/");
c.set("store", "kdenlive");
c.start();
if (ignore) {
s.set("ignore_points", ignore);
}
const QByteArray clipXml = c.get("string");
QScopedPointer<Mlt::Producer> master(new Mlt::Producer(*m_masterProducer->profile(), "xml-string", clipXml.constData()));
Mlt::Producer *clone = master->cut();
clone->set("kdenlive:id", m_binId.toUtf8().constData());
return clone;
}
bool ProjectClip::isReady() const
{
return m_clipStatus == StatusReady;
......
......@@ -57,6 +57,7 @@ class ProjectClip : public AbstractProjectItem, public ClipController
Q_OBJECT
public:
friend class Bin;
friend bool TimelineModel::checkConsistency(); // for testing
/**
* @brief Constructor; used when loading a project and the producer is already available.
......@@ -192,10 +193,9 @@ public:
Note that this function does not account for children, use TreeItem::accumulate if you want to get that information as well.
*/
bool isIncludedInTimeline() override;
/** @brief Replace instance of this clip in timeline */
void replaceInTimeline();
/** @brief Returns a list of all timeline clip ids for this bin clip */
QList <int> timelineInstances() const;
Mlt::Producer *cloneProducer();
protected:
friend class ClipModel;
......@@ -211,6 +211,10 @@ protected:
void deregisterTimelineClip(int clipId);
void emitProducerChanged(const QString& id, const std::shared_ptr<Mlt::Producer> &producer) override {emit producerChanged(id, producer);};
/** @brief Replace instance of this clip in timeline */
void replaceInTimeline();
void connectEffectStack();
public slots:
void updateAudioThumbnail(const QVariantList &audioLevels);
/** @brief Extract image thumbnails for timeline. */
......
......@@ -52,6 +52,10 @@ void EffectStackModel::loadEffects()
if (ptr) {
qDebug()<<"// FOUND FILTERS IN CLIP: "<<ptr->filter_count();
for (int i = 0; i < ptr->filter_count(); i++) {
if (ptr->filter(i)->get("kdenlive_id") == nullptr) {
// don't consider internal MLT stuff
continue;
}
auto effect = EffectItemModel::construct(ptr->filter(i), shared_from_this());
//effect->setParameters
qDebug()<<"// Adding effect: "<<effect->getAssetId();
......
......@@ -130,6 +130,7 @@ void ClipController::addMasterProducer(const std::shared_ptr<Mlt::Producer> &pro
emitProducerChanged(m_controllerBinId, producer);
setProducerProperty(QStringLiteral("kdenlive:id"), m_controllerBinId);
}
connectEffectStack();
}
void ClipController::getProducerXML(QDomDocument &document, bool includeMeta)
......
......@@ -203,6 +203,7 @@ public:
protected:
virtual void emitProducerChanged(const QString& , const std::shared_ptr<Mlt::Producer> &) {};
virtual void connectEffectStack() {};
std::shared_ptr<Mlt::Producer> m_masterProducer;
Mlt::Properties *m_properties;
......
......@@ -54,8 +54,9 @@ ClipModel::ClipModel(std::shared_ptr<TimelineModel> parent, std::shared_ptr<Mlt:
int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id)
{
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(binClipId);
std::shared_ptr<Mlt::Producer> originalProducer = binClip->originalProducer();
std::shared_ptr<Mlt::Producer> cutProducer(originalProducer->cut());
//std::shared_ptr<Mlt::Producer> originalProducer = binClip->originalProducer();
//std::shared_ptr<Mlt::Producer> cutProducer(originalProducer->cut());
std::shared_ptr<Mlt::Producer> cutProducer(binClip->cloneProducer());
return construct(parent, binClipId, cutProducer, id);
}
......@@ -381,3 +382,45 @@ void ClipModel::setShowKeyframes(bool show)
QWriteLocker locker(&m_lock);
service()->set("kdenlive:timeline_display", (int) show);
}
bool ClipModel::setClipState(PlaylistState::ClipState state)
{
if (!getProperty("mlt_service").startsWith(QStringLiteral("avformat"))) {
return false;
}
switch (state) {
case PlaylistState::Original:
m_producer->parent().set("audio_index", 0);
m_producer->parent().set("video_index", 0);
break;
case PlaylistState::AudioOnly:
m_producer->parent().set("video_index", -1);
m_producer->parent().set("audio_index", 0);
break;
case PlaylistState::VideoOnly:
m_producer->parent().set("audio_index", -1);
m_producer->parent().set("video_index", 0);
break;
case PlaylistState::Disabled:
m_producer->parent().set("audio_index", -1);
m_producer->parent().set("video_index", -1);
break;
default:
break;
}
return true;
}
PlaylistState::ClipState ClipModel::clipState() const
{
if (m_producer->parent().get_int("audio_index") == -1) {
if (m_producer->parent().get_int("video_index") == -1) {
return PlaylistState::Disabled;
} else {
return PlaylistState::VideoOnly;
}
} else if (m_producer->parent().get_int("video_index") == -1) {
return PlaylistState::AudioOnly;
}
return PlaylistState::Original;
}
......@@ -75,6 +75,10 @@ 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;
/** @brief Sets the timeline clip status (video / audio only) */
bool setClipState(PlaylistState::ClipState state);
/* @brief returns the length of the item on the timeline
*/
......
......@@ -65,6 +65,7 @@ bool TimelineFunctions::requestClipCut(std::shared_ptr<TimelineItemModel> timeli
res = res && timeline->requestClipMove(newId, timeline->getClipTrackId(clipId), position, true, false, undo, redo);
return res;
}
bool TimelineFunctions::requestClipCut(std::shared_ptr<TimelineItemModel> timeline, int clipId, int position)
{
std::function<bool(void)> undo = []() { return true; };
......@@ -305,4 +306,38 @@ void TimelineFunctions::showCompositionKeyframes(std::shared_ptr<TimelineItemMod
timeline->dataChanged(modelIndex, modelIndex, {TimelineModel::KeyframesRole});
}
bool TimelineFunctions::changeClipState(std::shared_ptr<TimelineItemModel> timeline, int clipId, PlaylistState::ClipState status)
{
PlaylistState::ClipState oldState = timeline->m_allClips[clipId]->clipState();
if (oldState == status) {
return false;
}
std::function<bool(void)> undo = []() { return true; };
std::function<bool(void)> redo = []() { return true; };
redo = [timeline, clipId, status]() {
bool res = timeline->m_allClips[clipId]->setClipState(status);
QModelIndex ix = timeline->makeClipIndexFromID(clipId);
timeline->dataChanged(ix, ix, {TimelineModel::StatusRole});
timeline->invalidateClip(clipId);
int start = timeline->getItemPosition(clipId);
int end = start + timeline->getItemPlaytime(clipId);
timeline->checkRefresh(start, end);
return res;
};
undo = [timeline, clipId, oldState]() {
bool res = timeline->m_allClips[clipId]->setClipState(oldState);
QModelIndex ix = timeline->makeClipIndexFromID(clipId);
timeline->dataChanged(ix, ix, {TimelineModel::StatusRole});
timeline->invalidateClip(clipId);
int start = timeline->getItemPosition(clipId);
int end = start + timeline->getItemPlaytime(clipId);
timeline->checkRefresh(start, end);
return res;
};
bool result = redo();
if (result) {
pCore->pushUndo(undo, redo, i18n("Change clip state"));
}
return result;
}
......@@ -25,6 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <memory>
#include <unordered_set>
#include "undohelper.hpp"
#include "definitions.h"
/**
* @namespace TimelineFunction
......@@ -59,6 +60,7 @@ struct TimelineFunctions {
static bool requestClipCopy(std::shared_ptr<TimelineItemModel> timeline, int clipId, int trackId, int position);
static void showClipKeyframes(std::shared_ptr<TimelineItemModel> timeline, int clipId, bool value);
static void showCompositionKeyframes(std::shared_ptr<TimelineItemModel> timeline, int compoId, bool value);
static bool changeClipState(std::shared_ptr<TimelineItemModel> timeline, int clipId, PlaylistState::ClipState status);
};
#endif
......@@ -173,6 +173,7 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles[MarkersRole] = "markers";
roles[KeyframesRole] = "keyframeModel";
roles[ShowKeyframesRole] = "showKeyframes";
roles[StatusRole] = "clipStatus";
roles[InPointRole] = "in";
roles[OutPointRole] = "out";
roles[FramerateRole] = "fps";
......@@ -251,6 +252,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
case KeyframesRole: {
return QVariant::fromValue<KeyframeModel *>(clip->getKeyframeModel());
}
case StatusRole:
return clip->clipState();
case StartRole:
return clip->getPosition();
case DurationRole:
......
......@@ -111,6 +111,7 @@ public:
StartRole, /// clip only
BinIdRole, /// clip only
MarkersRole, /// clip only
StatusRole, /// clip only
KeyframesRole,
DurationRole,
InPointRole, /// clip only
......
......@@ -41,6 +41,7 @@ Rectangle {
property var audioLevels
property var markers
property var keyframeModel
property int clipStatus
property int fadeIn: 0
property int fadeOut: 0
property int binId: 0
......@@ -248,7 +249,7 @@ Rectangle {
clip: true
Image {
id: outThumbnail
visible: timeline.showThumbnails && mltService != 'color' && !isAudio
visible: timeline.showThumbnails && mltService != 'color' && !isAudio && clipStatus < 2
opacity: trackRoot.isAudio || trackRoot.isHidden ? 0.2 : 1
anchors.top: container.top
anchors.right: container.right
......@@ -261,7 +262,7 @@ Rectangle {
Image {
id: inThumbnail
visible: timeline.showThumbnails && mltService != 'color' && !isAudio
visible: timeline.showThumbnails && mltService != 'color' && !isAudio && clipStatus < 2
opacity: trackRoot.isAudio || trackRoot.isHidden ? 0.2 : 1
anchors.left: container.left
anchors.bottom: container.bottom
......@@ -274,8 +275,8 @@ Rectangle {
Row {
id: waveform
visible: hasAudio && timeline.showAudioThumbnails && !trackRoot.isMute
height: isAudio || trackRoot.isAudio ? container.height - 1 : (container.height - 1) / 2
visible: hasAudio && timeline.showAudioThumbnails && !trackRoot.isMute && (clipStatus == 0 || clipStatus == 2)
height: isAudio || trackRoot.isAudio || clipStatus == 2 ? container.height - 1 : (container.height - 1) / 2
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: container.bottom
......@@ -734,6 +735,40 @@ Rectangle {
text: i18n('Split At Playhead')
onTriggered: timeline.triggerAction('cut_timeline_clip')
}
Menu {
title: i18n('Clip Type...')
ExclusiveGroup {
id: clipTypeGroup
}
MenuItem {
text: i18n('Original')
checkable: true
checked: clipStatus == 0
exclusiveGroup: clipTypeGroup
onTriggered: timeline.setClipStatus(clipRoot.clipId, 0)
}
MenuItem {
text: i18n('Video Only')
checkable: true
checked: clipStatus == 1
exclusiveGroup: clipTypeGroup
onTriggered: timeline.setClipStatus(clipRoot.clipId, 1)
}
MenuItem {
text: i18n('Audio Only')
checkable: true
checked: clipStatus == 2
exclusiveGroup: clipTypeGroup
onTriggered: timeline.setClipStatus(clipRoot.clipId, 2)
}
MenuItem {
text: i18n('Disabled')
checkable: true
checked: clipStatus == 3
exclusiveGroup: clipTypeGroup
onTriggered: timeline.setClipStatus(clipRoot.clipId, 3)
}
}
/*MenuItem {
id: mergeItem
text: i18n('Merge with next clip')
......
......@@ -102,6 +102,12 @@ Column{
value: model.reloadThumb
when: loader.status == Loader.Ready && loader.item.isComposition === false
}
Binding {
target: loader.item
property: "clipStatus"
value: model.clipStatus
when: loader.status == Loader.Ready && !loader.item.isComposition
}
Binding {
target: loader.item
property: "fadeOut"
......
......@@ -1044,7 +1044,6 @@ void TimelineController::invalidateClip(int cid)
}
int start = m_model->getItemPosition(cid);
int end = start + m_model->getItemPlaytime(cid);
qDebug()<<"invalid range: "<<start<<"-"<<end;
m_timelinePreview->invalidatePreview(start, end);
}
......@@ -1138,3 +1137,8 @@ void TimelineController::showCompositionKeyframes(int clipId, bool value)
{
TimelineFunctions::showCompositionKeyframes(m_model, clipId, value);
}
void TimelineController::setClipStatus(int clipId, int status)
{
TimelineFunctions::changeClipState(m_model, clipId, (PlaylistState::ClipState) status);
}
......@@ -24,6 +24,7 @@
#include "timeline2/model/timelineitemmodel.hpp"
#include "timelinewidget.h"
#include "definitions.h"
class PreviewManager;
class QAction;
......@@ -219,7 +220,9 @@ public:
*/
Q_INVOKABLE void insertSpace(int trackId = -1, int frame = -1);
Q_INVOKABLE void removeSpace(int trackId = -1, int frame = -1, bool affectAllTracks = false);
/* @brief Change a clip status (normal / audio only / video only)
*/
Q_INVOKABLE void setClipStatus(int clipId, int status);
const QString getTrackNameFromIndex(int trackIndex);
/* @brief Seeks to selected clip start / end
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment