File indexing completed on 2024-05-05 04:54:09
0001 /* 0002 SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #pragma once 0008 0009 #include "definitions.h" 0010 0011 #include <QDir> 0012 #include <QFuture> 0013 #include <QMutex> 0014 #include <QProcess> 0015 #include <QTimer> 0016 #include <QUuid> 0017 0018 class TimelineController; 0019 0020 namespace Mlt { 0021 class Tractor; 0022 class Playlist; 0023 class Producer; 0024 } // namespace Mlt 0025 0026 /** @class PreviewManager 0027 @brief Handles timeline preview. 0028 This manager creates an additional video track on top of the current timeline and renders 0029 chunks (small video files of 25 frames) that are added on this track when rendered. 0030 This allow us to get a preview with a smooth playback of our project. 0031 Only the preview zone is rendered. Once defined, a preview zone shows as a red line below 0032 the timeline ruler. As chunks are rendered, the zone turns to green. 0033 */ 0034 class PreviewManager : public QObject 0035 { 0036 Q_OBJECT 0037 0038 public: 0039 friend class TimelineModel; 0040 friend class TimelineController; 0041 0042 explicit PreviewManager(Mlt::Tractor *tractor, QUuid uuid, QObject *parent = nullptr); 0043 ~PreviewManager() override; 0044 /** @brief: initialize base variables, return false if error. */ 0045 bool initialize(); 0046 /** @brief: after a small delay (some operations trigger several invalidatePreview calls), take care of these invalidated chunks. */ 0047 void invalidatePreviews(); 0048 /** @brief: user adds current timeline zone to the preview zone. */ 0049 void addPreviewRange(const QPoint zone, bool add); 0050 /** @brief: Remove all existing previews. */ 0051 void clearPreviewRange(bool resetZones); 0052 /** @brief: stops current rendering process. */ 0053 void abortRendering(); 0054 /** @brief: rendering parameters have changed, reload them. */ 0055 bool loadParams(); 0056 /** @brief: Create the preview track if not existing. */ 0057 bool buildPreviewTrack(); 0058 /** @brief: Delete the preview track. */ 0059 void deletePreviewTrack(); 0060 /** @brief: Whenever we save or render our project, we remove the preview track so it is not saved. */ 0061 void reconnectTrack(); 0062 /** @brief: After project save or render, re-add our preview track. */ 0063 void disconnectTrack(); 0064 /** @brief: Returns directory currently used to store the preview files. */ 0065 const QDir getCacheDir() const; 0066 /** @brief: Load existing ruler chunks. */ 0067 void loadChunks(QVariantList previewChunks, QVariantList dirtyChunks, Mlt::Playlist &playlist); 0068 int setOverlayTrack(Mlt::Playlist *overlay); 0069 /** @brief Remove the effect compare overlay track */ 0070 void removeOverlayTrack(); 0071 /** @brief The current preview chunk being processed, -1 if none */ 0072 int workingPreview; 0073 /** @brief Returns the list of existing chunks */ 0074 QPair<QStringList, QStringList> previewChunks(); 0075 bool hasOverlayTrack() const; 0076 bool hasPreviewTrack() const; 0077 int addedTracks() const; 0078 /** @brief Returns true if a preview render range has already been defined */ 0079 bool hasDefinedRange() const; 0080 /** @brief Returns true if the render process is still running */ 0081 bool isRunning() const; 0082 0083 private: 0084 Mlt::Tractor *m_tractor; 0085 QUuid m_uuid; 0086 Mlt::Playlist *m_previewTrack; 0087 Mlt::Playlist *m_overlayTrack; 0088 bool m_warnOnCrash; 0089 int m_previewTrackIndex; 0090 /** @brief: The kdenlive timeline preview process. */ 0091 QProcess m_previewProcess; 0092 /** @brief: The directory used to store the preview files. */ 0093 QDir m_cacheDir; 0094 /** @brief: The directory used to store undo history of preview files (child of m_cacheDir). */ 0095 QDir m_undoDir; 0096 QMutex m_previewMutex; 0097 QStringList m_consumerParams; 0098 QString m_extension; 0099 /** @brief: Timer used to autostart preview rendering. */ 0100 QTimer m_previewTimer; 0101 /** @brief: Since some timeline operations generate several invalidate calls, use a timer to get them all. */ 0102 QTimer m_previewGatherTimer; 0103 bool m_initialized; 0104 QList<int> m_waitingThumbs; 0105 QFuture<void> m_previewThread; 0106 /** @brief: The count of chunks to process - to calculate job progress */ 0107 int m_chunksToRender; 0108 /** @brief: The count of already processed chunks - to calculate job progress */ 0109 int m_processedChunks; 0110 /** @brief: The render process output, useful in case of failure */ 0111 QString m_errorLog; 0112 /** @brief: After an undo/redo, if we have preview history, use it. */ 0113 void reloadChunks(const QVariantList &chunks); 0114 /** @brief: A chunk failed to render, abort. */ 0115 void corruptedChunk(int workingPreview, const QString &fileName); 0116 /** @brief: Get a compressed list of chunks, like: "0-500,525,575". */ 0117 const QStringList getCompressedList(const QVariantList items) const; 0118 0119 /** @brief Compare two chunks for usage by std::sort 0120 * @returns true if @param c1 is less than @param c2 0121 */ 0122 static bool chunkSort(const QVariant &c1, const QVariant &c2) { return c1.toInt() < c2.toInt(); }; 0123 0124 private Q_SLOTS: 0125 /** @brief: To avoid filling the hard drive, remove preview undo history after 5 steps. */ 0126 void doCleanupOldPreviews(); 0127 /** @brief: Start the real rendering process. */ 0128 void doPreviewRender(const QString &scene); // std::shared_ptr<Mlt::Producer> sourceProd); 0129 /** @brief: If user does an undo, then makes a new timeline operation, delete undo history of more recent stack . */ 0130 void slotRemoveInvalidUndo(int ix); 0131 /** @brief: When the timer collecting invalid zones is done, process. */ 0132 void slotProcessDirtyChunks(); 0133 /** @brief: Process preview rendering output. */ 0134 void receivedStderr(); 0135 void processEnded(int exitCode, QProcess::ExitStatus status); 0136 0137 public Q_SLOTS: 0138 /** @brief: Prepare and start rendering. */ 0139 void startPreviewRender(); 0140 /** @brief: A chunk has been created, notify ruler. */ 0141 void gotPreviewRender(int frame, const QString &file, int progress); 0142 /** @brief: a timeline operation caused changes to frames between startFrame and endFrame. */ 0143 void invalidatePreview(int startFrame, int endFrame); 0144 0145 protected: 0146 QVariantList m_renderedChunks; 0147 QVariantList m_dirtyChunks; 0148 mutable QMutex m_dirtyMutex; 0149 /** @brief: Re-enable timeline preview track. */ 0150 void enable(); 0151 /** @brief: Temporarily disable timeline preview track. */ 0152 void disable(); 0153 0154 Q_SIGNALS: 0155 void abortPreview(); 0156 void cleanupOldPreviews(); 0157 void previewRender(int frame, const QString &file, int progress); 0158 void dirtyChunksChanged(); 0159 void renderedChunksChanged(); 0160 void workingPreviewChanged(); 0161 };