glwidget.h 12.2 KB
Newer Older
1 2 3
/*
 * Copyright (c) 2011-2014 Meltytech, LLC
 * Author: Dan Dennedy <dan@dennedy.org>
Laurent Montel's avatar
Laurent Montel committed
4
 *
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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 3 of the License, or
 * (at your option) any later version.
 *
 * 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 GLWIDGET_H
#define GLWIDGET_H

Nicolas Carion's avatar
Nicolas Carion committed
22 23 24 25
#include <QMutex>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
26 27
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
Nicolas Carion's avatar
Nicolas Carion committed
28
#include <QQuickView>
29 30 31
#include <QApplication>
#include <QFont>
#include <QFontMetrics>
32
#include <QRect>
Nicolas Carion's avatar
Nicolas Carion committed
33 34
#include <QSemaphore>
#include <QThread>
35
#include <QTimer>
36

Nicolas Carion's avatar
style  
Nicolas Carion committed
37
#include "bin/model/markerlistmodel.hpp"
38
#include "definitions.h"
Nicolas Carion's avatar
Nicolas Carion committed
39
#include "scopes/sharedframe.h"
40 41 42

class QOpenGLFunctions_3_2_Core;

Nicolas Carion's avatar
Nicolas Carion committed
43
namespace Mlt {
44
class Filter;
Nicolas Carion's avatar
Nicolas Carion committed
45 46 47
class Producer;
class Consumer;
class Profile;
48 49 50 51
}

class RenderThread;
class FrameRenderer;
52
class MonitorProxy;
53

Laurent Montel's avatar
Laurent Montel committed
54
typedef void *(*thread_function_t)(void *);
55 56 57 58 59 60 61 62 63

class GLWidget : public QQuickView, protected QOpenGLFunctions
{
    Q_OBJECT
    Q_PROPERTY(QRect rect READ rect NOTIFY rectChanged)
    Q_PROPERTY(float zoom READ zoom NOTIFY zoomChanged)
    Q_PROPERTY(QPoint offset READ offset NOTIFY offsetChanged)

public:
64
    friend class MonitorController;
65
    friend class Monitor;
66

Laurent Montel's avatar
Laurent Montel committed
67
    GLWidget(int id, QObject *parent = nullptr);
68 69
    ~GLWidget();

70
    int requestedSeekPosition;
Laurent Montel's avatar
Laurent Montel committed
71
    void createThread(RenderThread **thread, thread_function_t function, void *data);
72 73
    void startGlsl();
    void stopGlsl();
74
    void clear();
Laurent Montel's avatar
Laurent Montel committed
75
    int reconfigureMulti(const QString &params, const QString &path, Mlt::Profile *profile);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
76
    void stopCapture();
Laurent Montel's avatar
Laurent Montel committed
77
    int reconfigure(Mlt::Profile *profile = nullptr);
78 79
    /** @brief Get the current MLT producer playlist.
     * @return A string describing the playlist */
80
    const QString sceneList(const QString &root, const QString &fullPath = QString());
81

Nicolas Carion's avatar
Nicolas Carion committed
82
    int displayWidth() const { return m_rect.width(); }
83
    void updateAudioForAnalysis();
Nicolas Carion's avatar
Nicolas Carion committed
84
    int displayHeight() const { return m_rect.height(); }
85

Nicolas Carion's avatar
Nicolas Carion committed
86 87 88 89
    QObject *videoWidget() { return this; }
    Mlt::Filter *glslManager() const { return m_glslManager; }
    QRect rect() const { return m_rect; }
    QRect effectRect() const { return m_effectRect; }
90
    float zoom() const;
91
    float scale() const;
92 93 94
    QPoint offset() const;
    Mlt::Consumer *consumer();
    Mlt::Producer *producer();
95
    QSize profileSize() const;
96
    QRect displayRect() const;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
97 98
    /** @brief set to true if we want to emit a QImage of the frame for analysis */
    bool sendFrameForAnalysis;
99
    void updateGamma();
100
    Mlt::Profile *profile();
101
    void reloadProfile();
102 103
    void lockMonitor();
    void releaseMonitor();
104
    int realTime() const;
Laurent Montel's avatar
Laurent Montel committed
105
    void setAudioThumb(int channels = 0, const QVariantList &audioCache = QList<QVariant>());
