projectclip.h 14.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
Copyright (C) 2012  Till Theato <root@ttill.de>
Copyright (C) 2014  Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.kdenlive.org.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License or (at your option) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
11
by the membership of KDE e.V.), which shall act as a proxy
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
defined in Section 14 of version 3 of the license.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PROJECTCLIP_H
#define PROJECTCLIP_H

#include "abstractprojectitem.h"
#include "definitions.h"
28
#include "mltcontroller/clipcontroller.h"
29
#include "timeline2/model/timelinemodel.hpp"
30

31
#include <QFuture>
Nicolas Carion's avatar
Nicolas Carion committed
32
#include <QMutex>
33
#include <memory>
34

35
class ClipPropertiesController;
36
class ProjectFolder;
37
class ProjectSubClip;
38
class QDomElement;
39

Nicolas Carion's avatar
Nicolas Carion committed
40
namespace Mlt {
41
42
class Producer;
class Properties;
43
} // namespace Mlt
44
45
46
47

/**
 * @class ProjectClip
 * @brief Represents a clip in the project (not timeline).
48
49
50
51
52
53
54
55
 * It will be displayed as a bin item that can be dragged onto the timeline.
 * A single bin clip can be inserted several times on the timeline, and the ProjectClip
 * keeps track of all the ids of the corresponding ClipModel.
 * Note that because of a limitation in melt and AvFilter, it is currently difficult to
 * mix the audio of two producers that are cut from the same master producer
 * (that produces small but noticeable clicking artifacts)
 * 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
 * a master clip for each clip where the timewarp producer has been applied
56
57
 */

