File indexing completed on 2024-04-21 04:51:14
0001 /* 0002 SPDX-FileCopyrightText: 2012 Till Theato <root@ttill.de> 0003 SPDX-FileCopyrightText: 2014 Jean-Baptiste Mardelle <jb@kdenlive.org> 0004 This file is part of Kdenlive. See www.kdenlive.org. 0005 0006 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #pragma once 0010 0011 #include "abstractprojectitem.h" 0012 #include "definitions.h" 0013 #include "mltcontroller/clipcontroller.h" 0014 #include "timeline2/model/timelinemodel.hpp" 0015 0016 #include <QFuture> 0017 #include <QMutex> 0018 #include <QTemporaryFile> 0019 #include <QTimer> 0020 #include <QUuid> 0021 #include <memory> 0022 0023 class ClipPropertiesController; 0024 class ProjectFolder; 0025 class ProjectSubClip; 0026 class QDomElement; 0027 0028 namespace Mlt { 0029 class Producer; 0030 class Properties; 0031 } // namespace Mlt 0032 0033 /** 0034 * @class ProjectClip 0035 * @brief Represents a clip in the project (not timeline). 0036 * It will be displayed as a bin item that can be dragged onto the timeline. 0037 * A single bin clip can be inserted several times on the timeline, and the ProjectClip 0038 * keeps track of all the ids of the corresponding ClipModel. 0039 * Note that because of a limitation in melt and AvFilter, it is currently difficult to 0040 * mix the audio of two producers that are cut from the same master producer 0041 * (that produces small but noticeable clicking artifacts) 0042 * 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 0043 * a master clip for each clip where the timewarp producer has been applied 0044 */ 0045 class ProjectClip : public AbstractProjectItem, public ClipController 0046 { 0047 Q_OBJECT 0048 0049 public: 0050 friend class Bin; 0051 friend bool TimelineModel::checkConsistency(const std::vector<int> &guideSnaps); // for testing 0052 /** 0053 * @brief Constructor; used when loading a project and the producer is already available. 0054 */ 0055 static std::shared_ptr<ProjectClip> construct(const QString &id, const QIcon &thumb, const std::shared_ptr<ProjectItemModel> &model, 0056 std::shared_ptr<Mlt::Producer> &producer); 0057 /** 0058 * @brief Constructor. 0059 * @param description element describing the clip; the "kdenlive:id" attribute and "resource" property are used 0060 */ 0061 static std::shared_ptr<ProjectClip> construct(const QString &id, const QDomElement &description, const QIcon &thumb, 0062 std::shared_ptr<ProjectItemModel> model); 0063 0064 /** 0065 * @brief Retreive original clip from proxy path when using external proxies 0066 */ 0067 static const QString getOriginalFromProxy(QString proxyPath); 0068 /** 0069 * @brief Retreive original clip from proxy path when using external proxies 0070 */ 0071 static const QString getProxyFromOriginal(QString originalPath); 0072 /** 0073 * @brief Retreive the list of external files used for sequence clip timewarp 0074 */ 0075 QStringList timelineSequenceExtraResources() const; 0076 0077 /** 0078 * @brief True if we started a clip reload 0079 */ 0080 bool isReloading; 0081 0082 struct TimeWarpInfo 0083 { 0084 bool enableRemap; 0085 QString timeMapData; 0086 int pitchShift; 0087 QString imageMode; 0088 }; 0089 0090 protected: 0091 ProjectClip(const QString &id, const QIcon &thumb, const std::shared_ptr<ProjectItemModel> &model, std::shared_ptr<Mlt::Producer> &producer); 0092 ProjectClip(const QString &id, const QDomElement &description, const QIcon &thumb, const std::shared_ptr<ProjectItemModel> &model); 0093 0094 public: 0095 ~ProjectClip() override; 0096 0097 void reloadProducer(bool refreshOnly = false, bool isProxy = false, bool forceAudioReload = false) override; 0098 0099 /** @brief Returns a unique hash identifier used to store clip thumbnails. */ 0100 // virtual void hash() = 0; 0101 0102 /** @brief Returns this if @param id matches the clip's id or nullptr otherwise. */ 0103 std::shared_ptr<ProjectClip> clip(const QString &id) override; 0104 0105 std::shared_ptr<ProjectFolder> folder(const QString &id) override; 0106 0107 std::shared_ptr<ProjectSubClip> getSubClip(int in, int out); 0108 0109 /** @brief Returns this if @param ix matches the clip's index or nullptr otherwise. */ 0110 std::shared_ptr<ProjectClip> clipAt(int ix) override; 0111 0112 /** @brief Returns the clip type as defined in definitions.h */ 0113 ClipType::ProducerType clipType() const override; 0114 0115 /** @brief Get the marker model for this clip */ 0116 std::shared_ptr<MarkerListModel> markerModel(); 0117 0118 /** @brief Set rating on item */ 0119 void setRating(uint rating) override; 0120 0121 bool selfSoftDelete(Fun &undo, Fun &redo) override; 0122 0123 Fun getAudio_lambda() override; 0124 0125 /** @brief Returns true if item has both audio and video enabled. */ 0126 bool hasAudioAndVideo() const override; 0127 0128 /** @brief Check if clip has a parent folder with id id */ 0129 bool hasParent(const QString &id) const; 0130 0131 /** @brief Returns true is the clip can have the requested state */ 0132 bool isCompatible(PlaylistState::ClipState state) const; 0133 0134 ClipPropertiesController *buildProperties(QWidget *parent); 0135 QPoint zone() const override; 0136 0137 /** @brief Returns whether this clip has a url (=describes a file) or not. */ 0138 bool hasUrl() const; 0139 0140 /** @brief Returns the clip's url. */ 0141 const QString url() const; 0142 0143 /** @brief Returns the clip's frame size. */ 0144 const QSize frameSize() const; 0145 0146 /** @brief Returns the clip's duration. */ 0147 GenTime duration() const; 0148 size_t frameDuration() const; 0149 0150 /** @brief Returns the original clip's fps. */ 0151 double getOriginalFps() const; 0152 0153 bool rename(const QString &name, int column) override; 0154 0155 QDomElement toXml(QDomDocument &document, bool includeMeta = false, bool includeProfile = true) override; 0156 0157 const QVariant getData(DataType type) const override; 0158 0159 QPixmap thumbnail(int width, int height); 0160 0161 /** @brief Returns this clip's producer. */ 0162 std::unique_ptr<Mlt::Producer> getThumbProducer() override; 0163 0164 /** @brief Recursively disable/enable bin effects. */ 0165 void setBinEffectsEnabled(bool enabled) override; 0166 0167 /** @brief Set properties on this clip. TODO: should we store all in MLT or use extra m_properties ?. */ 0168 void setProperties(const QMap<QString, QString> &properties, bool refreshPanel = false); 0169 0170 /** @brief Get an XML property from MLT produced xml. */ 0171 static QString getXmlProperty(const QDomElement &producer, const QString &propertyName, const QString &defaultValue = QString()); 0172 0173 QString getToolTip() const override; 0174 0175 /** @brief The clip hash created from the clip's resource. */ 0176 const QString hash(bool createIfEmpty = true); 0177 /** @brief The clip hash created from the clip's resource, plus the video stream in case of multi-stream clips. */ 0178 const QString hashForThumbs(); 0179 /** @brief Callculate a file hash from a path. */ 0180 static const QPair<QByteArray, qint64> calculateHash(const QString &path); 0181 0182 /** Cache for every audio Frame with 10 Bytes */ 0183 /** format is frame -> channel ->bytes */ 0184 bool audioThumbCreated() const; 0185 0186 void setWaitingStatus(const QString &id); 0187 /** @brief Returns true if the clip matched a condition, for example vcodec=mpeg1video. */ 0188 bool matches(const QString &condition); 0189 0190 /** @brief Returns the number of audio channels. */ 0191 int audioChannels() const; 0192 /** @brief get data analysis value. */ 0193 QStringList updatedAnalysisData(const QString &name, const QString &data, int offset); 0194 QMap<QString, QString> analysisData(bool withPrefix = false); 0195 /** @brief Returns the list of this clip's subclip's ids. */ 0196 QStringList subClipIds() const; 0197 /** @brief Delete cached audio thumb - needs to be recreated */ 0198 void discardAudioThumb(); 0199 /** @brief Get path for this clip's audio thumbnail */ 0200 const QString getAudioThumbPath(int stream); 0201 /** @brief Returns true if this producer has audio and can be splitted on timeline*/ 0202 bool isSplittable() const; 0203 0204 /** @brief Returns true if a clip corresponding to this bin is inserted in a timeline. 0205 Note that this function does not account for children, use TreeItem::accumulate if you want to get that information as well. 0206 */ 0207 bool isIncludedInTimeline() override; 0208 /** @brief Returns a list of all timeline clip ids for this bin clip */ 0209 QList<int> timelineInstances(QUuid activeUuid = QUuid()) const; 0210 QMap<QUuid, QList<int>> getAllTimelineInstances() const; 0211 /** @brief This function returns a cut to the master producer associated to the timeline clip with given ID. 0212 Each clip must have a different master producer (see comment of the class) 0213 */ 0214 std::shared_ptr<Mlt::Producer> getTimelineProducer(int trackId, int clipId, PlaylistState::ClipState st, int audioStream = -1, double speed = 1.0, 0215 bool secondPlaylist = false, const TimeWarpInfo timeremapInfo = {}); 0216 0217 /** @brief This function should only be used at loading. It takes a producer that was read from mlt, and checks whether the master producer is already in 0218 use. If yes, then we must create a new one, because of the mixing bug. In any case, we return a cut of the master that can be used in the timeline The 0219 bool returned has the following sementic: 0220 - if true, then the returned cut still possibly has effect on it. You need to rebuild the effectStack based on this 0221 - if false, then the returned cut don't have effects anymore (it's a fresh one), so you need to reload effects from the old producer 0222 */ 0223 std::pair<std::shared_ptr<Mlt::Producer>, bool> giveMasterAndGetTimelineProducer(int clipId, std::shared_ptr<Mlt::Producer> master, PlaylistState::ClipState state, int tid, bool secondPlaylist = false); 0224 0225 std::shared_ptr<Mlt::Producer> cloneProducer(bool removeEffects = false, bool timelineProducer = false); 0226 void cloneProducerToFile(const QString &path, bool thumbsProducer = false); 0227 static std::shared_ptr<Mlt::Producer> cloneProducer(const std::shared_ptr<Mlt::Producer> &producer); 0228 std::unique_ptr<Mlt::Producer> softClone(const char *list); 0229 /** @brief Returns a clone of the producer, useful for movit clip jobs 0230 */ 0231 std::unique_ptr<Mlt::Producer> getClone(); 0232 /** @brief Saves the subclips data as json 0233 */ 0234 void updateZones(); 0235 /** @brief Display Bin thumbnail given a percent 0236 */ 0237 void getThumbFromPercent(int percent, bool storeFrame = false); 0238 /** @brief Get the frame position used for Bin clip thumbnail 0239 */ 0240 int getThumbFrame() const; 0241 /** @brief Return audio cache for a stream 0242 */ 0243 const QVector <uint8_t> audioFrameCache(int stream = -1); 0244 /** @brief Return FFmpeg's audio stream index for an MLT audio stream index 0245 */ 0246 int getAudioStreamFfmpegIndex(int mltStream); 0247 void setClipStatus(FileStatus::ClipStatus status) override; 0248 /** @brief Rename an audio stream for this clip 0249 */ 0250 void renameAudioStream(int id, const QString &name) override; 0251 0252 /** @brief Add an audio effect on a specific audio stream with undo/redo. */ 0253 void requestAddStreamEffect(int streamIndex, const QString effectName) override; 0254 /** @brief Add an audio effect on a specific audio stream for this clip. */ 0255 void addAudioStreamEffect(int streamIndex, const QString effectName); 0256 /** @brief Remove an audio effect on a specific audio stream with undo/redo. */ 0257 void requestRemoveStreamEffect(int streamIndex, const QString effectName) override; 0258 /** @brief Remove an audio effect on a specific audio stream for this clip. */ 0259 void removeAudioStreamEffect(int streamIndex, QString effectName); 0260 /** @brief Get the list of audio stream effects for a defined stream. */ 0261 QStringList getAudioStreamEffect(int streamIndex) const override; 0262 /** @brief Calculate the folder's hash (based on the files it contains). */ 0263 static const QByteArray getFolderHash(const QDir &dir, QString fileName); 0264 /** @brief Check if the clip is included in timeline and reset its occurrences on producer reload. */ 0265 void updateTimelineOnReload(); 0266 int getRecordTime(); 0267 /** @brief Return maximum audio level for a stream. */ 0268 int getAudioMax(int stream); 0269 /** @brief A timeline clip was modified, reload its other timeline instances. */ 0270 void reloadTimeline(std::shared_ptr<EffectStackModel> stack = nullptr); 0271 /** @brief Copy sequence clip timewarp producers to a new location (when saving / rendering). */ 0272 void copyTimeWarpProducers(const QDir sequenceFolder, bool copy); 0273 /** @brief Refresh zones of insertion in timeline. */ 0274 void refreshBounds(); 0275 /** @brief Retuns a list of important enforces parameters in MLT format, for example to disable autorotate. */ 0276 const QStringList enforcedParams() const; 0277 /** @brief Remove clip references in a timeline. */ 0278 void purgeReferences(const QUuid &activeUuid, bool deleteClip = true); 0279 /** @brief Check if clip is referenced in a timeline, and return the clip's bin id if it is */ 0280 const QString isReferenced(const QUuid &activeUuid) const; 0281 const QString baseThumbPath(); 0282 /** @brief Returns false if the clip is or embeds a timeline with uuid. */ 0283 bool canBeDropped(const QUuid &uuid) const; 0284 const QList<QUuid> registeredUuids() const; 0285 /** @brief Get the sequence's unique identifier, empty if not a sequence clip. */ 0286 const QUuid &getSequenceUuid() const; 0287 void resetSequenceThumbnails(); 0288 /** @brief Returns the clip name (usually file name) */ 0289 QString clipName(); 0290 /** @brief Save an xml playlist of current clip with in/out points as zone.x()/y() */ 0291 void saveZone(QPoint zone, const QDir &dir); 0292 /** @brief When a sequence clip has a track change, update info and properties panel */ 0293 void refreshTracksState(int tracksCount = -1); 0294 /** 0295 * @brief Returns a pixmap created from a frame of the producer. 0296 * @param position frame position 0297 * @param width width of the pixmap (only a guidance) 0298 * @param height height of the pixmap (only a guidance) 0299 */ 0300 QPixmap pixmap(int position = 0, int width = 0, int height = 0); 0301 /** @brief Returns true if this clip has a variable framerate */ 0302 bool hasVariableFps(); 0303 0304 protected: 0305 friend class ClipModel; 0306 /** @brief This is a call-back called by a ClipModel when it is created 0307 @param timeline ptr to the pointer in which this ClipModel is inserted 0308 @param clipId id of the inserted clip 0309 */ 0310 void registerTimelineClip(std::weak_ptr<TimelineModel> timeline, int clipId); 0311 void registerService(std::weak_ptr<TimelineModel> timeline, int clipId, const std::shared_ptr<Mlt::Producer> &service, bool forceRegister = false); 0312 0313 /** @brief update the producer to reflect new parent folder */ 0314 void updateParent(std::shared_ptr<TreeItem> parent) override; 0315 0316 /** @brief This is a call-back called by a ClipModel when it is deleted 0317 @param clipId id of the deleted clip 0318 */ 0319 void deregisterTimelineClip(int clipId, bool audioClip, const QUuid &uuid); 0320 void replaceInTimeline(); 0321 void connectEffectStack() override; 0322 0323 public Q_SLOTS: 0324 /** @brief Store the audio thumbnails once computed. Note that the parameter is a value and not a reference, fill free to use it as a sink (use std::move to 0325 * avoid copy). */ 0326 void updateAudioThumbnail(bool cachedThumb); 0327 /** @brief Delete the proxy file */ 0328 void deleteProxy(bool reloadClip = true); 0329 /** @brief A clip job progressed, update display */ 0330 void updateJobProgress(); 0331 0332 /** @brief Sets thumbnail for this clip. */ 0333 void setThumbnail(const QImage &, int in, int out, bool inCache = false); 0334 0335 /** @brief A proxy clip is available or disabled, update path and reload */ 0336 void updateProxyProducer(const QString &path); 0337 0338 /** @brief Request updating some clip droles */ 0339 void updateTimelineClips(const QVector<int> &roles); 0340 0341 /** @brief If a clip is invalid on load, mark it as such so we don't try to re-insert it on undo/redo. */ 0342 void setInvalid(); 0343 0344 /** 0345 * Imports effect from a given producer 0346 * @param producer Producer containing the effects 0347 * @param originalDecimalPoint Decimal point to convert to “.”; See AssetParameterModel 0348 */ 0349 void importEffects(const std::shared_ptr<Mlt::Producer> &producer, const QString &originalDecimalPoint = QString()); 0350 0351 /** @brief Sets the MLT producer associated with this clip 0352 * @param producer The producer 0353 * @param replaceProducer If true, we replace existing producer with this one 0354 * @returns true if producer was changed 0355 * . */ 0356 bool setProducer(std::shared_ptr<Mlt::Producer> producer, bool generateThumb = false, bool clearTrackProducers = true); 0357 0358 void importJsonMarkers(const QString &json); 0359 /** @brief Refresh zones of insertion in timeline. */ 0360 void checkClipBounds(); 0361 0362 private: 0363 /** @brief Generate and store file hash if not available. */ 0364 const QString getFileHash(); 0365 QMutex m_producerMutex; 0366 QMutex m_thumbMutex; 0367 QString m_thumbXml; 0368 const QString geometryWithOffset(const QString &data, int offset); 0369 QMap <QString, QByteArray> m_audioLevels; 0370 /** @brief If true, all timeline occurrences of this clip will be replaced from a fresh producer on reload. */ 0371 bool m_resetTimelineOccurences; 0372 0373 /** @brief This is a helper function that creates the disabled producer. This is a clone of the original one, with audio and video disabled */ 0374 void createDisabledMasterProducer(); 0375 QMap<QUuid, QList<int>> m_registeredClipsByUuid; 0376 QTimer m_boundaryTimer; 0377 0378 /** @brief the following holds a producer for each audio clip in the timeline 0379 * keys are the id of the clips in the timeline, values are their values */ 0380 std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_audioProducers; 0381 std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_videoProducers; 0382 std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_timewarpProducers; 0383 std::shared_ptr<Mlt::Producer> m_disabledProducer; 0384 // A temporary uuid used to reset thumbnails on producer change 0385 QUuid m_uuid; 0386 // The sequence unique identifier 0387 QUuid m_sequenceUuid; 0388 QTemporaryFile m_sequenceThumbFile; 0389 /** @brief Update the clip description from the properties. */ 0390 void updateDescription(); 0391 0392 Q_SIGNALS: 0393 void producerChanged(const QString &, Mlt::Producer prod); 0394 void refreshPropertiesPanel(); 0395 void refreshAnalysisPanel(); 0396 void refreshClipDisplay(); 0397 void thumbReady(int, const QImage &); 0398 /** @brief Clip is ready, load properties. */ 0399 void loadPropertiesPanel(); 0400 void audioThumbReady(); 0401 void updateStreamInfo(int ix); 0402 void boundsChanged(QVector <QPoint>bounds); 0403 void registeredClipChanged(); 0404 };