106 107
    int droppedFrames() const;
    void resetDrops();
108
    bool checkFrameNumber(int pos);
109 110
    /** @brief Return current timeline position */
    int getCurrentPos() const;
111 112
    /** @brief Requests a monitor refresh */
    void requestRefresh();
113
    void setRulerInfo(int duration, std::shared_ptr<MarkerListModel> model = nullptr);
114
    MonitorProxy *getControllerProxy();
115 116 117 118 119
    bool playZone(bool loop = false);
    bool loopClip();
    void startConsumer();
    void stop();
    int rulerHeight() const;
120 121 122 123 124 125 126 127 128 129
    /** @brief return current play producer's playing speed */
    double playSpeed() const;
    /** @brief Turn drop frame feature on/off */
    void setDropFrames(bool drop);
    /** @brief Returns current audio volume */
    int volume() const;
    /** @brief Set audio volume on consumer */
    void setVolume(double volume);
    /** @brief Returns current producer's duration in frames */
    int duration() const;
130

131
protected:
132 133 134
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseDoubleClickEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;
135
    /** @brief Update producer, should ONLY be called from monitor */
136
    int setProducer(Mlt::Producer *producer, bool isActive, int position = -1);
137 138

public slots:
139 140
    void seek(int pos);
    void requestSeek();
141
    void setZoom(float zoom);
142 143
    void setOffsetX(int x, int max);
    void setOffsetY(int y, int max);
144
    void slotSwitchAudioOverlay(bool enable);
145
    void slotZoomScene(double value);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
146
    void initializeGL();
147
    void releaseAnalyse();
148
    void switchPlay(bool play, double speed = 1.0);
149

150
signals:
Laurent Montel's avatar
Laurent Montel committed
151
    void frameDisplayed(const SharedFrame &frame);
152 153 154 155 156 157 158 159 160 161 162
    void textureUpdated();
    void dragStarted();
    void seekTo(int x);
    void gpuNotSupported();
    void started();
    void paused();
    void playing();
    void rectChanged();
    void zoomChanged();
    void offsetChanged();
    void monitorPlay();
163
    void switchFullScreen(bool minimizeOnly = false);
164
    void mouseSeek(int eventDelta, uint modifiers);
165
    void startDrag();
Nicolas Carion's avatar
Nicolas Carion committed
166
    void analyseFrame(const QImage &);
Laurent Montel's avatar
Laurent Montel committed
167
    void audioSamplesSignal(const audioShortVector &, int, int, int);
Laurent Montel's avatar
Laurent Montel committed
168
    void showContextMenu(const QPoint &);
169
    void lockMonitor(bool);
170
    void passKeyEvent(QKeyEvent *);
Laurent Montel's avatar
Laurent Montel committed
171
    void panView(const QPoint &diff);
172
    void seekPosition(int);
173
    void activateMonitor();
174

175 176 177 178 179 180 181
protected:
    Mlt::Filter *m_glslManager;
    Mlt::Consumer *m_consumer;
    Mlt::Producer *m_producer;
    Mlt::Profile *m_monitorProfile;
    QMutex m_mutex;

182
private:
183
    int m_id;
184
    QRect m_rect;
185
    QRect m_effectRect;
186
    GLuint m_texture[3];
Laurent Montel's avatar
Laurent Montel committed
187
    QOpenGLShaderProgram *m_shader;
188
    QPoint m_panStart;
189 190
    QPoint m_dragStart;
    QSemaphore m_initSem;
191
    QSemaphore m_analyseSem;
192
    bool m_isInitialized;
Laurent Montel's avatar
Laurent Montel committed
193 194 195 196 197 198
    Mlt::Event *m_threadStartEvent;
    Mlt::Event *m_threadStopEvent;
    Mlt::Event *m_threadCreateEvent;
    Mlt::Event *m_threadJoinEvent;
    Mlt::Event *m_displayEvent;
    FrameRenderer *m_frameRenderer;
199 200 201 202 203 204
    int m_projectionLocation;
    int m_modelViewLocation;
    int m_vertexLocation;
    int m_texCoordLocation;
    int m_colorspaceLocation;
    int m_textureLocation[3];
205
    QTimer m_refreshTimer;
206
    float m_zoom;
207
    bool m_openGLSync;
208
    bool m_sendFrame;
209 210
    bool m_isZoneMode;
    bool m_isLoopMode;
