<parameter type="bool" name="invert" default="0">
<name>Invert</name>
</parameter>
+
+ <parameter type="bool" name="track" default="0">
+ <name>Track</name>
+ </parameter>
<parameter type="constant" name="feather" max="500" min="0" default="0">
<name>Feather width</name>
}
}
-void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, int out, bool isEffect)
+void EffectStackEdit::transferParamDesc(const QDomElement d, ItemInfo info, bool isEffect)
{
clearAllItems();
if (m_keyframeEditor) delete m_keyframeEditor;
m_keyframeEditor = NULL;
m_params = d;
- m_in = in;
- m_out = out;
+ m_in = isEffect ? info.cropStart.frames(KdenliveSettings::project_fps()) : info.startPos.frames(KdenliveSettings::project_fps());
+ m_out = isEffect ? (info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1 : info.endPos.frames(KdenliveSettings::project_fps());
if (m_params.isNull()) {
// kDebug() << "// EMPTY EFFECT STACK";
return;
connect(pl, SIGNAL(parameterChanged()), this, SLOT(collectAllParameters()));
} else if (type == "geometry") {
if (KdenliveSettings::on_monitor_effects()) {
- m_geometryWidget = new GeometryWidget(m_monitor, m_timecode, pos, isEffect, m_params.hasAttribute("showrotation"), this);
+ m_geometryWidget = new GeometryWidget(m_monitor, m_timecode, isEffect ? 0 : qMax(0, (int)info.startPos.frames(KdenliveSettings::project_fps())), isEffect, m_params.hasAttribute("showrotation"), this);
m_geometryWidget->setFrameSize(m_frameSize);
m_geometryWidget->slotShowScene(!disable);
// connect this before setupParam to make sure the monitor scene shows up at startup
connect(this, SIGNAL(syncEffectsPos(int)), m_geometryWidget, SLOT(slotSyncPosition(int)));
connect(this, SIGNAL(effectStateChanged(bool)), m_geometryWidget, SLOT(slotShowScene(bool)));
} else {
- Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, pos);
+ Geometryval *geo = new Geometryval(m_profile, m_timecode, m_frameSize, isEffect ? 0 : qMax(0, (int)info.startPos.frames(KdenliveSettings::project_fps())));
if (minFrame == maxFrame)
geo->setupParam(pa, m_in, m_out);
else
meetDependency(paramName, type, EffectsList::parameter(e, depends));
#ifdef QJSON
} else if (type == "roto-spline") {
- RotoWidget *roto = new RotoWidget(value, m_monitor, m_in, m_out, m_timecode, this);
+ RotoWidget *roto = new RotoWidget(value, m_monitor, info, m_timecode, this);
roto->slotShowScene(!disable);
connect(roto, SIGNAL(valueChanged()), this, SLOT(collectAllParameters()));
connect(roto, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int)));
public slots:
/** @brief Called when an effect is selected, builds the UI for this effect. */
- void transferParamDesc(const QDomElement d, int pos, int in, int out, bool isEffect = true);
+ void transferParamDesc(const QDomElement d, ItemInfo info, bool isEffect = true);
/** @brief Called whenever(?) some parameter is changed in the gui.
*
if (m_clipref == NULL) {
m_ui.effectlist->blockSignals(true);
m_ui.effectlist->clear();
- m_effectedit->transferParamDesc(QDomElement(), 0, 0, 0);
+ ItemInfo info;
+ m_effectedit->transferParamDesc(QDomElement(), info);
//m_ui.region_url->clear();
m_ui.effectlist->blockSignals(false);
m_ui.checkAll->setToolTip(QString());
}
m_ui.effectlist->blockSignals(false);
if (m_ui.effectlist->count() == 0) {
- m_effectedit->transferParamDesc(QDomElement(), 0, 0, 0);
+ ItemInfo info;
+ m_effectedit->transferParamDesc(QDomElement(), info);
//m_ui.region_url->clear();
} else slotItemSelectionChanged(false);
slotUpdateCheckAllButton();
eff = m_currentEffectList.at(activeRow);
if (m_trackMode) {
// showing track effects
- m_effectedit->transferParamDesc(eff, 0, 0, m_trackInfo.duration);
- } else m_effectedit->transferParamDesc(eff,
- 0,
- m_clipref->cropStart().frames(KdenliveSettings::project_fps()),
- (m_clipref->cropStart() + m_clipref->cropDuration()).frames(KdenliveSettings::project_fps()) - 1); //minx max frame
+ ItemInfo info;
+ info.track = m_trackInfo.type;
+ info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
+ info.cropStart = GenTime(0);
+ info.startPos = GenTime(-1);
+ info.track = 0;
+ m_effectedit->transferParamDesc(eff, info);
+ } else {
+ m_effectedit->transferParamDesc(eff, m_clipref->info());
+ }
//m_ui.region_url->setUrl(KUrl(eff.attribute("region")));
m_ui.labelComment->setText(i18n(eff.firstChildElement("description").firstChildElement("full").text().toUtf8().data()));
}
if (m_trackMode) {
EffectsList::setParameter(dom, "in", QString::number(0));
EffectsList::setParameter(dom, "out", QString::number(m_trackInfo.duration));
- m_effectedit->transferParamDesc(dom, 0, 0, m_trackInfo.duration);//minx max frame
+ ItemInfo info;
+ info.track = m_trackInfo.type;
+ info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
+ info.cropStart = GenTime(0);
+ info.startPos = GenTime(-1);
+ info.track = 0;
+ m_effectedit->transferParamDesc(dom, info);
emit updateEffect(NULL, m_trackindex, old, dom, activeRow);
} else {
m_clipref->initEffect(dom);
- m_effectedit->transferParamDesc(dom, 0, m_clipref->cropStart().frames(KdenliveSettings::project_fps()), (m_clipref->cropStart() + m_clipref->cropDuration()).frames(KdenliveSettings::project_fps()));//minx max frame
+ m_effectedit->transferParamDesc(dom, m_clipref->info());
//m_ui.region_url->setUrl(KUrl(dom.attribute("region")));
emit updateEffect(m_clipref, -1, old, dom, activeRow);
}
m_ui.buttonUp->setEnabled(false);
m_ui.buttonDown->setEnabled(false);
m_ui.checkAll->setEnabled(false);
- m_effectedit->transferParamDesc(QDomElement(), 0, 0, 0);
+ ItemInfo info;
+ m_effectedit->transferParamDesc(QDomElement(), info);
//m_ui.region_url->clear();
m_ui.buttonShowComments->setEnabled(false);
m_ui.labelComment->setText(QString());
}
}
+Mlt::Producer* Render::getProducer()
+{
+ return m_mltProducer;
+}
+
#include "renderer.moc"
QList <int> checkTrackSequence(int);
void sendFrameUpdate();
+ /** @brief Returns a pointer to the main producer. */
+ Mlt::Producer *getProducer();
+
private:
/** @brief The name of this renderer.
#include "simplekeyframes/simplekeyframewidget.h"
#include "kdenlivesettings.h"
+#include <mlt++/Mlt.h>
+
#include <math.h>
#include <qjson/parser.h>
#include <QVBoxLayout>
+/** @brief Listener for "tracking-finished" event in MLT rotoscoping filter. */
+void tracking_finished(mlt_service *owner, RotoWidget *self, char *data)
+{
+ Q_UNUSED(owner)
-RotoWidget::RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent) :
+ if (self)
+ self->setSpline(QString(data));
+}
+
+RotoWidget::RotoWidget(QString data, Monitor *monitor, ItemInfo info, Timecode t, QWidget* parent) :
QWidget(parent),
m_monitor(monitor),
m_showScene(true),
- m_in(in),
- m_out(out)
+ m_in(info.cropStart.frames(KdenliveSettings::project_fps())),
+ m_out((info.cropStart + info.cropDuration).frames(KdenliveSettings::project_fps()) - 1),
+ m_filter(NULL)
{
QVBoxLayout *l = new QVBoxLayout(this);
m_keyframeWidget = new SimpleKeyframeWidget(t, m_out - m_in, this);
edit->showVisibilityButton(true);
m_scene = edit->getScene();
- QJson::Parser parser;
- bool ok;
- m_data = parser.parse(data.toUtf8(), &ok);
- if (!ok) {
- // :(
- }
-
-
- if (m_data.canConvert(QVariant::Map)) {
- /*
- * pass keyframe data to keyframe timeline
- */
- QList <int> keyframes;
- QMap <QString, QVariant> map = m_data.toMap();
- QMap <QString, QVariant>::const_iterator i = map.constBegin();
- while (i != map.constEnd()) {
- keyframes.append(i.key().toInt() - m_in);
- ++i;
- }
- m_keyframeWidget->setKeyframes(keyframes);
-
- for (int j = 0; j < keyframes.count(); ++j) {
- // key might already be justified
- if (map.contains(QString::number(keyframes.at(j) + m_in))) {
- QVariant value = map.take(QString::number(keyframes.at(j) + m_in));
- map[QString::number(keyframes.at(j) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = value;
- }
- }
- m_data = QVariant(map);
- } else {
- // static (only one keyframe)
- m_keyframeWidget->setKeyframes(QList <int>() << 0);
- }
-
m_item = new SplineItem(QList <BPoint>(), NULL, m_scene);
connect(m_item, SIGNAL(changed(bool)), this, SLOT(slotUpdateData(bool)));
connect(m_keyframeWidget, SIGNAL(keyframeMoved(int,int)), this, SLOT(slotMoveKeyframe(int,int)));
connect(m_scene, SIGNAL(addKeyframe()), this, SLOT(slotAddKeyframe()));
- slotPositionChanged(0, false);
+ setSpline(data, false);
+ setupTrackingListen(info);
}
RotoWidget::~RotoWidget()
{
+ if (m_filter)
+ mlt_events_disconnect(m_filter->get_properties(), this);
+
delete m_keyframeWidget;
m_scene->removeItem(m_item);
data = m_data.toMap()[QString::number(keyframe).rightJustified(log10((double)m_out) + 1, '0')].toList();
else
data = m_data.toList();
+
+ // skip tracking flag
+ if (data.count() && data.at(0).canConvert(QVariant::String))
+ data.removeFirst();
+
foreach (const QVariant &bpoint, data) {
QList <QVariant> l = bpoint.toList();
BPoint p;
m_keyframeWidget->updateTimecodeFormat();
}
+void RotoWidget::keyframeTimelineFullUpdate()
+{
+ if (m_data.canConvert(QVariant::Map)) {
+ QList <int> keyframes;
+ QMap <QString, QVariant> map = m_data.toMap();
+ QMap <QString, QVariant>::const_iterator i = map.constBegin();
+ while (i != map.constEnd()) {
+ keyframes.append(i.key().toInt() - m_in);
+ ++i;
+ }
+ m_keyframeWidget->setKeyframes(keyframes);
+
+ /*for (int j = 0; j < keyframes.count(); ++j) {
+ // key might already be justified
+ if (map.contains(QString::number(keyframes.at(j) + m_in))) {
+ QVariant value = map.take(QString::number(keyframes.at(j) + m_in));
+ map[QString::number(keyframes.at(j) + m_in).rightJustified(log10((double)m_out) + 1, '0')] = value;
+ }
+ }
+ m_data = QVariant(map);*/
+ } else {
+ // static (only one keyframe)
+ m_keyframeWidget->setKeyframes(QList <int>() << 0);
+ }
+}
+
+void RotoWidget::setupTrackingListen(ItemInfo info)
+{
+ if (info.startPos < GenTime()) {
+ // TODO: track effects
+ return;
+ }
+
+ Mlt::Service service(m_monitor->render->getProducer()->parent().get_service());
+ Mlt::Tractor tractor(service);
+ Mlt::Producer trackProducer(tractor.track(tractor.count() - info.track - 1));
+ Mlt::Playlist trackPlaylist((mlt_playlist) trackProducer.get_service());
+
+ Mlt::Producer *clip = trackPlaylist.get_clip_at((int)info.startPos.frames(KdenliveSettings::project_fps()));
+ if (!clip) {
+ return;
+ }
+
+ int i = 0;
+ Mlt::Filter *filter = clip->filter(0);
+ while (filter) {
+ if (strcmp(filter->get("kdenlive_id"), "rotoscoping") == 0) {
+ m_filter = filter;
+ filter->listen("tracking-finished", this, (mlt_listener)tracking_finished);
+ break;
+ }
+ filter = clip->filter(++i);
+ }
+
+ delete clip;
+}
+
+void RotoWidget::setSpline(QString spline, bool notify)
+{
+ QJson::Parser parser;
+ bool ok;
+ m_data = parser.parse(spline.simplified().toUtf8(), &ok);
+ if (!ok) {
+ // :(
+ }
+ keyframeTimelineFullUpdate();
+ slotPositionChanged(m_keyframeWidget->getPosition(), false);
+ if (notify)
+ emit valueChanged();
+}
static QVariant interpolate(int position, int in, int out, QVariant *splineIn, QVariant *splineOut)
QList<QVariant> keyframe1 = splineIn->toList();
QList<QVariant> keyframe2 = splineOut->toList();
QList<QVariant> keyframe;
+ if (keyframe1.count() && keyframe1.at(0).canConvert(QVariant::String))
+ keyframe1.removeFirst();
+ if (keyframe2.count() && keyframe2.at(0).canConvert(QVariant::String))
+ keyframe2.removeFirst();
int max = qMin(keyframe1.count(), keyframe2.count());
+
for (int i = 0; i < max; ++i) {
QList<QVariant> p1 = keyframe1.at(i).toList();
QList<QVariant> p2 = keyframe2.at(i).toList();
#ifndef ROTOWIDGET_H
#define ROTOWIDGET_H
+#include "definitions.h"
#include "bpoint.h"
#include "timecode.h"
class MonitorScene;
class SplineItem;
class SimpleKeyframeWidget;
+namespace Mlt
+{
+class Filter;
+}
/** @brief Adjusts keyframes after resizing a clip. */
bool adjustRotoDuration(QString *data, int in, int out);
Q_OBJECT
public:
- RotoWidget(QString data, Monitor *monitor, int in, int out, Timecode t, QWidget* parent = 0);
+ RotoWidget(QString data, Monitor *monitor, ItemInfo info, Timecode t, QWidget* parent = 0);
virtual ~RotoWidget();
/** @brief Returns the spline(s) in the JSON format used by filter_rotoscoping (MLT). */
QString getSpline();
+ /** @brief Replaces current data with \param spline (JSON). */
+ void setSpline(QString spline, bool notify = true);
+
/** @brief Passed on to the keyframe timeline. Switches between frames and hh:mm:ss:ff timecode. */
void updateTimecodeFormat();
SplineItem *m_item;
int m_in;
int m_out;
+ Mlt::Filter *m_filter;
/** @brief Returns the list of cubic Bézier points that form the spline at position @param keyframe.
* The points are brought from the range [0, 1] into project resolution space.
* Set @param keyframe to -1 if only one keyframe currently exists. */
QList <BPoint> getPoints(int keyframe);
+ /** @brief Adds tracking_finished as listener for "tracking-finished" event in MLT rotoscoping filter. */
+ void setupTrackingListen(ItemInfo info);
+
+ /** @brief Passes list of keyframe positions to keyframe timeline widget. */
+ void keyframeTimelineFullUpdate();
+
private slots:
/** @brief Makes sure the monitor effect scene is only visible if the clip this geometry belongs to is visible.
* @param renderPos Postion of the Monitor / Timeline cursor */
void TransitionSettings::slotTransitionChanged(bool reinit, bool updateCurrent)
{
QDomElement e = m_usedTransition->toXML().cloneNode().toElement();
- int start = m_usedTransition->startPos().frames(KdenliveSettings::project_fps());
- int end = m_usedTransition->endPos().frames(KdenliveSettings::project_fps());
if (reinit) {
// Reset the transition parameters to the default one
QDomElement newTransition = MainWindow::transitions.getEffectByName(transitionList->currentText()).cloneNode().toElement();
slotUpdateEffectParams(e, newTransition);
- m_effectEdit->transferParamDesc(newTransition, start, start, end, false);
+ m_effectEdit->transferParamDesc(newTransition, m_usedTransition->info(), false);
} else if (!updateCurrent) {
// Transition changed, update parameters dialog
//slotUpdateEffectParams(e, e);
- m_effectEdit->transferParamDesc(e, start, start, end, false);
+ m_effectEdit->transferParamDesc(e, m_usedTransition->info(), false);
} else {
// Same transition, we just want to update the parameters value
slotUpdateEffectParams(e, e);
- if (m_usedTransition->hasGeometry()) m_effectEdit->transferParamDesc(m_usedTransition->toXML(), start, start, end, false);
+ if (m_usedTransition->hasGeometry())
+ m_effectEdit->transferParamDesc(m_usedTransition->toXML(), m_usedTransition->info(), false);
}
}
} else {
// null transition selected
m_usedTransition = NULL;
- m_effectEdit->transferParamDesc(QDomElement(), 0, 0, 0, false);
+ ItemInfo info;
+ m_effectEdit->transferParamDesc(QDomElement(), info, false);
}
}