1 /*
2 SPDX-FileCopyrightText: 2014 Till Theato <root@ttill.de>
3 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
4 */
5 
6 #ifndef CORE_H
7 #define CORE_H
8 
9 #include "definitions.h"
10 #include "jobs/taskmanager.h"
11 #include "kdenlivecore_export.h"
12 #include "undohelper.hpp"
13 #include <QMutex>
14 #include <QObject>
15 #include <QColor>
16 #include <QUrl>
17 #include <memory>
18 #include <QPoint>
19 #include <QThreadPool>
20 #include <QTextEdit>
21 #include <KSharedDataCache>
22 #include <unordered_set>
23 #include "timecode.h"
24 
25 #include <mlt++/MltProfile.h>
26 #include <mlt++/MltPlaylist.h>
27 
28 class Bin;
29 class DocUndoStack;
30 class EffectStackModel;
31 class KdenliveDoc;
32 class LibraryWidget;
33 class MainWindow;
34 class MediaCapture;
35 class MixerManager;
36 class Monitor;
37 class MonitorManager;
38 class ProfileModel;
39 class ProjectItemModel;
40 class ProjectManager;
41 class SubtitleEdit;
42 class SubtitleModel;
43 class TextBasedEdit;
44 class TimeRemap;
45 
46 namespace Mlt {
47     class Repository;
48     class Producer;
49 } // namespace Mlt
50 
51 #define EXIT_RESTART (42)
52 #define EXIT_CLEAN_RESTART (43)
53 #define pCore Core::self()
54 
55 /**
56  * @class Core
57  * @brief Singleton that provides access to the different parts of Kdenlive
58  *
59  * Needs to be initialize before any widgets are created in MainWindow.
60  * Plugins should be loaded after the widget setup.
61  */
62 class /*KDENLIVECORE_EXPORT*/ Core : public QObject
63 {
64     Q_OBJECT
65 
66 public:
67     Core(const Core &) = delete;
68     Core &operator=(const Core &) = delete;
69     Core(Core &&) = delete;
70     Core &operator=(Core &&) = delete;
71 
72     ~Core() override;
73 
74     /**
75      * @brief Setup the basics of the application, in particular the connection
76      * with Mlt
77      * @param isAppImage do we expect an AppImage (if yes, we use App path to deduce
78      * other binaries paths (melt, ffmpeg, etc)
79      * @param MltPath (optional) path to MLT environment
80      */
81     static bool build(bool testMode = false);
82 
83     /**
84      * @brief Init the GUI part of the app and show the main window
85      * @param Url (optional) file to open
86      * If Url is present, it will be opened, otherwise, if openlastproject is
87      * set, latest project will be opened. If no file is open after trying this,
88      * a default new file will be created. */
89     void initGUI(bool isAppImage, const QString &MltPath, const QUrl &Url, const QString &clipsToLoad = QString());
90 
91     /** @brief Returns a pointer to the singleton object. */
92     static std::unique_ptr<Core> &self();
93 
94     /** @brief Delete the global core instance */
95     static void clean();
96 
97     /** @brief Returns a pointer to the main window. */
98     MainWindow *window();
99 
100     /** @brief Returns a pointer to the project manager. */
101     ProjectManager *projectManager();
102     /** @brief Returns a pointer to the current project. */
103     KdenliveDoc *currentDoc();
104     /** @brief Returns project's timecode. */
105     Timecode timecode() const;
106     /** @brief Returns a pointer to the monitor manager. */
107     MonitorManager *monitorManager();
108     /** @brief Returns a pointer to the view of the project bin. */
109     Bin *bin();
110     /** @brief Returns a pointer to the view of the active bin (or main bin on no focus). */
111     Bin *activeBin();
112     /** @brief Select a clip in the Bin from its id. */
113     void selectBinClip(const QString &id, bool activateMonitor = true, int frame = -1, const QPoint &zone = QPoint());
114     /** @brief Selects an item in the current timeline (clip, composition, subtitle). */
115     void selectTimelineItem(int id);
116     /** @brief Returns a pointer to the model of the project bin. */
117     std::shared_ptr<ProjectItemModel> projectItemModel();
118     /** @brief Returns a pointer to the library. */
119     LibraryWidget *library();
120     /** @brief Returns a pointer to the subtitle edit. */
121     SubtitleEdit *subtitleWidget();
122     /** @brief Returns a pointer to the text based editing widget. */
123     TextBasedEdit *textEditWidget();
124     /** @brief Returns a pointer to the time remapping widget. */
125     TimeRemap *timeRemapWidget();
126     /** @brief Returns true if clip displayed in remap widget is the bin clip with id clipId. */
127     bool currentRemap(const QString &clipId);
128     /** @brief Returns a pointer to the audio mixer. */
129     MixerManager *mixer();
130     ToolType::ProjectTool activeTool();
131 
132     /** @brief Returns a pointer to MLT's repository */
133     std::unique_ptr<Mlt::Repository> &getMltRepository();
134 
135     /** @brief Returns a pointer to the current profile */
136     std::unique_ptr<ProfileModel> &getCurrentProfile() const;
137     const QString &getCurrentProfilePath() const;
138 
139     /** @brief Define the active profile
140      *  @returns true if profile exists, false if not found
141      */
142     bool setCurrentProfile(const QString &profilePath);
143     /** @brief Returns Sample Aspect Ratio of current profile */
144     double getCurrentSar() const;
145     /** @brief Returns Display Aspect Ratio of current profile */
146     double getCurrentDar() const;
147 
148     /** @brief Returns frame rate of current profile */
149     double getCurrentFps() const;
150 
151     /** @brief Returns the frame size (width x height) of current profile */
152     QSize getCurrentFrameSize() const;
153     /** @brief Returns the frame display size (width x height) of current profile */
154     QSize getCurrentFrameDisplaySize() const;
155     /** @brief Request project monitor refresh */
156     void requestMonitorRefresh();
157     /** @brief Request project monitor refresh if current position is inside range*/
158     void refreshProjectRange(QPair<int, int> range);
159     /** @brief Request project monitor refresh if referenced item is under cursor */
160     void refreshProjectItem(const ObjectId &id);
161     /** @brief Returns a reference to a monitor (clip or project monitor) */
162     Monitor *getMonitor(int id);
163     /** @brief Returns timeline's active track info (position and tag) */
164     QPair <int,QString> currentTrackInfo() const;
165     /** @brief This function must be called whenever the profile used changes */
166     void profileChanged();
167 
168     /** @brief Create and push and undo object based on the corresponding functions
169         Note that if you class permits and requires it, you should use the macro PUSH_UNDO instead*/
170     void pushUndo(const Fun &undo, const Fun &redo, const QString &text);
171     void pushUndo(QUndoCommand *command);
172     /** @brief display a user info/warning message in statusbar */
173     void displayMessage(const QString &message, MessageType type, int timeout = -1);
174     /** @brief Clear asset view if itemId is displayed. */
175     void clearAssetPanel(int itemId);
176     /** @brief Returns the effectstack of a given bin clip. */
177     std::shared_ptr<EffectStackModel> getItemEffectStack(int itemType, int itemId);
178     int getItemPosition(const ObjectId &id);
179     int getItemIn(const ObjectId &id);
180     int getItemTrack(const ObjectId &id);
181     int getItemDuration(const ObjectId &id);
182     QSize getItemFrameSize(const ObjectId &id);
183     /** @brief Returns the capabilities of a clip: AudioOnly, VideoOnly or Disabled if both are allowed */
184     PlaylistState::ClipState getItemState(const ObjectId &id);
185     /** @brief Get a list of video track names with indexes */
186     QMap<int, QString> getTrackNames(bool videoOnly);
187     /** @brief Returns the composition A track (MLT index / Track id) */
188     QPair<int, int> getCompositionATrack(int cid) const;
189     void setCompositionATrack(int cid, int aTrack);
190     /** @brief Return true if composition's a_track is automatic (no forced track)
191      */
192     bool compositionAutoTrack(int cid) const;
193     std::shared_ptr<DocUndoStack> undoStack();
194     double getClipSpeed(int id) const;
195     /** @brief Mark an item as invalid for timeline preview */
196     void invalidateItem(ObjectId itemId);
197     void invalidateRange(QPair<int, int>range);
198     void prepareShutdown();
199     /** the keyframe model changed (effect added, deleted, active effect changed), inform timeline */
200     void updateItemKeyframes(ObjectId id);
201     /** A fade for clip id changed, update timeline */
202     void updateItemModel(ObjectId id, const QString &service);
203     /** Show / hide keyframes for a timeline clip */
204     void showClipKeyframes(ObjectId id, bool enable);
205     Mlt::Profile *thumbProfile();
206     /** @brief Returns the current project duration */
207     int projectDuration() const;
208     /** @brief Returns true if current project has some rendered timeline preview  */
209     bool hasTimelinePreview() const;
210     /** @brief Returns current timeline cursor position  */
211     int getTimelinePosition() const;
212     /** @brief Handles audio and video capture **/
213     void startMediaCapture(int tid, bool, bool);
214     void stopMediaCapture(int tid, bool, bool);
215     QStringList getAudioCaptureDevices();
216     int getMediaCaptureState();
217     bool isMediaCapturing();
218     MediaCapture *getAudioDevice();
219     /** @brief Returns Project Folder name for capture output location */
220     QString getProjectFolderName();
221     /** @brief Returns a timeline clip's bin id */
222     QString getTimelineClipBinId(int cid);
223     /** @brief Returns all track ids in timeline */
224     std::unordered_set<QString> getAllTimelineTracksId();
225     /** @brief Returns a frame duration from a timecode */
226     int getDurationFromString(const QString &time);
227     /** @brief An error occurred within a filter, inform user */
228     void processInvalidFilter(const QString service, const QString id, const QString message);
229     /** @brief Update current project's tags */
230     void updateProjectTags(QMap <QString, QString> tags);
231     /** @brief Returns the project profile */
232     Mlt::Profile *getProjectProfile();
233     /** @brief Returns the consumer profile, that will be scaled
234      *  according to preview settings. Should only be used on the consumer */
235     Mlt::Profile &getMonitorProfile();
236     /** @brief Returns a copy of current timeline's master playlist */
237     std::unique_ptr<Mlt::Producer> getMasterProducerInstance();
238     /** @brief Returns a copy of a track's playlist */
239     std::unique_ptr<Mlt::Producer> getTrackProducerInstance(int tid);
240     /** @brief Returns the undo stack index (position). */
241     int undoIndex() const;
242     /** @brief Enable / disable monitor multitrack view. Returns false if multitrack was not previously enabled */
243     bool enableMultiTrack(bool enable);
244     /** @brief Returns number of audio channels for this project. */
245     int audioChannels();
246     /** @brief Add guides in the project. */
247     void addGuides(QList <int> guides);
248     /** @brief Temporarily un/plug a list of clips in timeline. */
249     void temporaryUnplug(QList<int> clipIds, bool hide);
250     /** @brief Returns the current doc's subtitle model. */
251     std::shared_ptr<SubtitleModel> getSubtitleModel(bool enforce = false);
252     /** @brief Transcode a video file. */
253     void transcodeFile(const QString url);
254     /** @brief Display key binding info in statusbar. */
255     void setWidgetKeyBinding(const QString &mess = QString());
256     KSharedDataCache audioThumbCache;
257     /* @brief The thread job pool for clip jobs, allowing to set a max number of concurrent jobs */
258     TaskManager taskManager;
259     /** @brief The number of clip load jobs changed */
260     void loadingClips(int);
261     /** @brief Resize current mix item */
262     void resizeMix(int cid, int duration, MixAlignment align, int rightFrames = -1);
263     /** @brief Get Mix cut pos (the duration of the mix on the right clip) */
264     int getMixCutPos(int cid) const;
265     /** @brief Get alignment info for a mix item */
266     MixAlignment getMixAlign(int cid) const;
267     /** @brief Closing current document, do some cleanup */
268     void cleanup();
269     /** @brief Instantiates a "Get Hot New Stuff" dialog.
270      * @param configFile configuration file for KNewStuff
271      * @return number of installed items */
272     int getNewStuff(const QString &configFile);
273     /** @brief Get the frame size of the clip above a composition */
274     const QSize getCompositionSizeOnTrack(const ObjectId &id);
275     void loadTimelinePreview(const QString &chunks, const QString &dirty, const QDateTime &documentDate, int enablePreview, Mlt::Playlist &playlist);
276 
277 private:
278     explicit Core();
279     static std::unique_ptr<Core> m_self;
280 
281     /** @brief Makes sure Qt's locale and system locale settings match. */
282     void initLocale();
283 
284     MainWindow *m_mainWindow{nullptr};
285     ProjectManager *m_projectManager{nullptr};
286     MonitorManager *m_monitorManager{nullptr};
287     std::shared_ptr<ProjectItemModel> m_projectItemModel;
288     LibraryWidget *m_library{nullptr};
289     SubtitleEdit *m_subtitleWidget{nullptr};
290     TextBasedEdit *m_textEditWidget{nullptr};
291     TimeRemap *m_timeRemapWidget{nullptr};
292     MixerManager *m_mixerWidget{nullptr};
293 
294     /** @brief Current project's profile path */
295     QString m_currentProfile;
296 
297     QString m_profile;
298     Timecode m_timecode;
299     std::unique_ptr<Mlt::Profile> m_thumbProfile;
300     /** @brief Mlt profile used in the consumer 's monitors */
301     Mlt::Profile m_monitorProfile;
302     /** @brief Mlt profile used to build the project's clips */
303     std::unique_ptr<Mlt::Profile> m_projectProfile;
304     bool m_guiConstructed = false;
305     /** @brief Check that the profile is valid (width is a multiple of 8 and height a multiple of 2 */
306     void checkProfileValidity();
307     std::unique_ptr<MediaCapture> m_capture;
308     QUrl m_mediaCaptureFile;
309     QMutex m_thumbProfileMutex;
310 
311 public slots:
312     /** @brief Trigger (launch) an action by its actionCollection name */
313     void triggerAction(const QString &name);
314     /** @brief Get an action's descriptive text by its actionCollection name */
315     const QString actionText(const QString &name);
316     /** @brief display a user info/warning message in the project bin */
317     void displayBinMessage(const QString &text, int type, const QList<QAction *> &actions = QList<QAction *>(), bool showClose = false, BinMessage::BinCategory messageCategory = BinMessage::BinCategory::NoMessage);
318     void displayBinLogMessage(const QString &text, int type, const QString &logInfo);
319     /** @brief Create small thumbnails for luma used in compositions */
320     void buildLumaThumbs(const QStringList &values);
321     /** @brief Try to find a display name for the given filename.
322      *  This is espacally helpfull for mlt's dynamically created luma files without thumb (luma01.pgm, luma02.pgm,...),
323      *  but also for others as it makes the visible name translatable.
324      *  @return The name that fits to the filename or if none is found the filename it self
325      */
326     const QString nameForLumaFile(const QString filename);
327     /** @brief Set current project modified. */
328     void setDocumentModified();
329     /** @brief Show currently selected effect zone in timeline ruler. */
330     void showEffectZone(ObjectId id, QPair <int, int>inOut, bool checked);
331     void updateMasterZones();
332     /** @brief Open the proxies test dialog. */
333     void testProxies();
334     /** @brief Refresh the monitor profile when project profile changes. */
335     void updateMonitorProfile();
336     /** @brief Add a new Bin Widget. */
337     void addBin(const QString &id = QString());
338 
339 signals:
340     void coreIsReady();
341     void updateLibraryPath();
342     //void updateMonitorProfile();
343     /** @brief Call config dialog on a selected page / tab */
344     void showConfigDialog(int, int);
345     void finalizeRecording(const QString &captureFile);
346     void autoScrollChanged();
347     /** @brief Send a message to splash screen if still displayed */
348     void loadingMessageUpdated(const QString &, int progress = 0, int max = -1);
349     /** @brief Opening finished, close splash screen */
350     void closeSplash();
351     /** @brief Trigger an update of the the speech models list */
352     void voskModelUpdate(const QStringList models);
353     /** @brief This signal means that VOSK and/or SRT module availability changed*/
354     void updateVoskAvailability();
355     /** @brief Update current effect zone */
356     void updateEffectZone(const QPoint p, bool withUndo);
357     /** @brief The effect stask is about to be deleted, disconnect everything */
358     void disconnectEffectStack();
359     /** @brief Add a time remap effect to clip and show keyframes dialog */
360     void remapClip(int cid);
361     /** @brief A monitor property changed, check if we need to reset */
362     void monitorProfileUpdated();
363     /** @brief Color theme changed, process refresh */
364     void updatePalette();
365     /** @brief Emitted when a clip is resized (to handle clip monitor inserted zones) */
366     void clipInstanceResized(const QString &binId);
367 };
368 
369 #endif
370