211
    SharedFrame m_sharedFrame;
212
    QPoint m_offset;
213
    QOffscreenSurface m_offscreenSurface;
Laurent Montel's avatar
Laurent Montel committed
214
    QOpenGLContext *m_shareContext;
215
    bool m_audioWaveDisplayed;
216
    MonitorProxy *m_proxy;
217
    QScopedPointer<Mlt::Producer> m_blackClip;
Laurent Montel's avatar
Laurent Montel committed
218 219 220
    static void on_frame_show(mlt_consumer, void *self, mlt_frame frame);
    static void on_gl_frame_show(mlt_consumer, void *self, mlt_frame frame_ptr);
    static void on_gl_nosync_frame_show(mlt_consumer, void *self, mlt_frame frame_ptr);
221 222 223
    void createAudioOverlay(bool isAudio);
    void removeAudioOverlay();
    void adjustAudioOverlay(bool isAudio);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
224
    QOpenGLFramebufferObject *m_fbo;
225
    void refreshSceneLayout();
226
    void resetZoneMode();
227 228 229 230 231

private slots:
    void resizeGL(int width, int height);
    void updateTexture(GLuint yName, GLuint uName, GLuint vName);
    void paintGL();
232
    void onFrameDisplayed(const SharedFrame &frame);
233
    void refresh();
234 235

protected:
236 237 238 239
    void resizeEvent(QResizeEvent *event) override;
    void mousePressEvent(QMouseEvent *) override;
    void mouseMoveEvent(QMouseEvent *) override;
    void keyPressEvent(QKeyEvent *event) override;
240 241 242 243 244 245 246
    void createShader();
};

class RenderThread : public QThread
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
247
    RenderThread(thread_function_t function, void *data, QOpenGLContext *context, QSurface *surface);
248
    ~RenderThread();
249 250

protected:
251
    void run() override;
252 253 254

private:
    thread_function_t m_function;
Laurent Montel's avatar
Laurent Montel committed
255 256 257
    void *m_data;
    QOpenGLContext *m_context;
    QSurface *m_surface;
258 259 260 261 262 263
};

class FrameRenderer : public QThread
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
264
    explicit FrameRenderer(QOpenGLContext *shareContext, QSurface *surface);
265
    ~FrameRenderer();
Nicolas Carion's avatar
Nicolas Carion committed
266 267
    QSemaphore *semaphore() { return &m_semaphore; }
    QOpenGLContext *context() const { return m_context; }
268
    Q_INVOKABLE void showFrame(Mlt::Frame frame);
269
    Q_INVOKABLE void showGLFrame(Mlt::Frame frame);
270
    Q_INVOKABLE void showGLNoSyncFrame(Mlt::Frame frame);
271 272 273 274 275 276

public slots:
    void cleanup();

signals:
    void textureReady(GLuint yName, GLuint uName = 0, GLuint vName = 0);
Laurent Montel's avatar
Laurent Montel committed
277 278
    void frameDisplayed(const SharedFrame &frame);
    void audioSamplesSignal(const audioShortVector &, int, int, int);
279 280 281

private:
    QSemaphore m_semaphore;
282
    SharedFrame m_displayFrame;
Laurent Montel's avatar
Laurent Montel committed
283 284
    QOpenGLContext *m_context;
    QSurface *m_surface;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
285

286 287 288
public:
    GLuint m_renderTexture[3];
    GLuint m_displayTexture[3];
Laurent Montel's avatar
Laurent Montel committed
289
    QOpenGLFunctions_3_2_Core *m_gl32;
290
    bool sendAudioForAnalysis;
291 292
};

