1 | /*************************************************************************** |
2 | krender.h - description |
3 | ------------------- |
4 | begin : Fri Nov 22 2002 |
5 | copyright : (C) 2002 by Jason Wood (jasonwood@blueyonder.co.uk) |
6 | copyright : (C) 2010 by Jean-Baptiste Mardelle (jb@kdenlive.org) |
7 | |
8 | ***************************************************************************/ |
9 | |
10 | /*************************************************************************** |
11 | * * |
12 | * This program is free software; you can redistribute it and/or modify * |
13 | * it under the terms of the GNU General Public License as published by * |
14 | * the Free Software Foundation; either version 2 of the License, or * |
15 | * (at your option) any later version. * |
16 | * * |
17 | ***************************************************************************/ |
18 | |
19 | /** |
20 | * @class Render |
21 | * @brief Client side of the interface to a renderer. |
22 | * |
23 | * From Kdenlive's point of view, you treat the Render object as the renderer, |
24 | * and simply use it as if it was local. Calls are asynchronous - you send a |
25 | * call out, and then receive the return value through the relevant signal that |
26 | * get's emitted once the call completes. |
27 | */ |
28 | |
29 | #ifndef RENDERER_H |
30 | #define RENDERER_H |
31 | |
32 | #include "gentime.h" |
33 | #include "definitions.h" |
34 | #include "monitor/abstractmonitor.h" |
35 | |
36 | #include <mlt/framework/mlt_types.h> |
37 | |
38 | #include <kurl.h> |
39 | |
40 | #include <QtXml/qdom.h> |
41 | #include <QString> |
42 | #include <QMap> |
43 | #include <QList> |
44 | #include <QEvent> |
45 | #include <QMutex> |
46 | #include <QFuture> |
47 | #include <QSemaphore> |
48 | #include <QTimer> |
49 | |
50 | class QPixmap; |
51 | |
52 | class KComboBox; |
53 | |
54 | namespace Mlt |
55 | { |
56 | class Consumer; |
57 | class Playlist; |
58 | class Properties; |
59 | class Tractor; |
60 | class Transition; |
61 | class Frame; |
62 | class Field; |
63 | class Producer; |
64 | class Filter; |
65 | class Profile; |
66 | class Service; |
67 | class Event; |
68 | } |
69 | |
70 | struct requestClipInfo { |
71 | QDomElement xml; |
72 | QString clipId; |
73 | int imageHeight; |
74 | bool replaceProducer; |
75 | |
76 | bool operator==(const requestClipInfo &a) |
77 | { |
78 | return clipId == a.clipId; |
79 | } |
80 | }; |
81 | |
82 | class MltErrorEvent : public QEvent |
83 | { |
84 | public: |
85 | MltErrorEvent(const QString &message) |
86 | : QEvent(QEvent::User), |
87 | m_message(message) |
88 | { |
89 | |
90 | } |
91 | |
92 | QString message() const { |
93 | return m_message; |
94 | } |
95 | |
96 | private: |
97 | QString m_message; |
98 | }; |
99 | |
100 | |
101 | class Render: public AbstractRender |
102 | { |
103 | Q_OBJECT public: |
104 | |
105 | enum FailStates { OK = 0, |
106 | APP_NOEXIST |
107 | }; |
108 | /** @brief Build a MLT Renderer |
109 | * @param rendererName A unique identifier for this renderer |
110 | * @param winid The parent widget identifier (required for SDL display). Set to 0 for OpenGL rendering |
111 | * @param profile The MLT profile used for the renderer (default one will be used if empty). */ |
112 | Render(Kdenlive::MonitorId rendererName, int winid, QString profile = QString(), QWidget *parent = 0); |
113 | |
114 | /** @brief Destroy the MLT Renderer. */ |
115 | virtual ~Render(); |
116 | |
117 | /** @brief Seeks the renderer clip to the given time. */ |
118 | void seek(const GenTime &time); |
119 | void seek(int time); |
120 | void seekToFrameDiff(int diff); |
121 | |
122 | /** @brief Sets the current MLT producer playlist. |
123 | * @param list The xml describing the playlist |
124 | * @param position (optional) time to seek to */ |
125 | int setSceneList(const QDomDocument &list, int position = 0); |
126 | |
127 | /** @brief Sets the current MLT producer playlist. |
128 | * @param list new playlist |
129 | * @param position (optional) time to seek to |
130 | * @return 0 when it has success, different from 0 otherwise |
131 | * |
132 | * Creates the producer from the text playlist. */ |
133 | int setSceneList(QString playlist, int position = 0); |
134 | int setProducer(Mlt::Producer *producer, int position); |
135 | |
136 | /** @brief Get the current MLT producer playlist. |
137 | * @return A string describing the playlist */ |
138 | const QString sceneList(); |
139 | bool saveSceneList(QString path, QDomElement kdenliveData = QDomElement()); |
140 | |
141 | /** @brief Tells the renderer to play the scene at the specified speed, |
142 | * @param speed speed to play the scene to |
143 | * |
144 | * The speed is relative to normal playback, e.g. 1.0 is normal speed, 0.0 |
145 | * is paused, -1.0 means play backwards. It does not specify start/stop */ |
146 | void play(double speed); |
147 | void switchPlay(bool play); |
148 | void pause(); |
149 | |
150 | /** @brief Stops playing. |
151 | * @param startTime time to seek to */ |
152 | void stop(const GenTime &startTime); |
153 | int volume() const; |
154 | |
155 | QImage (int frame_position, const QString &path = QString(), int width = -1, int height = -1); |
156 | |
157 | /** @brief Plays the scene starting from a specific time. |
158 | * @param startTime time to start playing the scene from */ |
159 | void play(const GenTime & startTime); |
160 | void playZone(const GenTime & startTime, const GenTime & stopTime); |
161 | void loopZone(const GenTime & startTime, const GenTime & stopTime); |
162 | |
163 | void saveZone(KUrl url, QString desc, QPoint zone); |
164 | |
165 | /** @brief Save a clip in timeline to an xml playlist. */ |
166 | bool saveClip(int track, const GenTime &position, const KUrl &url, const QString &desc = QString()); |
167 | |
168 | /** @brief Return true if we are currently playing */ |
169 | bool isPlaying() const; |
170 | |
171 | /** @brief Returns the speed at which the renderer is currently playing. |
172 | * |
173 | * It returns 0.0 when the renderer is not playing anything. */ |
174 | double playSpeed() const; |
175 | |
176 | /** @brief Returns the current seek position of the renderer. */ |
177 | GenTime seekPosition() const; |
178 | int seekFramePosition() const; |
179 | |
180 | void emitFrameUpdated(Mlt::Frame&); |
181 | void (); |
182 | void emitConsumerStopped(bool forcePause = false); |
183 | |
184 | /** @brief Change the Mlt PROFILE |
185 | * @param profileName The MLT profile name |
186 | * @param dropSceneList If true, the current playlist will be deleted |
187 | * @return true if the profile was changed |
188 | * . */ |
189 | int resetProfile(const QString& profileName, bool dropSceneList = false); |
190 | /** @brief Returns true if the render uses profileName as current profile. */ |
191 | bool hasProfile(const QString& profileName) const; |
192 | double fps() const; |
193 | |
194 | /** @brief Returns the width of a frame for this profile. */ |
195 | int frameRenderWidth() const; |
196 | /** @brief Returns the display width of a frame for this profile. */ |
197 | int renderWidth() const; |
198 | /** @brief Returns the height of a frame for this profile. */ |
199 | int renderHeight() const; |
200 | |
201 | /** @brief Returns display aspect ratio. */ |
202 | double dar() const; |
203 | /** @brief Returns sample aspect ratio. */ |
204 | double sar() const; |
205 | /** @brief If monitor is active, refresh it. */ |
206 | void refreshIfActive(); |
207 | /** @brief Start the MLT monitor consumer. */ |
208 | void startConsumer(); |
209 | |
210 | /* |
211 | * Playlist manipulation. |
212 | */ |
213 | Mlt::Producer *checkSlowMotionProducer(Mlt::Producer *prod, QDomElement element); |
214 | int mltInsertClip(ItemInfo info, QDomElement element, Mlt::Producer *prod, bool overwrite = false, bool push = false); |
215 | bool mltUpdateClip(Mlt::Tractor *tractor, ItemInfo info, QDomElement element, Mlt::Producer *prod); |
216 | bool mltCutClip(int track, const GenTime &position); |
217 | void mltInsertSpace(QMap <int, int> trackClipStartList, QMap <int, int> trackTransitionStartList, int track, const GenTime &duration, const GenTime &timeOffset); |
218 | int mltGetSpaceLength(const GenTime &pos, int track, bool fromBlankStart); |
219 | |
220 | /** @brief Returns the duration/length of @param track as reported by the track producer. */ |
221 | int mltTrackDuration(int track); |
222 | |
223 | bool mltResizeClipEnd(ItemInfo info, GenTime clipDuration, bool refresh = true); |
224 | bool mltResizeClipStart(ItemInfo info, GenTime diff); |
225 | bool mltResizeClipCrop(ItemInfo info, GenTime newCropStart); |
226 | bool mltMoveClip(int startTrack, int endTrack, GenTime pos, GenTime moveStart, Mlt::Producer *prod, bool overwrite = false, bool insert = false); |
227 | bool mltMoveClip(int startTrack, int endTrack, int pos, int moveStart, Mlt::Producer *prod, bool overwrite = false, bool insert = false); |
228 | bool mltRemoveClip(int track, GenTime position); |
229 | |
230 | /** @brief Deletes an effect from a clip in MLT's playlist. */ |
231 | bool mltRemoveEffect(int track, GenTime position, int index, bool updateIndex, bool doRefresh = true); |
232 | bool mltRemoveTrackEffect(int track, int index, bool updateIndex); |
233 | |
234 | /** @brief Adds an effect to a clip in MLT's playlist. */ |
235 | bool mltAddEffect(int track, GenTime position, EffectsParameterList params, bool doRefresh = true); |
236 | bool addFilterToService(Mlt::Service service, EffectsParameterList params, int duration); |
237 | bool mltAddEffect(Mlt::Service service, EffectsParameterList params, int duration, bool doRefresh); |
238 | bool mltAddTrackEffect(int track, EffectsParameterList params); |
239 | |
240 | /** @brief Enable / disable clip effects. |
241 | * @param track The track where the clip is |
242 | * @param position The start position of the clip |
243 | * @param effectIndexes The list of effect indexes to enable / disable |
244 | * @param disable True if effects should be disabled, false otherwise */ |
245 | bool mltEnableEffects(int track, const GenTime &position, const QList<int> &effectIndexes, bool disable); |
246 | /** @brief Enable / disable track effects. |
247 | * @param track The track where the effect is |
248 | * @param effectIndexes The list of effect indexes to enable / disable |
249 | * @param disable True if effects should be disabled, false otherwise */ |
250 | bool mltEnableTrackEffects(int track, const QList<int> &effectIndexes, bool disable); |
251 | |
252 | /** @brief Edits an effect parameters in MLT's playlist. */ |
253 | bool mltEditEffect(int track, const GenTime &position, EffectsParameterList params); |
254 | bool mltEditTrackEffect(int track, EffectsParameterList params); |
255 | |
256 | /** @brief Updates the "kdenlive_ix" (index) value of an effect. */ |
257 | void mltUpdateEffectPosition(int track, const GenTime &position, int oldPos, int newPos); |
258 | |
259 | /** @brief Changes the order of effects in MLT's playlist. |
260 | * |
261 | * It switches effects from oldPos and newPos, updating the "kdenlive_ix" |
262 | * (index) value. */ |
263 | void mltMoveEffect(int track, const GenTime &position, int oldPos, int newPos); |
264 | void mltMoveTrackEffect(int track, int oldPos, int newPos); |
265 | |
266 | /** @brief Enables/disables audio/video in a track. */ |
267 | void mltChangeTrackState(int track, bool mute, bool blind); |
268 | bool mltMoveTransition(QString type, int startTrack, int newTrack, int newTransitionTrack, GenTime oldIn, GenTime oldOut, GenTime newIn, GenTime newOut); |
269 | bool mltAddTransition(QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool refresh = true); |
270 | void mltDeleteTransition(QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool refresh = true); |
271 | void mltUpdateTransition(QString oldTag, QString tag, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml, bool force = false); |
272 | void mltUpdateTransitionParams(QString type, int a_track, int b_track, GenTime in, GenTime out, QDomElement xml); |
273 | QList <TransitionInfo> mltInsertTrack(int ix, bool videoTrack); |
274 | void mltDeleteTrack(int ix); |
275 | bool mltUpdateClipProducer(Mlt::Tractor *tractor, int track, int pos, Mlt::Producer *prod); |
276 | void mltPlantTransition(Mlt::Field *field, Mlt::Transition &tr, int a_track, int b_track); |
277 | Mlt::Producer *invalidProducer(const QString &id); |
278 | |
279 | /** @brief Changes the speed of a clip in MLT's playlist. |
280 | * |
281 | * It creates a new "framebuffer" producer, which must have its "resource" |
282 | * property set to "video.mpg?0.6", where "video.mpg" is the path to the |
283 | * clip and "0.6" is the speed in percentage. The newly created producer |
284 | * will have its "id" property set to "slowmotion:parentid:speed", where |
285 | * "parentid" is the id of the original clip in the ClipManager list and |
286 | * "speed" is the current speed. */ |
287 | int mltChangeClipSpeed(ItemInfo info, ItemInfo speedIndependantInfo, double speed, double oldspeed, int strobe, Mlt::Producer *prod); |
288 | |
289 | const QList <Mlt::Producer *> producersList(); |
290 | void setDropFrames(bool show); |
291 | /** @brief Sets an MLT consumer property. */ |
292 | void setConsumerProperty(const QString &name, const QString &value); |
293 | QString updateSceneListFps(double current_fps, double new_fps, const QString &scene); |
294 | |
295 | void showAudio(Mlt::Frame&); |
296 | |
297 | QList <int> checkTrackSequence(int); |
298 | void sendFrameUpdate(); |
299 | |
300 | /** @brief Returns a pointer to the main producer. */ |
301 | Mlt::Producer *getProducer(); |
302 | /** @brief Returns the number of clips to process (When requesting clip info). */ |
303 | int processingItems(); |
304 | /** @brief Processing of this clip is over, producer was set on clip, remove from list. */ |
305 | void processingDone(const QString &id); |
306 | /** @brief Force processing of clip with selected id. */ |
307 | void forceProcessing(const QString &id); |
308 | /** @brief Are we currently processing clip with selected id. */ |
309 | bool isProcessing(const QString &id); |
310 | |
311 | /** @brief Requests the file properties for the specified URL (will be put in a queue list) |
312 | @param xml The xml parameters for the clip |
313 | @param clipId The clip Id string |
314 | @param imageHeight The height (in pixels) of the returned thumbnail (height of a treewidgetitem in projectlist) |
315 | @param replaceProducer If true, the MLT producer will be recreated */ |
316 | void getFileProperties(const QDomElement &xml, const QString &clipId, int imageHeight, bool replaceProducer = true); |
317 | |
318 | /** @brief Lock the MLT service */ |
319 | Mlt::Tractor *lockService(); |
320 | /** @brief Unlock the MLT service */ |
321 | void unlockService(Mlt::Tractor *tractor); |
322 | const QString activeClipId(); |
323 | /** @brief Fill a combobox with the found blackmagic devices */ |
324 | static bool getBlackMagicDeviceList(KComboBox *devicelist, bool force = false); |
325 | static bool getBlackMagicOutputDeviceList(KComboBox *devicelist, bool force = false); |
326 | /** @brief Frame rendering is handeled by Kdenlive, don't show video through SDL display */ |
327 | void disablePreview(bool disable); |
328 | /** @brief Get current seek pos requested of SEEK_INACTIVE if we are not currently seeking */ |
329 | int requestedSeekPosition; |
330 | /** @brief Get current seek pos requested of current producer pos if not seeking */ |
331 | int getCurrentSeekPosition() const; |
332 | /** @brief Create a producer from url and load it in the monitor */ |
333 | void loadUrl(const QString &url); |
334 | /** @brief Check if the installed FFmpeg / Libav supports x11grab */ |
335 | static bool checkX11Grab(); |
336 | |
337 | /** @brief Ask to set this monitor as active */ |
338 | void setActiveMonitor(); |
339 | |
340 | QSemaphore showFrameSemaphore; |
341 | bool externalConsumer; |
342 | |
343 | protected: |
344 | static void consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr); |
345 | static void consumer_gl_frame_show(mlt_consumer, Render * self, mlt_frame frame_ptr); |
346 | |
347 | private: |
348 | |
349 | /** @brief The name of this renderer. |
350 | * |
351 | * Useful to identify the renderers by what they do - e.g. background |
352 | * rendering, workspace monitor, etc. */ |
353 | Kdenlive::MonitorId m_name; |
354 | Mlt::Consumer * m_mltConsumer; |
355 | Mlt::Producer * m_mltProducer; |
356 | Mlt::Profile *m_mltProfile; |
357 | Mlt::Event *m_showFrameEvent; |
358 | Mlt::Event *m_pauseEvent; |
359 | double m_fps; |
360 | |
361 | /** @brief True if we are playing a zone. |
362 | * |
363 | * It's determined by the "in" and "out" properties being temporarily |
364 | * changed. */ |
365 | bool m_isZoneMode; |
366 | bool m_isLoopMode; |
367 | GenTime m_loopStart; |
368 | |
369 | /** @brief True when the monitor is in split view. */ |
370 | bool m_isSplitView; |
371 | |
372 | Mlt::Producer *m_blackClip; |
373 | QString m_activeProfile; |
374 | |
375 | QTimer m_refreshTimer; |
376 | QMutex m_mutex; |
377 | QMutex m_infoMutex; |
378 | |
379 | /** @brief A human-readable description of this renderer. */ |
380 | int m_winid; |
381 | |
382 | QLocale m_locale; |
383 | QFuture <void> m_infoThread; |
384 | QList <requestClipInfo> m_requestList; |
385 | bool m_paused; |
386 | /** @brief True if this monitor is active. */ |
387 | bool m_isActive; |
388 | |
389 | void closeMlt(); |
390 | void mltCheckLength(Mlt::Tractor *tractor); |
391 | void mltPasteEffects(Mlt::Producer *source, Mlt::Producer *dest); |
392 | QMap<QString, QString> mltGetTransitionParamsFromXml(const QDomElement &xml); |
393 | QMap<QString, Mlt::Producer *> m_slowmotionProducers; |
394 | /** @brief The ids of the clips that are currently being loaded for info query */ |
395 | QStringList m_processingClipId; |
396 | |
397 | /** @brief Build the MLT Consumer object with initial settings. |
398 | * @param profileName The MLT profile to use for the consumer */ |
399 | void buildConsumer(const QString& profileName); |
400 | void resetZoneMode(); |
401 | void fillSlowMotionProducers(); |
402 | /** @brief Get the track number of the lowest audible (non muted) audio track |
403 | * @param return The track number */ |
404 | int getLowestNonMutedAudioTrack(Mlt::Tractor tractor); |
405 | |
406 | /** @brief Make sure our audio mixing transitions are applied to the lowest track */ |
407 | void fixAudioMixing(Mlt::Tractor tractor); |
408 | /** @brief Make sure we inform MLT if we need a lot of threads for avformat producer */ |
409 | void checkMaxThreads(); |
410 | /** @brief Clone serialisable properties only */ |
411 | void cloneProperties(Mlt::Properties &dest, Mlt::Properties &source); |
412 | |
413 | private slots: |
414 | |
415 | /** @brief Refreshes the monitor display. */ |
416 | void refresh(); |
417 | /** @brief Process the clip info requests (in a separate thread). */ |
418 | void processFileProperties(); |
419 | /** @brief A clip with multiple video streams was found, ask what to do. */ |
420 | void slotMultiStreamProducerFound(const QString &path, QList<int> audio_list, QList<int> video_list, stringMap data); |
421 | void showFrame(Mlt::Frame *); |
422 | void slotCheckSeeking(); |
423 | |
424 | signals: |
425 | |
426 | /** @brief The renderer received a reply to a getFileProperties request. */ |
427 | void replyGetFileProperties(const QString &clipId, Mlt::Producer*, const stringMap &, const stringMap &, bool replaceProducer); |
428 | |
429 | /** @brief The renderer received a reply to a getImage request. */ |
430 | void replyGetImage(const QString &, const QString &, int, int); |
431 | void replyGetImage(const QString &, const QImage &); |
432 | |
433 | /** @brief The renderer stopped, either playing or rendering. */ |
434 | void stopped(); |
435 | |
436 | /** @brief The renderer started playing. */ |
437 | void playing(double); |
438 | |
439 | /** @brief The renderer started rendering. */ |
440 | void rendering(const GenTime &); |
441 | |
442 | /** @brief An error occurred within this renderer. */ |
443 | void error(const QString &, const QString &); |
444 | void durationChanged(int); |
445 | void rendererPosition(int); |
446 | void rendererStopped(int); |
447 | /** @brief The clip is not valid, should be removed from project. */ |
448 | void removeInvalidClip(const QString &, bool replaceProducer); |
449 | /** @brief The proxy is not valid, should be deleted. |
450 | * @param id The original clip's id |
451 | * @param durationError Should be set to true if the proxy failed because it has not same length as original clip |
452 | */ |
453 | void removeInvalidProxy(const QString &id, bool durationError); |
454 | void refreshDocumentProducers(bool displayRatioChanged, bool fpsChanged); |
455 | /** @brief A proxy clip is missing, ask for creation. */ |
456 | void requestProxy(QString); |
457 | /** @brief A multiple stream clip was found. */ |
458 | void multiStreamFound(const QString &,QList<int>,QList<int>,stringMap data); |
459 | |
460 | |
461 | /** @brief A frame's image has to be shown. |
462 | * |
463 | * Used in Mac OS X. */ |
464 | void showImageSignal(QImage); |
465 | void showAudioSignal(const QVector<double> &); |
466 | void addClip(const KUrl &, stringMap); |
467 | void checkSeeking(); |
468 | /** @brief Activate current monitor. */ |
469 | void activateMonitor(Kdenlive::MonitorId); |
470 | void mltFrameReceived(Mlt::Frame *); |
471 | void infoProcessingFinished(); |
472 | |
473 | public slots: |
474 | |
475 | /** @brief Starts the consumer. */ |
476 | void start(); |
477 | |
478 | /** @brief Stops the consumer. */ |
479 | void stop(); |
480 | int getLength(); |
481 | |
482 | /** @brief Checks if the file is readable by MLT. */ |
483 | bool isValid(const KUrl &url); |
484 | |
485 | void slotSplitView(bool doit); |
486 | void slotSwitchFullscreen(); |
487 | void slotSetVolume(int volume); |
488 | void seekToFrame(int pos); |
489 | /** @brief Starts a timer to query for a refresh. */ |
490 | void doRefresh(); |
491 | }; |
492 | |
493 | #endif |
494 | |