Commit 4df5f2d7 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Create a new ClipController class that handles communication with clip's bin, fix various crashes

parent 6cc0d26d
......@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "monitor/monitor.h"
#include "doc/kdenlivedoc.h"
#include "core.h"
#include "mltcontroller/clipcontroller.h"
#include "projectsortproxymodel.h"
#include "mlt++/Mlt.h"
......@@ -229,6 +230,7 @@ void Bin::deleteClip(const QString &id)
{
ProjectClip *clip = m_rootFolder->clip(id);
if (!clip) return;
m_jobManager->discardJobs(id);
m_rootFolder->removeChild(clip);
delete clip;
if (m_openedProducer == id) {
......@@ -293,11 +295,14 @@ int Bin::lastClipId() const
void Bin::setDocument(KdenliveDoc* project)
{
// Remove clip from Bin's monitor
m_monitor->open(NULL);
closeEditing();
setEnabled(false);
delete m_rootFolder;
delete m_itemView;
m_itemView = NULL;
delete m_jobManager;
delete m_rootFolder;
m_clipCounter = 1;
m_folderCounter = 1;
m_doc = project;
......@@ -307,6 +312,7 @@ void Bin::setDocument(KdenliveDoc* project)
m_itemModel->setIconSize(m_iconSize);
m_jobManager = new JobManager(this, project->fps());
m_rootFolder = new ProjectFolder(this);
setEnabled(true);
connect(this, SIGNAL(producerReady(QString)), m_doc->renderer(), SLOT(slotProcessingDone(QString)));
//connect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), m_itemView
//connect(m_itemModel, SIGNAL(updateCurrentItem()), this, SLOT(autoSelect()));
......@@ -407,10 +413,11 @@ void Bin::selectProxyModel(const QModelIndex &id)
if (id.isValid()) {
ProjectClip *currentItem = static_cast<ProjectClip*>(m_proxyModel->mapToSource(id).internalPointer());
if (currentItem) {
// Set item as current so that it displays its content in clip monitor
currentItem->setCurrent(true);
if (!currentItem->isFolder()) {
m_openedProducer = currentItem->clipId();
currentItem->setCurrent(true);
m_editAction->setEnabled(true);
m_openedProducer = currentItem->clipId();
if (m_propertiesPanel->width() > 0) {
// if info panel is displayed, update info
if (!currentItem->isFolder()) showClipProperties(currentItem);
......@@ -418,6 +425,7 @@ void Bin::selectProxyModel(const QModelIndex &id)
} else {
// A folder was selected, disable editing clip
m_editAction->setEnabled(false);
m_openedProducer.clear();
}
m_deleteAction->setEnabled(true);
} else {
......@@ -669,15 +677,21 @@ void Bin::slotThumbnailReady(const QString &id, const QImage &img)
ProjectClip *Bin::getBinClip(const QString &id)
{
ProjectClip *clip = m_rootFolder->clip(id);
ProjectClip *clip;
if (id.contains("_")) {
clip = m_rootFolder->clip(id.section("_", 0, 0));
}
else {
clip = m_rootFolder->clip(id);
}
return clip;
}
void Bin::slotProducerReady(requestClipInfo info, Mlt::Producer *producer)
void Bin::slotProducerReady(requestClipInfo info, ClipController *controller)
{
ProjectClip *clip = m_rootFolder->clip(info.clipId);
if (clip) {
clip->setProducer(producer, info.replaceProducer);
clip->setProducer(controller, info.replaceProducer);
emit producerReady(info.clipId);
if (m_openedProducer == info.clipId) {
m_monitor->open(clip->producer());
......@@ -685,10 +699,10 @@ void Bin::slotProducerReady(requestClipInfo info, Mlt::Producer *producer)
}
else {
// Clip not found, create it
QString groupId = producer->get("groupid");
QString groupId = controller->property("groupid");
ProjectFolder *parentFolder;
if (!groupId.isEmpty()) {
QString groupName = producer->get("group");
QString groupName = controller->property("group");
parentFolder = m_rootFolder->folder(groupId);
if (!parentFolder) {
// parent folder does not exist, put in root folder
......@@ -697,7 +711,7 @@ void Bin::slotProducerReady(requestClipInfo info, Mlt::Producer *producer)
if (groupId.toInt() >= m_folderCounter) m_folderCounter = groupId.toInt() + 1;
}
else parentFolder = m_rootFolder;
ProjectClip *newItem = new ProjectClip(info.clipId, producer, parentFolder);
ProjectClip *newItem = new ProjectClip(info.clipId, controller, parentFolder);
if (info.clipId.toInt() >= m_clipCounter) m_clipCounter = info.clipId.toInt() + 1;
}
}
......
......@@ -33,6 +33,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QSortFilterProxyModel>
class KdenliveDoc;
class ClipController;
class QSplitter;
class KToolBar;
class KSplitterCollapserButton;
......@@ -311,7 +312,7 @@ public slots:
* @param replaceProducer If true, we replace the producer even if the clip already has one
* @param producer The MLT producer
*/
void slotProducerReady(requestClipInfo info, Mlt::Producer *producer);
void slotProducerReady(requestClipInfo info, ClipController *controller);
void slotDeleteClip();
void slotRefreshClipProperties();
void slotSwitchClipProperties(const QModelIndex &ix);
......
......@@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "projectclip.h"
#include "projectfolder.h"
#include "bin.h"
#include "mltcontroller/clipcontroller.h"
#include <QDomElement>
#include <QFile>
......@@ -31,22 +32,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KLocalizedString>
#include <mlt++/Mlt.h>
ProjectClip::ProjectClip(const QString &id, Mlt::Producer *producer, ProjectFolder* parent) :
ProjectClip::ProjectClip(const QString &id, ClipController *controller, ProjectFolder* parent) :
AbstractProjectItem(AbstractProjectItem::ClipItem, id, parent),
m_producer(producer)
m_controller(controller)
{
m_properties = QMap <QString, QString> ();
QString resource = producer->get("resource");
if (!resource.isEmpty()) {
m_url = QUrl::fromLocalFile(resource);
if (m_url.isValid()) {
m_name = m_url.fileName();
}
}
m_duration = producer->get_length_time(mlt_time_smpte_df);
m_name = m_controller->clipName();
m_duration = m_controller->getStringDuration();
getFileHash();
setParent(parent);
......@@ -54,12 +48,12 @@ ProjectClip::ProjectClip(const QString &id, Mlt::Producer *producer, ProjectFold
ProjectClip::ProjectClip(const QDomElement& description, ProjectFolder* parent) :
AbstractProjectItem(AbstractProjectItem::ClipItem, description, parent)
, m_producer(NULL)
, m_controller(NULL)
{
Q_ASSERT(description.hasAttribute("id"));
m_properties = QMap <QString, QString> ();
m_url = QUrl::fromLocalFile(getXmlProperty(description, "resource"));
m_name = m_url.fileName();
QUrl resource = QUrl::fromLocalFile(getXmlProperty(description, "resource"));
m_name = resource.fileName();
if (description.hasAttribute("zone"))
m_zone = QPoint(description.attribute("zone").section(':', 0, 0).toInt(), description.attribute("zone").section(':', 1, 1).toInt());
setParent(parent);
......@@ -68,9 +62,7 @@ ProjectClip::ProjectClip(const QDomElement& description, ProjectFolder* parent)
ProjectClip::~ProjectClip()
{
// Cancel all clip jobs so that we don't crash trying to access the deleted clip
bin()->discardJobs(m_id);
delete m_producer;
delete m_controller;
}
QString ProjectClip::getXmlProperty(const QDomElement &producer, const QString &propertyName)
......@@ -88,18 +80,8 @@ QString ProjectClip::getXmlProperty(const QDomElement &producer, const QString &
ClipType ProjectClip::clipType() const
{
//TODO: store in properties
if (m_producer == NULL) return Unknown;
QString service = m_producer->get("mlt_service");
if (service == "avformat" || service == "avformat-novalidate") {
return AV;
}
if (service == "qimage" || service == "pixbuf") {
return Image;
}
if (service == "color") {
return Color;
}
if (m_controller == NULL) return Unknown;
return m_controller->clipType();
}
ProjectClip* ProjectClip::clip(const QString &id)
......@@ -123,14 +105,14 @@ ProjectClip* ProjectClip::clipAt(int ix)
return NULL;
}
bool ProjectClip::hasUrl() const
/*bool ProjectClip::isValid() const
{
return m_url.isValid();
}
return m_controller->isValid();
}*/
QUrl ProjectClip::url() const
{
return m_url;
return m_controller->clipUrl();
}
bool ProjectClip::hasLimitedDuration() const
......@@ -141,8 +123,8 @@ bool ProjectClip::hasLimitedDuration() const
int ProjectClip::duration() const
{
if (m_producer) {
return m_producer->get_playtime();
if (m_controller) {
return m_controller->getPlaytime();
}
return -1;
}
......@@ -166,8 +148,8 @@ void ProjectClip::reloadProducer()
void ProjectClip::setCurrent(bool current, bool notify)
{
AbstractProjectItem::setCurrent(current, notify);
if (current && m_producer) {
bin()->openProducer(m_id, producer());
if (current && m_controller) {
bin()->openProducer(m_id, m_controller->masterProducer());
}
}
......@@ -180,7 +162,7 @@ QDomElement ProjectClip::toXml(QDomDocument& document)
QString path = m_properties.value("proxy");
if (path.length() < 2) {
// No proxy
path = m_url.toLocalFile();
path = m_controller->clipUrl().toLocalFile();
}
QDomText value = document.createTextNode(path);
prop.appendChild(value);
......@@ -219,36 +201,39 @@ void ProjectClip::setThumbnail(QImage img)
bin()->emitItemUpdated(this);
}
void ProjectClip::setProducer(Mlt::Producer *producer, bool replaceProducer)
void ProjectClip::setProducer(ClipController *controller, bool replaceProducer)
{
if (!replaceProducer && m_producer) {
if (!replaceProducer && m_controller) {
qDebug()<<"// RECIEVED PRODUCER BUT WE ALREADY HAVE ONE\n----------";
return;
}
if (m_producer) {
delete m_producer;
if (m_controller) {
// Replace clip for this controller
m_controller->updateProducer(controller->masterProducer());
delete controller;
}
m_producer = producer;
m_duration = producer->get_length_time(mlt_time_smpte_df);
else m_controller = controller;
m_duration = m_controller->getStringDuration();
bin()->emitItemUpdated(this);
getFileHash();
}
Mlt::Producer *ProjectClip::producer()
{
if (!m_producer) return NULL;
return new Mlt::Producer(*m_producer);
if (!m_controller) return NULL;
return m_controller->masterProducer();
}
bool ProjectClip::hasProducer() const
{
return m_producer!= NULL;
return m_controller!= NULL;
}
QMap <QString, QString> ProjectClip::properties()
{
//TODO: move into its own class that creates its own widget (reuse clipproperties)
//TODO: move into its own class in ClipController
QMap <QString, QString> result;
/*
if (m_producer) {
mlt_properties props = m_producer->get_properties();
QString key = "video_index";
......@@ -260,6 +245,7 @@ QMap <QString, QString> ProjectClip::properties()
codecKey = "meta.media." + QString::number(audio_index) + ".codec.long_name";
result.insert(i18n("Audio codec"), mlt_properties_get(props, codecKey.toUtf8().constData()));
}
*/
return result;
}
......@@ -287,25 +273,22 @@ void ProjectClip::removeMarker(int position)
void ProjectClip::setProducerProperty(const char *name, int data)
{
if (m_producer) {
m_producer->set(name, data);
//TODO: also set property on all track producers
if (m_controller) {
m_controller->setProperty(name, data);
}
}
void ProjectClip::setProducerProperty(const char *name, double data)
{
if (m_producer) {
m_producer->set(name, data);
//TODO: also set property on all track producers
if (m_controller) {
m_controller->setProperty(name, data);
}
}
void ProjectClip::setProducerProperty(const char *name, const char *data)
{
if (m_producer) {
m_producer->set(name, data);
//TODO: also set property on all track producers
if (m_controller) {
m_controller->setProperty(name, data);
}
}
......@@ -313,8 +296,8 @@ void ProjectClip::setProducerProperty(const char *name, const char *data)
QString ProjectClip::getProducerProperty(const QString &key)
{
QString value;
if (m_producer) {
value = m_producer->get(key.toUtf8().constData());
if (m_controller) {
value = m_controller->property(key);
}
return value;
}
......@@ -328,8 +311,7 @@ const QString ProjectClip::hash()
void ProjectClip::getFileHash()
{
if (clipType() == SlideShow) return;
QFile file(m_url.toLocalFile());
qDebug()<<"// CREATE FILE HASH FOR CLIP";
QFile file(m_controller->clipUrl().toLocalFile());
if (file.open(QIODevice::ReadOnly)) { // write size and hash only if resource points to a file
QByteArray fileData;
QByteArray fileHash;
......
......@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class ProjectFolder;
class QDomElement;
class ClipController;
namespace Mlt {
class Producer;
......@@ -54,7 +55,7 @@ public:
/**
* @brief Constructor; used when loading a project and the producer is already available.
*/
ProjectClip(const QString &id, Mlt::Producer *producer, ProjectFolder* parent);
ProjectClip(const QString &id, ClipController *controller, ProjectFolder* parent);
/**
* @brief Constructor.
* @param description element describing the clip; the "id" attribute and "resource" property are used
......@@ -121,7 +122,7 @@ public:
* @param producer The producer
* @param replaceProducer If true, we replace existing producer with this one
* . */
void setProducer(Mlt::Producer *producer, bool replaceProducer);
void setProducer(ClipController *controller, bool replaceProducer);
/** @brief Returns true if this clip already has a producer. */
bool hasProducer() const;
......@@ -170,8 +171,8 @@ private:
QList <int> m_markers;
//TODO: handle properties inside MLT ?
QMap <QString, QString> m_properties;
/** @brief The MLT producer for this clip. */
Mlt::Producer *m_producer;
/** @brief The Clip controller for this clip. */
ClipController *m_controller;
/** @brief Generate and store file hash if not available. */
void getFileHash();
};
......
......@@ -41,13 +41,22 @@ ProjectFolder::ProjectFolder(Bin *bin) :
AbstractProjectItem(AbstractProjectItem::FolderItem, QString::number(-1))
, m_bin(bin)
{
m_name = "root";
setParent(NULL);
}
ProjectFolder::~ProjectFolder()
{
qDebug()<<" * * *DELETING PRO FOLDER: "<<m_name;
}
void ProjectFolder::setCurrent(bool current, bool notify)
{
AbstractProjectItem::setCurrent(current, notify);
if (current) {
bin()->openProducer(QString(), NULL);
}
}
ProjectClip* ProjectFolder::clip(const QString &id)
{
......
......@@ -63,6 +63,9 @@ public:
*/
ProjectFolder* folder(const QString &id);
/** @brief Calls AbstractProjectItem::setCurrent and blank the bin monitor. */
virtual void setCurrent(bool current, bool notify = true);
/**
* @brief Returns the clip if it is a child (also indirect).
* @param index index of the child which should be returned
......
......@@ -47,6 +47,7 @@ void Core::init()
m_binWidget = new Bin();
m_binController = new BinController();
m_monitorManager = new MonitorManager(this);
emit coreIsReady();
}
Core* Core::self()
......
......@@ -72,6 +72,9 @@ private:
MonitorManager *m_monitorManager;
BinController *m_binController;
Bin *m_binWidget;
signals:
void coreIsReady();
};
#endif
......@@ -154,7 +154,7 @@ bool DocumentValidator::validate(const double currentVersion)
// Remove "main bin" playlist that simply holds the bin's clips and is not a real playlist
for (int i = 0; i < playlists.count(); ++i) {
QString playlistId = playlists.at(i).toElement().attribute("id");
if (playlistId == BinController::id()) {
if (playlistId == BinController::binPlaylistId()) {
// remove pseudo-playlist
//playlists.at(i).parentNode().removeChild(playlists.at(i));
trackOffset = 2;
......
......@@ -409,15 +409,12 @@ KdenliveDoc::~KdenliveDoc()
int KdenliveDoc::setSceneList()
{
qDebug()<<" ++++++++ SETTING SCENE LIST ++++++++++++++++";
m_render->resetProfile(KdenliveSettings::current_profile(), true);
if (m_render->setSceneList(m_document.toString(), m_documentProperties.value("position").toInt()) == -1) {
// INVALID MLT Consumer, something is wrong
return -1;
}
m_documentProperties.remove("position");
// m_document xml is now useless, clear it
m_document.clear();
return 0;
}
......@@ -1285,6 +1282,11 @@ void KdenliveDoc::addClipList(const QList<QUrl> &urls, const QMap<QString, QStri
emit selectLastAddedClip(QString::number(m_clipManager->lastClipId()));
}
ProjectClip *KdenliveDoc::getBinClip(const QString &clipId)
{
return pCore->bin()->getBinClip(clipId);
}
DocClipBase *KdenliveDoc::getBaseClip(const QString &clipId)
{
return m_clipManager->getClipById(clipId);
......
......@@ -45,6 +45,7 @@ class DocClipBase;
class MainWindow;
class TrackInfo;
class NotesPlugin;
class ProjectClip;
class QTextEdit;
class QProgressDialog;
......@@ -89,6 +90,7 @@ public:
bool addClipInfo(QDomElement elem, QDomElement orig, const QString &clipId);
void deleteClip(const QString &clipId);
int getFramePos(const QString &duration);
ProjectClip *getBinClip(const QString &clipId);
DocClipBase *getBaseClip(const QString &clipId);
void updateClip(const QString &id);
......
......@@ -439,8 +439,9 @@ MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString &
KdenliveSettings::setDecklink_extension(data.section(';', 1, 1));
}
}
pCore->projectManager()->init(Url, clipsToLoad);
QTimer::singleShot(0, pCore->projectManager(), SLOT(slotLoadOnOpen()));
#ifdef USE_JOGSHUTTLE
new JogManager(this);
......@@ -650,7 +651,7 @@ void MainWindow::slotConnectMonitors()
connect(m_projectMonitor->render, SIGNAL(replyGetImage(QString,QImage)), pCore->bin(), SLOT(slotThumbnailReady(QString,QImage)));
connect(m_projectMonitor->render, SIGNAL(gotFileProperties(requestClipInfo,Mlt::Producer *)), pCore->bin(), SLOT(slotProducerReady(requestClipInfo,Mlt::Producer *)));
connect(m_projectMonitor->render, SIGNAL(gotFileProperties(requestClipInfo,ClipController *)), pCore->bin(), SLOT(slotProducerReady(requestClipInfo,ClipController *)));
connect(m_projectMonitor->render, SIGNAL(replyGetFileProperties(requestClipInfo &,Mlt::Producer &,stringMap,stringMap)), m_projectList, SLOT(slotReplyGetFileProperties(requestClipInfo &,Mlt::Producer &,stringMap,stringMap)), Qt::DirectConnection);
//DirectConnection was necessary not to mess the analyze queue, but the monitor thread shouldn't show any UI widget (profile dialog), so adding an AutoConnection in between?
......
set(kdenlive_SRCS
${kdenlive_SRCS}
mltcontroller/bincontroller.cpp
mltcontroller/clipcontroller.cpp
PARENT_SCOPE)
\ No newline at end of file
......@@ -19,6 +19,7 @@
#include "bincontroller.h"
#include "clipcontroller.h"
#include "kdenlivesettings.h"
#include <QFileInfo>
......@@ -27,8 +28,8 @@ static const char* kPlaylistTrackId = "main bin";
BinController::BinController(QString profileName)
{
m_binPlaylist = NULL;
m_mltProfile = NULL;
m_binPlaylist = NULL;
Mlt::Factory::init();
if (profileName.isEmpty()) {
profileName = KdenliveSettings::current_profile();
......@@ -70,17 +71,50 @@ Mlt::Profile *BinController::profile()
void BinController::destroyBin()
{
if (m_binPlaylist) {
m_binPlaylist->clear();
delete m_binPlaylist;
m_binPlaylist = NULL;
m_binPlaylist->clear();
delete m_binPlaylist;
m_binPlaylist = NULL;
}
// Controllers are deleted from the Bin's ProjectClip
m_clipList.clear();
}
void BinController::initializeBin(Mlt::Playlist playlist)
{
// Fill Controller's list
m_binPlaylist = new Mlt::Playlist(playlist);
m_binPlaylist->set("id", kPlaylistTrackId);
rebuildIndex();
for (int i = 0; i < m_binPlaylist->count(); i++) {
Mlt::Producer *producer = m_binPlaylist->get_clip(i);
if (producer->is_blank() || !producer->is_valid()) continue;
QString id = producer->parent().get("id");
if (id.contains("_")) {
// This is a track producer
QString mainId = id.section("_", 0, 0);
int track = id.section("_", 1, 1).toInt();
if (m_clipList.contains(mainId)) {
// The controller for this track producer already exists
m_clipList.value(mainId)->appendTrackProducer(track, producer->parent());
}
else {
// Create empty controller for this track
ClipController *controller = new ClipController(this);
controller->appendTrackProducer(track, producer->parent());
m_clipList.insert(mainId, controller);
}
}
else {
if (m_clipList.contains(id)) {
//Controller was already added by a track producer, add master now
m_clipList.value(id)->addMasterProducer(producer->parent());
}
else {
// Controller has not been created yet
ClipController *controller = new ClipController(this, producer->parent());
m_clipList.insert(id, controller);
}
}
}
}
void BinController::createIfNeeded()
......@@ -90,117 +124,82 @@ void BinController::createIfNeeded()
m_binPlaylist->set("id", kPlaylistTrackId);
}
const QString BinController::id()
{
return kPlaylistTrackId;
}