293 294 295
class MonitorProxy : public QObject
{
    Q_OBJECT
Nicolas Carion's avatar
style  
Nicolas Carion committed
296
    // Q_PROPERTY(int consumerPosition READ consumerPosition NOTIFY consumerPositionChanged)
297
    Q_PROPERTY(int position READ position NOTIFY positionChanged)
298
    Q_PROPERTY(int seekPosition READ seekPosition WRITE setSeekPosition NOTIFY seekPositionChanged)
299 300
    Q_PROPERTY(int zoneIn READ zoneIn WRITE setZoneIn NOTIFY zoneChanged)
    Q_PROPERTY(int zoneOut READ zoneOut WRITE setZoneOut NOTIFY zoneChanged)
301
    Q_PROPERTY(int rulerHeight READ rulerHeight NOTIFY rulerHeightChanged)
302
    Q_PROPERTY(QString markerComment READ markerComment NOTIFY markerCommentChanged)
303
public:
Nicolas Carion's avatar
style  
Nicolas Carion committed
304 305 306
    MonitorProxy(GLWidget *parent)
        : QObject(parent)
        , q(parent)
307
        , m_position(0)
Nicolas Carion's avatar
style  
Nicolas Carion committed
308
        , m_seekPosition(-1)
309 310
        , m_zoneIn(0)
        , m_zoneOut(-1)
311
        , m_rulerHeight(QFontMetrics(QApplication::font()).lineSpacing() * 0.7)
312 313
    {
    }
Nicolas Carion's avatar
style  
Nicolas Carion committed
314
    int seekPosition() const { return m_seekPosition; }
315
    int position() const { return m_position; }
316
    int rulerHeight() const { return m_rulerHeight; }
317
    QString markerComment() const { return m_markerComment; }
318 319
    Q_INVOKABLE void requestSeekPosition(int pos)
    {
320
        q->activateMonitor();
321 322 323 324
        m_seekPosition = pos;
        emit seekPositionChanged();
        emit seekRequestChanged();
    }
325 326 327 328 329 330 331 332 333 334 335 336 337
    void setPosition(int pos)
    {
        m_position = pos;
        emit positionChanged();
    }
    void setMarkerComment(const QString &comment)
    {
        if (m_markerComment == comment) {
            return;
        }
        m_markerComment = comment;
        emit markerCommentChanged();
    }
Nicolas Carion's avatar
style  
Nicolas Carion committed
338 339
    void setSeekPosition(int pos)
    {
340 341 342
        m_seekPosition = pos;
        emit seekPositionChanged();
    }
343 344 345 346 347 348
    void pauseAndSeek(int pos)
    {
        q->switchPlay(false);
        m_seekPosition = pos;
        emit seekPositionChanged();
    }
349 350 351 352
    int zoneIn() const { return m_zoneIn; }
    int zoneOut() const { return m_zoneOut; }
    void setZoneIn(int pos)
    {
353 354 355
        if (m_zoneIn > 0) {
            emit removeSnap(m_zoneIn);
        }
356
        m_zoneIn = pos;
357 358 359
        if (pos > 0) {
            emit addSnap(pos);
        }
360 361 362 363
        emit zoneChanged();
    }
    void setZoneOut(int pos)
    {
364 365 366
        if (m_zoneOut > 0) {
            emit removeSnap(m_zoneOut);
        }
367
        m_zoneOut = pos;
368 369 370
        if (pos > 0) {
            emit addSnap(pos);
        }
371 372 373 374
        emit zoneChanged();
    }
    Q_INVOKABLE void setZone(int in, int out)
    {
375 376 377 378 379 380
        if (m_zoneIn > 0) {
            emit removeSnap(m_zoneIn);
        }
        if (m_zoneOut > 0) {
            emit removeSnap(m_zoneOut);
        }
381 382
        m_zoneIn = in;
        m_zoneOut = out;
383 384 385 386 387 388
        if (m_zoneIn > 0) {
            emit addSnap(m_zoneIn);
        }
        if (m_zoneOut > 0) {
            emit addSnap(m_zoneOut);
        }
389 390
        emit zoneChanged();
    }
391 392
    void setZone(QPoint zone)
    {
393 394 395 396 397 398
        setZone(zone.x(), zone.y());
    }
    void resetZone()
    {
        m_zoneIn = 0;
        m_zoneOut = -1;
399
    }
Nicolas Carion's avatar
style  
Nicolas Carion committed
400
    QPoint zone() const { return QPoint(m_zoneIn, m_zoneOut); }
401
signals:
402
    void positionChanged();
403
    void seekPositionChanged();
404
    void seekRequestChanged();
405
    void zoneChanged();
406
    void markerCommentChanged();
407
    void rulerHeightChanged();
408 409
    void addSnap(int);
    void removeSnap(int);
410 411 412 413 414

private:
    GLWidget *q;
    int m_position;
    int m_seekPosition;
415 416
    int m_zoneIn;
    int m_zoneOut;
417
    int m_rulerHeight;
418
    QString m_markerComment;
419 420
};

421
#endif