]> git.sesse.net Git - kdenlive/blob - src/widgets/videoglwidget.cpp
Add an fps counter for debugging.
[kdenlive] / src / widgets / videoglwidget.cpp
1 /***************************************************************************
2  *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *               2014 by Jean-Nicolas Artaud (jeannicolasartaud@gmail.com) *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
19  ***************************************************************************/
20
21 #include <QtGui>
22 #include <QtOpenGL>
23 #ifdef Q_WS_MAC
24 #include <OpenGL/glu.h>
25 #else
26 #include <GL/glu.h>
27 #endif
28 #include "widgets/videoglwidget.h"
29 extern "C" {
30 GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout);
31 }
32 #include <mlt++/Mlt.h>
33
34 #ifndef GL_TEXTURE_RECTANGLE_EXT
35 #define GL_TEXTURE_RECTANGLE_EXT GL_TEXTURE_RECTANGLE_NV
36 #endif
37
38 VideoGLWidget::VideoGLWidget(QWidget *parent, QGLWidget *share)
39     : QGLWidget(parent, share)
40     , x(0)
41     , y(0)
42     , w(width())
43     , h(height())
44     , m_image_width(0)
45     , m_image_height(0)
46     , m_texture(0)
47     , m_frame(NULL)
48     , m_frame_texture(0)
49     , m_display_ratio(4.0 / 3.0)
50     , m_backgroundColor(Qt::gray)
51 {  
52     setAttribute(Qt::WA_PaintOnScreen);
53     setAttribute(Qt::WA_OpaquePaintEvent);
54 }
55
56 VideoGLWidget::~VideoGLWidget()
57 {
58     makeCurrent();
59     if (m_texture)
60         glDeleteTextures(1, &m_texture);
61     // m_frame will be cleaned up when the profile is closed by Render.
62 }
63
64 QSize VideoGLWidget::minimumSizeHint() const
65 {
66     return QSize(40, 30);
67 }
68
69 QSize VideoGLWidget::sizeHint() const
70 {
71     return QSize(400, 300);
72 }
73
74 void VideoGLWidget::setImageAspectRatio(double ratio)
75 {
76     m_display_ratio = ratio;
77     resizeGL(width(), height());
78 }
79
80 void VideoGLWidget::initializeGL()
81 {
82     qglClearColor(m_backgroundColor);
83     glShadeModel(GL_FLAT);
84     glDisable(GL_DEPTH_TEST);
85     glDisable(GL_CULL_FACE);
86     glDisable(GL_LIGHTING);
87     glDisable(GL_DITHER);
88     glDisable(GL_BLEND);
89     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
90 }
91
92 void VideoGLWidget::resizeEvent(QResizeEvent* event)
93 {
94     resizeGL(event->size().width(),event->size().height());
95 }
96
97 void VideoGLWidget::resizeGL(int width, int height)
98 {
99     double this_aspect = (double) width / height;
100
101     // Special case optimisation to negate odd effect of sample aspect ratio
102     // not corresponding exactly with image resolution.
103     if ((int)(this_aspect * 1000) == (int)(m_display_ratio * 1000)) {
104         w = width;
105         h = height;
106     }
107     // Use OpenGL to normalise sample aspect ratio
108     else if (height * m_display_ratio > width) {
109         w = width;
110         h = width / m_display_ratio;
111     } else {
112         w = height * m_display_ratio;
113         h = height;
114     }
115     x = (width - w) / 2;
116     y = (height - h) / 2;
117
118     glViewport(0, 0, width, height);
119     glMatrixMode(GL_PROJECTION);
120     glLoadIdentity();
121     gluOrtho2D(0, width, height, 0);
122     glMatrixMode(GL_MODELVIEW);
123     glClear(GL_COLOR_BUFFER_BIT);
124 }
125
126 void VideoGLWidget::activateMonitor()
127 {
128     makeCurrent();
129     glViewport(0, 0, width(), height());
130     glMatrixMode(GL_PROJECTION);
131     glLoadIdentity();
132     gluOrtho2D(0, width(), height(), 0);
133     glMatrixMode(GL_MODELVIEW);
134     glClear(GL_COLOR_BUFFER_BIT);
135 }
136
137 void VideoGLWidget::paintGL()
138 {
139     if (m_texture) {
140 #ifdef Q_WS_MAC
141                 glClear(GL_COLOR_BUFFER_BIT);
142 #endif
143         glEnable(GL_TEXTURE_RECTANGLE_EXT);
144         glBegin(GL_QUADS);
145         glTexCoord2i(0, 0);
146         glVertex2i(x, y);
147         glTexCoord2i(m_image_width, 0);
148         glVertex2i(x + w, y);
149         glTexCoord2i(m_image_width, m_image_height);
150         glVertex2i(x + w, y + h);
151         glTexCoord2i(0, m_image_height);
152         glVertex2i(x, y + h);
153         glEnd();
154         glDisable(GL_TEXTURE_RECTANGLE_EXT);
155     }
156     if (m_frame_texture) {
157 #ifdef Q_WS_MAC
158                 glClear(GL_COLOR_BUFFER_BIT);
159 #endif
160         glEnable(GL_TEXTURE_2D);
161         glBindTexture(GL_TEXTURE_2D, m_frame_texture);
162         glBegin(GL_QUADS);
163         glTexCoord2i(0, 0);
164         glVertex2i(x, y);
165         glTexCoord2i(1, 0);
166         glVertex2i(x + w, y);
167         glTexCoord2i(1, 1);
168         glVertex2i(x + w, y + h);
169         glTexCoord2i(0, 1);
170         glVertex2i(x, y + h);
171         glEnd();
172         glDisable(GL_TEXTURE_2D);
173     }
174 }
175
176 void VideoGLWidget::showImage(const QImage &image)
177 {
178     m_image_width = image.width();
179     m_image_height = image.height();
180     makeCurrent();
181     if (m_texture)
182         glDeleteTextures(1, &m_texture);
183     delete m_frame;
184     m_frame = NULL;
185     m_frame_texture = 0;
186
187     glPixelStorei(GL_UNPACK_ROW_LENGTH, m_image_width);
188     glGenTextures(1, &m_texture);
189     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_texture);
190     glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
191     glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
192     glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA8, m_image_width, m_image_height, 0, GL_RGB,
193                  GL_UNSIGNED_BYTE, image.bits());
194     updateGL();
195 }
196
197 void VideoGLWidget::showImage(Mlt::Frame* frame, GLuint texnum)
198 {
199     static bool first = true;
200     static timespec start, now;
201     static int frameno = 0;
202
203     if (first) {
204         clock_gettime(CLOCK_MONOTONIC, &start);
205         first = false;
206     }
207
208     ++frameno;
209
210     clock_gettime(CLOCK_MONOTONIC, &now);
211     double elapsed = now.tv_sec - start.tv_sec +
212         1e-9 * (now.tv_nsec - start.tv_nsec);
213     printf("%d frames in %.3f seconds = %.1f fps (%.1f ms/frame)\n",
214         frameno, elapsed, frameno / elapsed,
215         1e3 * elapsed / frameno);
216
217     // Reset every 100 frames, so that local variations in frame times
218     // (especially for the first few frames, when the shaders are
219     // compiled etc.) don't make it hard to measure for the entire
220     // remaining duration of the program.
221     if (frameno == 100) {
222         frameno = 0;
223         start = now;
224     }
225
226     makeCurrent();
227     GLsync sync = (GLsync) frame->get("movit.convert.fence");
228     glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
229     if (m_texture) {
230         glDeleteTextures(1, &m_texture);
231         m_texture = 0;
232     }
233     delete m_frame;
234     m_frame = frame;
235     m_frame_texture = texnum;
236
237     updateGL();
238 }
239
240 void VideoGLWidget::mouseDoubleClickEvent(QMouseEvent * event)
241 {
242     // TODO: disable screensaver?
243     Qt::WindowFlags flags = windowFlags();
244     if (!isFullScreen()) {
245         // Check if we ahave a multiple monitor setup
246 #if QT_VERSION >= 0x040600
247         int monitors = QApplication::desktop()->screenCount();
248 #else
249         int monitors = QApplication::desktop()->numScreens();
250 #endif
251         if (monitors > 1) {
252             QRect screenres;
253             // Move monitor widget to the second screen (one screen for Kdenlive, the other one for the Monitor widget
254             int currentScreen = QApplication::desktop()->screenNumber(this);
255             if (currentScreen < monitors - 1)
256                 screenres = QApplication::desktop()->screenGeometry(currentScreen + 1);
257             else
258                 screenres = QApplication::desktop()->screenGeometry(currentScreen - 1);
259             move(QPoint(screenres.x(), screenres.y()));
260             resize(screenres.width(), screenres.height());
261         }
262
263         m_baseFlags = flags & (Qt::Window | Qt::SubWindow);
264         flags |= Qt::Window;
265         flags ^= Qt::SubWindow;
266         setWindowFlags(flags);
267 #ifdef Q_WS_X11
268         // This works around a bug with Compiz
269         // as the window must be visible before we can set the state
270         show();
271         raise();
272         setWindowState(windowState() | Qt::WindowFullScreen);   // set
273 #else
274         setWindowState(windowState() | Qt::WindowFullScreen);   // set
275         show();
276 #endif
277     } else {
278         flags ^= (Qt::Window | Qt::SubWindow); //clear the flags...
279         flags |= m_baseFlags; //then we reset the flags (window and subwindow)
280         setWindowFlags(flags);
281         setWindowState(windowState()  ^ Qt::WindowFullScreen);   // reset
282         show();
283     }
284     event->accept();
285 }
286
287
288 #include "videoglwidget.moc"