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