58
class ProjectClip : public AbstractProjectItem, public ClipController
59
60
61
62
{
    Q_OBJECT

public:
63
    friend class Bin;
64
    friend bool TimelineModel::checkConsistency(); // for testing
65
66
67
    /**
     * @brief Constructor; used when loading a project and the producer is already available.
     */
Nicolas Carion's avatar
Nicolas Carion committed
68
69
    static std::shared_ptr<ProjectClip> construct(const QString &id, const QIcon &thumb, const std::shared_ptr<ProjectItemModel> &model,
                                                  const std::shared_ptr<Mlt::Producer> &producer);
70
71
    /**
     * @brief Constructor.
72
     * @param description element describing the clip; the "kdenlive:id" attribute and "resource" property are used
73
     */
74
75
    static std::shared_ptr<ProjectClip> construct(const QString &id, const QDomElement &description, const QIcon &thumb,
                                                  std::shared_ptr<ProjectItemModel> model);
76
77

protected:
Nicolas Carion's avatar
Nicolas Carion committed
78
79
    ProjectClip(const QString &id, const QIcon &thumb, const std::shared_ptr<ProjectItemModel> &model, std::shared_ptr<Mlt::Producer> producer);
    ProjectClip(const QString &id, const QDomElement &description, const QIcon &thumb, const std::shared_ptr<ProjectItemModel> &model);
80
81

public:
Nicolas Carion's avatar
Nicolas Carion committed
82
    ~ProjectClip() override;
83

84
    void reloadProducer(bool refreshOnly = false, bool audioStreamChanged = false, bool reloadAudio = true);
85
86

    /** @brief Returns a unique hash identifier used to store clip thumbnails. */
Nicolas Carion's avatar
Nicolas Carion committed
87
    // virtual void hash() = 0;
88

Laurent Montel's avatar
Laurent Montel committed
89
    /** @brief Returns this if @param id matches the clip's id or nullptr otherwise. */
90
    std::shared_ptr<ProjectClip> clip(const QString &id) override;
91

92
    std::shared_ptr<ProjectFolder> folder(const QString &id) override;
93

94
    std::shared_ptr<ProjectSubClip> getSubClip(int in, int out);
95

Laurent Montel's avatar
Laurent Montel committed
96
    /** @brief Returns this if @param ix matches the clip's index or nullptr otherwise. */
97
    std::shared_ptr<ProjectClip> clipAt(int ix) override;
98
99

    /** @brief Returns the clip type as defined in definitions.h */
100
    ClipType::ProducerType clipType() const override;
101

102
103
    /** @brief Set rating on item */
    void setRating(uint rating) override;
104

105
    bool selfSoftDelete(Fun &undo, Fun &redo) override;
Nicolas Carion's avatar
Nicolas Carion committed
106

107
108
109
    /** @brief Returns true if item has both audio and video enabled. */
    bool hasAudioAndVideo() const override;

110
111
    /** @brief Check if clip has a parent folder with id id */
    bool hasParent(const QString &id) const;
112
113
114
115

    /** @brief Returns true is the clip can have the requested state */
    bool isCompatible(PlaylistState::ClipState state) const;

116
    ClipPropertiesController *buildProperties(QWidget *parent);
117
    QPoint zone() const override;
118

119
120
121
122
    /** @brief Returns whether this clip has a url (=describes a file) or not. */
    bool hasUrl() const;

    /** @brief Returns the clip's url. */
123
    const QString url() const;
124
125

    /** @brief Returns the clip's duration. */
126
    GenTime duration() const;
127
    size_t frameDuration() const;
128

129
130
    /** @brief Returns the original clip's fps. */
    double getOriginalFps() const;
131

132
    bool rename(const QString &name, int column) override;
133

134
    QDomElement toXml(QDomDocument &document, bool includeMeta = false, bool includeProfile = true) override;
135

136
    QVariant getData(DataType type) const override;
137
138

    /** @brief Sets thumbnail for this clip. */
Laurent Montel's avatar
Laurent Montel committed
139
    void setThumbnail(const QImage &);
140
    QPixmap thumbnail(int width, int height);
141
142
143
144

    /** @brief Sets the MLT producer associated with this clip
     *  @param producer The producer
     *  @param replaceProducer If true, we replace existing producer with this one
145
     *  @returns true if producer was changed
146
     * . */
147
    bool setProducer(std::shared_ptr<Mlt::Producer> producer, bool replaceProducer);
148

149
    /** @brief Returns true if this clip already has a producer. */
150
    bool isReady() const;
151

152
    /** @brief Returns this clip's producer. */
153
    std::shared_ptr<Mlt::Producer> thumbProducer() override;
154

155
    /** @brief Recursively disable/enable bin effects. */
156
    void setBinEffectsEnabled(bool enabled) override;
157
158

    /** @brief Set properties on this clip. TODO: should we store all in MLT or use extra m_properties ?. */
Laurent Montel's avatar
Laurent Montel committed
159
    void setProperties(const QMap<QString, QString> &properties, bool refreshPanel = false);
160

161
    /** @brief Get an XML property from MLT produced xml. */
162
    static QString getXmlProperty(const QDomElement &producer, const QString &propertyName, const QString &defaultValue = QString());
163

164
    QString getToolTip() const override;
165

166
167
    /** @brief The clip hash created from the clip's resource. */
    const QString hash();
168
169
    /** @brief Callculate a file hash from a path. */
    static const QPair<QByteArray, qint64> calculateHash(const QString path);
170

171
172
    /** @brief Returns true if we are using a proxy for this clip. */
    bool hasProxy() const;
173

174
175
176
    /** Cache for every audio Frame with 10 Bytes */
    /** format is frame -> channel ->bytes */
    bool audioThumbCreated() const;
177

178
    void setWaitingStatus(const QString &id);
179
    /** @brief Returns true if the clip matched a condition, for example vcodec=mpeg1video. */
Laurent Montel's avatar
Laurent Montel committed
180
    bool matches(const QString &condition);
181

182
183
    /** @brief Returns the number of audio channels. */
    int audioChannels() const;
184
185
    /** @brief get data analysis value. */
    QStringList updatedAnalysisData(const QString &name, const QString &data, int offset);
Laurent Montel's avatar
Laurent Montel committed
186
    QMap<QString, QString> analysisData(bool withPrefix = false);
187
188
    /** @brief Returns the list of this clip's subclip's ids. */
    QStringList subClipIds() const;
189
    /** @brief Delete cached audio thumb - needs to be recreated */
190
    void discardAudioThumb(bool miniThumbOnly = false);
191
    /** @brief Get path for this clip's audio thumbnail */
192
    const QString getAudioThumbPath(int stream, bool miniThumb = false);
193
194
    /** @brief Returns true if this producer has audio and can be splitted on timeline*/
    bool isSplittable() const;
195

Nicolas Carion's avatar
Nicolas Carion committed
196
197
198
199
    /** @brief Returns true if a clip corresponding to this bin is inserted in a timeline.
        Note that this function does not account for children, use TreeItem::accumulate if you want to get that information as well.
    */
    bool isIncludedInTimeline() override;
200
    /** @brief Returns a list of all timeline clip ids for this bin clip */
201
    QList<int> timelineInstances() const;
202
203
204
    /** @brief This function returns a cut to the master producer associated to the timeline clip with given ID.
        Each clip must have a different master producer (see comment of the class)
    */
205
    std::shared_ptr<Mlt::Producer> getTimelineProducer(int trackId, int clipId, PlaylistState::ClipState st, int audioStream = -1, double speed = 1.0);
206
207
208
209
210

    /* @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
       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
       bool returned has the following sementic:
           - if true, then the returned cut still possibly has effect on it. You need to rebuild the effectStack based on this
Yuri Chornoivan's avatar
Yuri Chornoivan committed
211
           - 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
212
    */
213
    std::pair<std::shared_ptr<Mlt::Producer>, bool> giveMasterAndGetTimelineProducer(int clipId, std::shared_ptr<Mlt::Producer> master, PlaylistState::ClipState state, int tid);
214

215
    std::shared_ptr<Mlt::Producer> cloneProducer(bool removeEffects = false);
Nicolas Carion's avatar
Nicolas Carion committed
216
    static std::shared_ptr<Mlt::Producer> cloneProducer(const std::shared_ptr<Mlt::Producer> &producer);
217
    std::shared_ptr<Mlt::Producer> softClone(const char *list);
218
219
220
    /** @brief Returns a clone of the producer, useful for movit clip jobs
     */
    std::unique_ptr<Mlt::Producer> getClone();
Nicolas Carion's avatar
Nicolas Carion committed
221
    void updateTimelineClips(const QVector<int> &roles);
222
223
224
    /** @brief Saves the subclips data as json
     */
    void updateZones();
225
226
227
    /** @brief Display Bin thumbnail given a percent
     */
    void getThumbFromPercent(int percent);
228
229
230
231
232
233
    /** @brief Return audio cache for a stream
     */
    QVector <uint8_t> audioFrameCache(int stream = -1);
    /** @brief Return FFmpeg's audio stream index for an MLT audio stream index
     */
    int getAudioStreamFfmpegIndex(int mltStream);
234
    void setClipStatus(AbstractProjectItem::CLIPSTATUS status) override;
235
236
237
    /** @brief Rename an audio stream for this clip
     */
    void renameAudioStream(int id, QString name) override;
Nicolas Carion's avatar
Nicolas Carion committed
238

239
240
    /** @brief Add an audio effect on a specific audio stream with undo/redo. */
    void requestAddStreamEffect(int streamIndex, const QString effectName) override;
241
    /** @brief Add an audio effect on a specific audio stream for this clip. */
242
243
244
    void addAudioStreamEffect(int streamIndex, const QString effectName);
    /** @brief Remove an audio effect on a specific audio stream with undo/redo. */
    void requestRemoveStreamEffect(int streamIndex, const QString effectName) override;
245
    /** @brief Remove an audio effect on a specific audio stream for this clip. */
246
    void removeAudioStreamEffect(int streamIndex, QString effectName);
247
248
    /** @brief Get the list of audio stream effects for a defined stream. */
    QStringList getAudioStreamEffect(int streamIndex) const override;
249
250
251
252
    /** @brief Calculate the folder's hash (based on the files it contains). */
    static const QByteArray getFolderHash(QDir dir);
    /** @brief Check if the clip is included in timeline and reset its occurences on producer reload. */
    void updateTimelineOnReload();
Nicolas Carion's avatar
Nicolas Carion committed
253

254
255
256
257
258
259
260
protected:
    friend class ClipModel;
    /** @brief This is a call-back called by a ClipModel when it is created
        @param timeline ptr to the pointer in which this ClipModel is inserted
        @param clipId id of the inserted clip
     */
    void registerTimelineClip(std::weak_ptr<TimelineModel> timeline, int clipId);
Nicolas Carion's avatar
Nicolas Carion committed
261
    void registerService(std::weak_ptr<TimelineModel> timeline, int clipId, const std::shared_ptr<Mlt::Producer> &service, bool forceRegister = false);
262

263
264
265
    /* @brief update the producer to reflect new parent folder */
    void updateParent(std::shared_ptr<TreeItem> parent) override;

266
267
268
    /** @brief This is a call-back called by a ClipModel when it is deleted
        @param clipId id of the deleted clip
    */
Nicolas Carion's avatar
Nicolas Carion committed
269
    void deregisterTimelineClip(int clipId);
270

271
    void emitProducerChanged(const QString &id, const std::shared_ptr<Mlt::Producer> &producer) override { emit producerChanged(id, producer); };
272
    void replaceInTimeline();
273
    void connectEffectStack() override;
274

275
public slots:
Nicolas Carion's avatar
Nicolas Carion committed
276
277
    /* @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
     * avoid copy). */
278
    void updateAudioThumbnail();
279
280
    /** @brief Delete the proxy file */
    void deleteProxy();
281

282
283
284
285
286
287
    /**
     * Imports effect from a given producer
     * @param producer Producer containing the effects
     * @param originalDecimalPoint Decimal point to convert to “.”; See AssetParameterModel
     */
    void importEffects(const std::shared_ptr<Mlt::Producer> &producer, QString originalDecimalPoint = QString());
288

289
290
private:
    /** @brief Generate and store file hash if not available. */
291
    const QString getFileHash();
292
    QMutex m_producerMutex;
293
    QMutex m_thumbMutex;
Nicolas Carion's avatar
Nicolas Carion committed
294
    QFuture<void> m_thumbThread;
Laurent Montel's avatar
Laurent Montel committed
295
    QList<int> m_requestedThumbs;
296
    const QString geometryWithOffset(const QString &data, int offset);
297
298
299
    QMap <QString, QByteArray> m_audioLevels;
    /** @brief If true, all timeline occurences of this clip will be replaced from a fresh producer on reload. */
    bool m_resetTimelineOccurences;
Nicolas Carion's avatar
Nicolas Carion committed
300
301
302
    // This is a helper function that creates the disabled producer. This is a clone of the original one, with audio and video disabled
    void createDisabledMasterProducer();

Nicolas Carion's avatar
Nicolas Carion committed
303
    std::map<int, std::weak_ptr<TimelineModel>> m_registeredClips;
304
305
306
307

    // the following holds a producer for each audio clip in the timeline
    // keys are the id of the clips in the timeline, values are their values
    std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_audioProducers;
308
    std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_videoProducers;
309
    std::unordered_map<int, std::shared_ptr<Mlt::Producer>> m_timewarpProducers;
310
    std::shared_ptr<Mlt::Producer> m_disabledProducer;
311

312
signals:
313
    void producerChanged(const QString &, const std::shared_ptr<Mlt::Producer> &);
314
    void refreshPropertiesPanel();
315
    void refreshAnalysisPanel();
316
    void refreshClipDisplay();
Laurent Montel's avatar
Laurent Montel committed
317
    void thumbReady(int, const QImage &);
318
319
    /** @brief Clip is ready, load properties. */
    void loadPropertiesPanel();
320
    void audioThumbReady();
321
    void updateStreamInfo(int ix);
322
323
324
};

#endif