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 };