| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
1 |
/**************************************************************************** |
|
2 |
** |
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 |
** All rights reserved. |
|
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the Qt Mobility Components. |
|
8 |
** |
|
9 |
** $QT_BEGIN_LICENSE:LGPL$ |
|
10 |
** GNU Lesser General Public License Usage |
| 5684483 by Jyri Tahtela at 2011-06-30 |
11 |
** This file may be used under the terms of the GNU Lesser General Public |
|
12 |
** License version 2.1 as published by the Free Software Foundation and |
|
13 |
** appearing in the file LICENSE.LGPL included in the packaging of this |
|
14 |
** file. Please review the following information to ensure the GNU Lesser |
|
15 |
** General Public License version 2.1 requirements will be met: |
|
16 |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
17 |
** |
|
18 |
** In addition, as a special exception, Nokia gives you certain additional |
| 5684483 by Jyri Tahtela at 2011-06-30 |
19 |
** rights. These rights are described in the Nokia Qt LGPL Exception |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
20 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
21 |
** |
| 5684483 by Jyri Tahtela at 2011-06-30 |
22 |
** GNU General Public License Usage |
|
23 |
** Alternatively, this file may be used under the terms of the GNU General |
|
24 |
** Public License version 3.0 as published by the Free Software Foundation |
|
25 |
** and appearing in the file LICENSE.GPL included in the packaging of this |
|
26 |
** file. Please review the following information to ensure the GNU General |
|
27 |
** Public License version 3.0 requirements will be met: |
|
28 |
** http://www.gnu.org/copyleft/gpl.html. |
|
29 |
** |
|
30 |
** Other Usage |
|
31 |
** Alternatively, this file may be used in accordance with the terms and |
|
32 |
** conditions contained in a signed written agreement between you and Nokia. |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
33 |
** |
|
34 |
** |
|
35 |
** |
|
36 |
** |
|
37 |
** |
|
38 |
** $QT_END_LICENSE$ |
|
39 |
** |
|
40 |
****************************************************************************/ |
|
41 |
|
|
42 |
#include "qvideosurfacegstsink.h" |
|
43 |
#include "qabstractvideosurface.h" |
| 70f91df by Dmytro Poplavskiy at 2011-03-31 |
44 |
#include "qgstutils.h" |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
45 |
|
|
46 |
#include <QtGui/qevent.h> |
|
47 |
#include <QtGui/qapplication.h> |
|
48 |
#include <QtGui/qx11info_x11.h> |
|
49 |
#include <QtCore/qdebug.h> |
|
50 |
#include <QtCore/qthread.h> |
|
51 |
|
|
52 |
#include <QtOpenGL/qgl.h> |
|
53 |
|
|
54 |
#include <gst/gst.h> |
|
55 |
#include <gst/interfaces/xoverlay.h> |
|
56 |
#include <gst/interfaces/propertyprobe.h> |
|
57 |
#include <gst/interfaces/meegovideotexture.h> |
|
58 |
#include <gst/interfaces/meegovideorenderswitch.h> |
|
59 |
|
|
60 |
|
|
61 |
#include <EGL/egl.h> |
|
62 |
#include <EGL/eglext.h> |
|
63 |
|
|
64 |
#include "qgstreamergltexturerenderer.h" |
|
65 |
|
|
66 |
//#define GL_TEXTURE_SINK_DEBUG 1 |
|
67 |
|
|
68 |
//from extdefs.h |
|
69 |
typedef void *EGLSyncKHR; |
|
70 |
typedef khronos_utime_nanoseconds_t EGLTimeKHR; |
|
71 |
|
|
72 |
#define GL_TEXTURE_EXTERNAL_OES 0x8D65 |
|
73 |
#define EGL_SYNC_FENCE_KHR 0x30F9 |
|
74 |
|
|
75 |
typedef EGLSyncKHR (EGLAPIENTRYP _PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, |
|
76 |
EGLenum type, const EGLint * attrib_list); |
|
77 |
typedef EGLBoolean (EGLAPIENTRYP _PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, |
|
78 |
EGLSyncKHR sync); |
|
79 |
|
|
80 |
|
|
81 |
const QAbstractVideoBuffer::HandleType EGLImageTextureHandle = |
|
82 |
QAbstractVideoBuffer::HandleType(QAbstractVideoBuffer::UserHandle+3434); |
|
83 |
|
|
84 |
// EGLSync functions |
|
85 |
_PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; |
|
86 |
_PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; |
|
87 |
|
|
88 |
class QGStreamerGLTextureBuffer : public QAbstractVideoBuffer |
|
89 |
{ |
|
90 |
public: |
|
91 |
QGStreamerGLTextureBuffer(MeegoGstVideoTexture *textureSink, int frameNumber) : |
|
92 |
QAbstractVideoBuffer(EGLImageTextureHandle), |
|
93 |
m_textureSink(MEEGO_GST_VIDEO_TEXTURE(textureSink)), |
| 768a35f by Dmytro Poplavskiy at 2011-04-15 |
94 |
m_frameNumber(frameNumber) |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
95 |
{ |
|
96 |
} |
|
97 |
|
|
98 |
~QGStreamerGLTextureBuffer() |
|
99 |
{ |
|
100 |
} |
|
101 |
|
|
102 |
|
|
103 |
MapMode mapMode() const { return NotMapped; } |
|
104 |
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) |
|
105 |
{ |
|
106 |
Q_UNUSED(mode); |
|
107 |
Q_UNUSED(numBytes); |
|
108 |
Q_UNUSED(bytesPerLine); |
|
109 |
|
| 768a35f by Dmytro Poplavskiy at 2011-04-15 |
110 |
//acquire_frame should really be called at buffer construction time |
| e600f44 by Dmytro Poplavskiy at 2011-04-15 |
111 |
//but it conflicts with id-less implementation of gst texture sink. |
| 768a35f by Dmytro Poplavskiy at 2011-04-15 |
112 |
#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 |
|
113 |
qDebug() << "acquire frame" << m_frameNumber; |
|
114 |
#endif |
|
115 |
if (!meego_gst_video_texture_acquire_frame(m_textureSink,m_frameNumber)) |
|
116 |
qWarning() << Q_FUNC_INFO << "acquire-frame failed" << m_frameNumber; |
|
117 |
|
|
118 |
|
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
119 |
#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 |
|
120 |
qDebug() << "map frame" << m_frameNumber; |
|
121 |
#endif |
|
122 |
|
|
123 |
gboolean bind_status = meego_gst_video_texture_bind_frame(m_textureSink, GL_TEXTURE_EXTERNAL_OES, m_frameNumber); |
|
124 |
if (!bind_status) |
|
125 |
qWarning() << Q_FUNC_INFO << "bind-frame failed"; |
|
126 |
|
|
127 |
return (uchar*)1; |
|
128 |
} |
|
129 |
|
|
130 |
void unmap() |
|
131 |
{ |
|
132 |
gboolean bind_status = meego_gst_video_texture_bind_frame(m_textureSink, GL_TEXTURE_EXTERNAL_OES, -1); |
|
133 |
|
|
134 |
#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 |
|
135 |
qDebug() << "unmap frame" << m_frameNumber; |
|
136 |
#endif |
|
137 |
|
| 768a35f by Dmytro Poplavskiy at 2011-04-15 |
138 |
if (!bind_status) |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
139 |
qWarning() << Q_FUNC_INFO << "unbind-frame failed"; |
| 768a35f by Dmytro Poplavskiy at 2011-04-15 |
140 |
|
|
141 |
//release_frame should really be called in destructor |
| e600f44 by Dmytro Poplavskiy at 2011-04-15 |
142 |
//but this conflicts with id-less implementation of gst texture sink. |
| 768a35f by Dmytro Poplavskiy at 2011-04-15 |
143 |
#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 |
|
144 |
qDebug() << "release frame" << m_frameNumber; |
|
145 |
#endif |
|
146 |
EGLSyncKHR sync = eglCreateSyncKHR(eglGetDisplay((EGLNativeDisplayType)QX11Info::display()), EGL_SYNC_FENCE_KHR, NULL); |
|
147 |
meego_gst_video_texture_release_frame(m_textureSink, m_frameNumber, sync); |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
148 |
} |
|
149 |
|
|
150 |
QVariant handle() const |
|
151 |
{ |
|
152 |
return m_frameNumber; |
|
153 |
} |
|
154 |
|
|
155 |
private: |
|
156 |
MeegoGstVideoTexture *m_textureSink; |
|
157 |
int m_frameNumber; |
|
158 |
}; |
|
159 |
|
|
160 |
|
|
161 |
QGstreamerGLTextureRenderer::QGstreamerGLTextureRenderer(QObject *parent) : |
|
162 |
QVideoRendererControl(parent), |
|
163 |
m_videoSink(0), |
|
164 |
m_surface(0), |
|
165 |
m_context(0), |
|
166 |
m_winId(0), |
|
167 |
m_colorKey(49,0,49), |
|
168 |
m_overlayEnabled(false), |
| 0669179 by Dmytro Poplavskiy at 2011-10-03 |
169 |
m_glEnabled(true), |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
170 |
m_bufferProbeId(-1) |
|
171 |
{ |
|
172 |
eglCreateSyncKHR = |
|
173 |
(_PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR"); |
|
174 |
eglDestroySyncKHR = |
|
175 |
(_PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR"); |
|
176 |
} |
|
177 |
|
|
178 |
QGstreamerGLTextureRenderer::~QGstreamerGLTextureRenderer() |
|
179 |
{ |
|
180 |
if (m_surface && m_surface->isActive()) |
|
181 |
m_surface->stop(); |
|
182 |
|
|
183 |
if (m_videoSink) |
|
184 |
gst_object_unref(GST_OBJECT(m_videoSink)); |
|
185 |
} |
|
186 |
|
|
187 |
GstElement *QGstreamerGLTextureRenderer::videoSink() |
|
188 |
{ |
|
189 |
if (!m_videoSink && isReady()) { |
|
190 |
if (m_context && !m_surface->supportedPixelFormats(EGLImageTextureHandle).isEmpty()) { |
|
191 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
192 |
qDebug() << Q_FUNC_INFO << ": using gltexture sink"; |
|
193 |
#endif |
|
194 |
if (m_context) |
|
195 |
m_context->makeCurrent(); |
|
196 |
m_videoSink = gst_element_factory_make("gltexturesink", "egl-texture-sink"); |
|
197 |
g_object_set(G_OBJECT(m_videoSink), |
|
198 |
"x-display", QX11Info::display(), |
|
199 |
"egl-display", eglGetDisplay((EGLNativeDisplayType)QX11Info::display()), |
| 0669179 by Dmytro Poplavskiy at 2011-10-03 |
200 |
"egl-context", m_glEnabled ? eglGetCurrentContext() |
|
201 |
: EGL_NO_CONTEXT, |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
202 |
"colorkey", m_colorKey.rgb(), |
|
203 |
"autopaint-colorkey", false, |
|
204 |
"use-framebuffer-memory", true, |
| 15705ea by Dmytro Poplavskiy at 2011-05-10 |
205 |
"render-mode", m_overlayEnabled ? VIDEO_RENDERSWITCH_XOVERLAY_MODE |
|
206 |
: VIDEO_RENDERSWITCH_TEXTURE_STREAMING_MODE, |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
207 |
(char*)NULL); |
|
208 |
|
|
209 |
g_signal_connect(G_OBJECT(m_videoSink), "frame-ready", G_CALLBACK(handleFrameReady), (gpointer)this); |
|
210 |
} else { |
|
211 |
qWarning() << Q_FUNC_INFO << ": Fallback to QVideoSurfaceGstSink since EGLImageTextureHandle is not supported"; |
|
212 |
m_videoSink = reinterpret_cast<GstElement*>(QVideoSurfaceGstSink::createSink(m_surface)); |
|
213 |
} |
|
214 |
|
|
215 |
if (m_videoSink) { |
|
216 |
gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership |
|
217 |
gst_object_sink(GST_OBJECT(m_videoSink)); |
|
218 |
|
|
219 |
GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); |
|
220 |
m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); |
|
221 |
} |
|
222 |
} |
|
223 |
|
|
224 |
return m_videoSink; |
|
225 |
} |
|
226 |
|
|
227 |
QAbstractVideoSurface *QGstreamerGLTextureRenderer::surface() const |
|
228 |
{ |
|
229 |
return m_surface; |
|
230 |
} |
|
231 |
|
|
232 |
void QGstreamerGLTextureRenderer::setSurface(QAbstractVideoSurface *surface) |
|
233 |
{ |
|
234 |
if (m_surface != surface) { |
|
235 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
236 |
qDebug() << Q_FUNC_INFO << surface; |
|
237 |
#endif |
|
238 |
|
|
239 |
bool oldReady = isReady(); |
|
240 |
|
| 0669179 by Dmytro Poplavskiy at 2011-10-03 |
241 |
m_context = m_glEnabled ? const_cast<QGLContext*>(QGLContext::currentContext()) : NULL; |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
242 |
|
|
243 |
if (m_videoSink) |
|
244 |
gst_object_unref(GST_OBJECT(m_videoSink)); |
|
245 |
|
|
246 |
m_videoSink = 0; |
|
247 |
|
|
248 |
if (m_surface) { |
|
249 |
disconnect(m_surface, SIGNAL(supportedFormatsChanged()), |
|
250 |
this, SLOT(handleFormatChange())); |
|
251 |
} |
|
252 |
|
|
253 |
m_surface = surface; |
|
254 |
|
|
255 |
if (oldReady != isReady()) |
|
256 |
emit readyChanged(!oldReady); |
|
257 |
|
|
258 |
if (m_surface) { |
|
259 |
connect(m_surface, SIGNAL(supportedFormatsChanged()), |
|
260 |
this, SLOT(handleFormatChange())); |
|
261 |
} |
|
262 |
|
|
263 |
emit sinkChanged(); |
|
264 |
} |
|
265 |
} |
|
266 |
|
|
267 |
void QGstreamerGLTextureRenderer::handleFormatChange() |
|
268 |
{ |
|
269 |
if (m_videoSink) |
|
270 |
gst_object_unref(GST_OBJECT(m_videoSink)); |
|
271 |
|
|
272 |
m_videoSink = 0; |
|
273 |
emit sinkChanged(); |
|
274 |
} |
|
275 |
|
|
276 |
void QGstreamerGLTextureRenderer::handleFrameReady(GstElement *sink, gint frame, gpointer data) |
|
277 |
{ |
|
278 |
Q_UNUSED(sink); |
|
279 |
QGstreamerGLTextureRenderer* renderer = reinterpret_cast<QGstreamerGLTextureRenderer*>(data); |
|
280 |
|
|
281 |
QMutexLocker locker(&renderer->m_mutex); |
|
282 |
QMetaObject::invokeMethod(renderer, "renderGLFrame", |
|
283 |
Qt::QueuedConnection, |
|
284 |
Q_ARG(int, frame)); |
|
285 |
|
|
286 |
//we have to wait to ensure the frame is not reused, |
|
287 |
//timeout is added to avoid deadlocks when the main thread is |
|
288 |
//waiting for rendering to complete, this is possible for example during state chages. |
|
289 |
//If frame is not rendered during 60ms (~1-2 frames interval) it's better to unblock and drop it if necessary |
|
290 |
renderer->m_renderCondition.wait(&renderer->m_mutex, 60); |
|
291 |
} |
|
292 |
|
|
293 |
void QGstreamerGLTextureRenderer::renderGLFrame(int frame) |
|
294 |
{ |
|
295 |
#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 |
|
296 |
qDebug() << Q_FUNC_INFO << "frame:" << frame << "surface active:" << m_surface->isActive(); |
|
297 |
#endif |
|
298 |
QMutexLocker locker(&m_mutex); |
|
299 |
|
| 0669179 by Dmytro Poplavskiy at 2011-10-03 |
300 |
if (!m_surface || !m_glEnabled) { |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
301 |
m_renderCondition.wakeAll(); |
|
302 |
return; |
|
303 |
} |
|
304 |
|
|
305 |
MeegoGstVideoTexture *textureSink = MEEGO_GST_VIDEO_TEXTURE(m_videoSink); |
|
306 |
|
|
307 |
if (m_context) |
|
308 |
m_context->makeCurrent(); |
|
309 |
|
| 784c9aa by Dmytro Poplavskiy at 2011-03-22 |
310 |
//don't try to render the frame if state is changed to NULL or READY |
|
311 |
GstState pendingState = GST_STATE_NULL; |
|
312 |
GstState newState = GST_STATE_NULL; |
|
313 |
GstStateChangeReturn res = gst_element_get_state(m_videoSink, |
|
314 |
&newState, |
|
315 |
&pendingState, |
|
316 |
0);//don't block and return immediately |
|
317 |
|
|
318 |
if (res == GST_STATE_CHANGE_FAILURE || |
|
319 |
newState == GST_STATE_NULL || |
|
320 |
pendingState == GST_STATE_NULL) { |
|
321 |
stopRenderer(); |
|
322 |
m_renderCondition.wakeAll(); |
|
323 |
return; |
|
324 |
} |
|
325 |
|
| 29a82d1 by Dmytro Poplavskiy at 2011-09-01 |
326 |
if (m_surface->isActive() && m_surface->surfaceFormat().handleType() != EGLImageTextureHandle) |
|
327 |
m_surface->stop(); |
|
328 |
|
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
329 |
if (!m_surface->isActive()) { |
|
330 |
//find the native video size |
|
331 |
GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); |
|
332 |
GstCaps *caps = gst_pad_get_negotiated_caps(pad); |
|
333 |
|
|
334 |
if (caps) { |
| 70f91df by Dmytro Poplavskiy at 2011-03-31 |
335 |
QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps); |
|
336 |
if (m_nativeSize != newNativeSize) { |
|
337 |
m_nativeSize = newNativeSize; |
|
338 |
emit nativeSizeChanged(); |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
339 |
} |
|
340 |
gst_caps_unref(caps); |
|
341 |
} |
|
342 |
|
|
343 |
//start the surface... |
|
344 |
QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, EGLImageTextureHandle); |
|
345 |
if (!m_surface->start(format)) { |
|
346 |
qWarning() << Q_FUNC_INFO << "failed to start video surface" << format; |
|
347 |
m_renderCondition.wakeAll(); |
|
348 |
return; |
|
349 |
} |
|
350 |
} |
|
351 |
|
|
352 |
QGStreamerGLTextureBuffer *buffer = new QGStreamerGLTextureBuffer(textureSink, frame); |
|
353 |
QVideoFrame videoFrame(buffer, |
|
354 |
m_surface->surfaceFormat().frameSize(), |
|
355 |
m_surface->surfaceFormat().pixelFormat()); |
|
356 |
m_surface->present(videoFrame); |
|
357 |
m_renderCondition.wakeAll(); |
|
358 |
} |
|
359 |
|
|
360 |
bool QGstreamerGLTextureRenderer::isReady() const |
|
361 |
{ |
| a1ba3f3 by Dmytro Poplavskiy at 2011-10-03 |
362 |
return m_surface != NULL; |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
363 |
} |
|
364 |
|
|
365 |
void QGstreamerGLTextureRenderer::handleBusMessage(GstMessage* gm) |
|
366 |
{ |
| 784c9aa by Dmytro Poplavskiy at 2011-03-22 |
367 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
368 |
qDebug() << Q_FUNC_INFO << GST_MESSAGE_TYPE_NAME(gm); |
|
369 |
#endif |
|
370 |
|
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
371 |
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) { |
|
372 |
GstState oldState; |
|
373 |
GstState newState; |
|
374 |
gst_message_parse_state_changed(gm, &oldState, &newState, 0); |
|
375 |
|
|
376 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
377 |
qDebug() << Q_FUNC_INFO << "State changed:" << oldState << newState; |
|
378 |
#endif |
|
379 |
|
|
380 |
if (newState == GST_STATE_READY || newState == GST_STATE_NULL) { |
| 784c9aa by Dmytro Poplavskiy at 2011-03-22 |
381 |
stopRenderer(); |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
382 |
} |
|
383 |
|
|
384 |
if (oldState == GST_STATE_READY && newState == GST_STATE_PAUSED) { |
|
385 |
updateNativeVideoSize(); |
|
386 |
} |
|
387 |
} |
|
388 |
} |
|
389 |
|
|
390 |
void QGstreamerGLTextureRenderer::handleSyncMessage(GstMessage* gm) |
|
391 |
{ |
|
392 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
393 |
qDebug() << Q_FUNC_INFO; |
|
394 |
#endif |
|
395 |
|
|
396 |
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT && |
|
397 |
gst_structure_has_name(gm->structure, "prepare-xwindow-id")) |
|
398 |
precessNewStream(); |
|
399 |
} |
|
400 |
|
|
401 |
void QGstreamerGLTextureRenderer::precessNewStream() |
|
402 |
{ |
|
403 |
if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { |
| 15705ea by Dmytro Poplavskiy at 2011-05-10 |
404 |
GstXOverlay *overlay = GST_X_OVERLAY(m_videoSink); |
|
405 |
|
|
406 |
gst_x_overlay_set_xwindow_id(overlay, m_winId); |
|
407 |
|
|
408 |
if (!m_displayRect.isEmpty()) { |
|
409 |
gst_x_overlay_set_render_rectangle(overlay, |
|
410 |
m_displayRect.x(), |
|
411 |
m_displayRect.y(), |
|
412 |
m_displayRect.width(), |
|
413 |
m_displayRect.height()); |
|
414 |
} |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
415 |
|
|
416 |
GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); |
|
417 |
m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); |
|
418 |
} |
|
419 |
} |
|
420 |
|
| 784c9aa by Dmytro Poplavskiy at 2011-03-22 |
421 |
void QGstreamerGLTextureRenderer::stopRenderer() |
|
422 |
{ |
|
423 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
424 |
qDebug() << Q_FUNC_INFO; |
|
425 |
#endif |
|
426 |
|
|
427 |
if (m_surface && m_surface->isActive()) |
|
428 |
m_surface->stop(); |
|
429 |
|
| 29a82d1 by Dmytro Poplavskiy at 2011-09-01 |
430 |
if (m_fallbackImage.isNull()) { |
|
431 |
if (!m_nativeSize.isEmpty()) { |
|
432 |
m_nativeSize = QSize(); |
|
433 |
emit nativeSizeChanged(); |
|
434 |
} |
|
435 |
} else { |
|
436 |
if (m_surface) { |
|
437 |
QVideoSurfaceFormat format(m_fallbackImage.size(), QVideoFrame::Format_RGB32); |
|
438 |
format.setPixelAspectRatio(m_nativeSize.width(), m_fallbackImage.width()); |
|
439 |
|
|
440 |
if (m_surface->start(format)) |
|
441 |
m_surface->present(QVideoFrame(m_fallbackImage)); |
|
442 |
} |
| 784c9aa by Dmytro Poplavskiy at 2011-03-22 |
443 |
} |
|
444 |
} |
|
445 |
|
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
446 |
bool QGstreamerGLTextureRenderer::overlayEnabled() const |
|
447 |
{ |
|
448 |
return m_overlayEnabled; |
|
449 |
} |
|
450 |
|
|
451 |
void QGstreamerGLTextureRenderer::setOverlayEnabled(bool enabled) |
|
452 |
{ |
|
453 |
|
|
454 |
if (m_videoSink && (m_overlayEnabled != enabled)) { |
|
455 |
qDebug() << Q_FUNC_INFO << enabled; |
|
456 |
g_object_set(G_OBJECT(m_videoSink), |
|
457 |
"render-mode", |
|
458 |
enabled ? VIDEO_RENDERSWITCH_XOVERLAY_MODE : VIDEO_RENDERSWITCH_TEXTURE_STREAMING_MODE, |
|
459 |
(char *)NULL); |
|
460 |
} |
|
461 |
|
|
462 |
m_overlayEnabled = enabled; |
|
463 |
} |
|
464 |
|
|
465 |
|
|
466 |
WId QGstreamerGLTextureRenderer::winId() const |
|
467 |
{ |
|
468 |
return m_winId; |
|
469 |
} |
|
470 |
|
|
471 |
void QGstreamerGLTextureRenderer::setWinId(WId id) |
|
472 |
{ |
|
473 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
474 |
qDebug() << Q_FUNC_INFO << id; |
|
475 |
#endif |
|
476 |
|
|
477 |
if (m_winId == id) |
|
478 |
return; |
|
479 |
|
|
480 |
bool oldReady = isReady(); |
|
481 |
|
|
482 |
m_winId = id; |
|
483 |
|
|
484 |
if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { |
| 15705ea by Dmytro Poplavskiy at 2011-05-10 |
485 |
//don't set winId in NULL state, |
|
486 |
//texture sink opens xvideo port on set_xwindow_id, |
|
487 |
//this fails if video resource is not granted by resource policy yet. |
|
488 |
//state is changed to READY/PAUSED/PLAYING only after resource is granted. |
|
489 |
GstState pendingState = GST_STATE_NULL; |
|
490 |
GstState newState = GST_STATE_NULL; |
|
491 |
GstStateChangeReturn res = gst_element_get_state(m_videoSink, |
|
492 |
&newState, |
|
493 |
&pendingState, |
|
494 |
0);//don't block and return immediately |
|
495 |
|
|
496 |
if (res != GST_STATE_CHANGE_FAILURE && |
|
497 |
newState != GST_STATE_NULL && |
|
498 |
pendingState != GST_STATE_NULL) |
|
499 |
gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_winId); |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
500 |
} |
|
501 |
|
|
502 |
if (oldReady != isReady()) |
|
503 |
emit readyChanged(!oldReady); |
|
504 |
} |
|
505 |
|
|
506 |
QRect QGstreamerGLTextureRenderer::overlayGeometry() const |
|
507 |
{ |
|
508 |
return m_displayRect; |
|
509 |
} |
|
510 |
|
|
511 |
void QGstreamerGLTextureRenderer::setOverlayGeometry(const QRect &geometry) |
|
512 |
{ |
|
513 |
if (m_displayRect != geometry) { |
|
514 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
515 |
qDebug() << Q_FUNC_INFO << geometry; |
|
516 |
#endif |
|
517 |
m_displayRect = geometry; |
|
518 |
|
|
519 |
if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { |
|
520 |
if (m_displayRect.isEmpty()) |
|
521 |
gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), -1, -1, -1, -1); |
|
522 |
else |
|
523 |
gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), |
|
524 |
m_displayRect.x(), |
|
525 |
m_displayRect.y(), |
|
526 |
m_displayRect.width(), |
|
527 |
m_displayRect.height()); |
|
528 |
repaintOverlay(); |
|
529 |
} |
|
530 |
} |
|
531 |
} |
|
532 |
|
|
533 |
QColor QGstreamerGLTextureRenderer::colorKey() const |
|
534 |
{ |
|
535 |
return m_colorKey; |
|
536 |
} |
|
537 |
|
|
538 |
void QGstreamerGLTextureRenderer::repaintOverlay() |
|
539 |
{ |
|
540 |
if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { |
|
541 |
//don't call gst_x_overlay_expose if the sink is in null state |
|
542 |
GstState state = GST_STATE_NULL; |
|
543 |
GstStateChangeReturn res = gst_element_get_state(m_videoSink, &state, NULL, 1000000); |
|
544 |
if (res != GST_STATE_CHANGE_FAILURE && state != GST_STATE_NULL) { |
|
545 |
gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); |
|
546 |
} |
|
547 |
} |
|
548 |
} |
|
549 |
|
|
550 |
QSize QGstreamerGLTextureRenderer::nativeSize() const |
|
551 |
{ |
|
552 |
return m_nativeSize; |
|
553 |
} |
|
554 |
|
|
555 |
gboolean QGstreamerGLTextureRenderer::padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) |
|
556 |
{ |
|
557 |
QGstreamerGLTextureRenderer *control = reinterpret_cast<QGstreamerGLTextureRenderer*>(user_data); |
|
558 |
QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); |
|
559 |
gst_pad_remove_buffer_probe(pad, control->m_bufferProbeId); |
|
560 |
|
|
561 |
return TRUE; |
|
562 |
} |
|
563 |
|
|
564 |
void QGstreamerGLTextureRenderer::updateNativeVideoSize() |
|
565 |
{ |
|
566 |
const QSize oldSize = m_nativeSize; |
|
567 |
|
|
568 |
if (m_videoSink) { |
|
569 |
//find video native size to update video widget size hint |
|
570 |
GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); |
|
571 |
GstCaps *caps = gst_pad_get_negotiated_caps(pad); |
|
572 |
|
|
573 |
if (caps) { |
| 70f91df by Dmytro Poplavskiy at 2011-03-31 |
574 |
m_nativeSize = QGstUtils::capsCorrectedResolution(caps); |
| 95aefda by Dmytro Poplavskiy at 2011-03-21 |
575 |
gst_caps_unref(caps); |
|
576 |
} |
|
577 |
} else { |
|
578 |
m_nativeSize = QSize(); |
|
579 |
} |
|
580 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
581 |
qDebug() << Q_FUNC_INFO << oldSize << m_nativeSize << m_videoSink; |
|
582 |
#endif |
|
583 |
|
|
584 |
if (m_nativeSize != oldSize) |
|
585 |
emit nativeSizeChanged(); |
|
586 |
} |
| 29a82d1 by Dmytro Poplavskiy at 2011-09-01 |
587 |
|
|
588 |
GstBuffer * QGstreamerGLTextureRenderer::fallbackBuffer() const |
|
589 |
{ |
|
590 |
//buffer is not saved, method is only necessary for READ part of Q_PROPERTY |
|
591 |
return 0; |
|
592 |
} |
|
593 |
|
|
594 |
void QGstreamerGLTextureRenderer::setFallbackBuffer(GstBuffer *buffer) |
|
595 |
{ |
|
596 |
#ifdef GL_TEXTURE_SINK_DEBUG |
|
597 |
qDebug() << Q_FUNC_INFO << buffer; |
|
598 |
#endif |
|
599 |
m_fallbackImage = QImage(); |
|
600 |
|
|
601 |
if (!buffer) |
|
602 |
return; |
|
603 |
|
|
604 |
GstCaps *caps = GST_BUFFER_CAPS(buffer); |
|
605 |
const uchar *data = GST_BUFFER_DATA(buffer); |
|
606 |
|
|
607 |
if (!(caps && data)) |
|
608 |
return; |
|
609 |
|
|
610 |
const GstStructure *structure = gst_caps_get_structure(caps, 0); |
|
611 |
guint32 fourcc; |
|
612 |
gst_structure_get_fourcc(structure, "format", &fourcc); |
|
613 |
|
|
614 |
if (fourcc != GST_MAKE_FOURCC('Y', 'U', 'Y', '2') && |
|
615 |
fourcc != GST_MAKE_FOURCC('U', 'Y', 'V', 'Y')) |
|
616 |
return; |
|
617 |
|
|
618 |
QSize resolution = QGstUtils::capsResolution(caps); |
|
619 |
m_fallbackImage = QImage(resolution, QImage::Format_RGB32); |
|
620 |
|
|
621 |
for (int y=0; y<resolution.height(); y++) { |
|
622 |
const uchar *src = data + y*GST_ROUND_UP_4(resolution.width())*2; |
|
623 |
quint32 *dst = (quint32 *)m_fallbackImage.scanLine(y); |
|
624 |
|
|
625 |
int y1, y2; |
|
626 |
int u, v; |
|
627 |
|
|
628 |
int r1, g1, b1; |
|
629 |
int r2, g2, b2; |
|
630 |
|
|
631 |
int r, g, b; |
|
632 |
|
|
633 |
for (int x=0; x<resolution.width(); x+=2) { |
|
634 |
if (fourcc == GST_MAKE_FOURCC('Y', 'U', 'Y', '2')) { |
|
635 |
y1 = *src; src++; |
|
636 |
u = *src; src++; |
|
637 |
y2 = *src; src++; |
|
638 |
v = *src; src++; |
|
639 |
} else { |
|
640 |
u = *src; src++; |
|
641 |
y1 = *src; src++; |
|
642 |
v = *src; src++; |
|
643 |
y2 = *src; src++; |
|
644 |
} |
|
645 |
|
|
646 |
y1 = 298*y1/256; |
|
647 |
y2 = 298*y2/256; |
|
648 |
|
|
649 |
r = 408*v/256 - 223; |
|
650 |
g = - 100*u/256 - 208*v/256 + 136; |
|
651 |
b = 516*u/256 - 276; |
|
652 |
|
|
653 |
r1 = qBound(0, y1 + r, 255); |
|
654 |
g1 = qBound(0, y1 + g, 255); |
|
655 |
b1 = qBound(0, y1 + b, 255); |
|
656 |
|
|
657 |
*dst = qRgb(r1, g1, b1); dst++; |
|
658 |
|
|
659 |
r2 = qBound(0, y2 + r, 255); |
|
660 |
g2 = qBound(0, y2 + g, 255); |
|
661 |
b2 = qBound(0, y2 + b, 255); |
|
662 |
|
|
663 |
*dst = qRgb(r2, g2, b2); dst++; |
|
664 |
} |
|
665 |
} |
|
666 |
} |
| 0669179 by Dmytro Poplavskiy at 2011-10-03 |
667 |
|
|
668 |
bool QGstreamerGLTextureRenderer::glEnabled() const |
|
669 |
{ |
|
670 |
return m_glEnabled; |
|
671 |
} |
|
672 |
|
|
673 |
void QGstreamerGLTextureRenderer::setGlEnabled(bool enabled) |
|
674 |
{ |
|
675 |
if (m_glEnabled == enabled) |
|
676 |
return; |
|
677 |
|
|
678 |
m_glEnabled = enabled; |
|
679 |
|
|
680 |
if (enabled) { |
|
681 |
m_context = const_cast<QGLContext*>(QGLContext::currentContext()); |
|
682 |
|
|
683 |
if (m_videoSink) |
|
684 |
g_object_set(G_OBJECT(m_videoSink), |
|
685 |
"egl-context", eglGetCurrentContext(), |
|
686 |
(char *)NULL); |
|
687 |
} else { |
|
688 |
m_context = NULL; |
|
689 |
|
|
690 |
if (m_videoSink) |
|
691 |
g_object_set(G_OBJECT(m_videoSink), |
|
692 |
"egl-context", EGL_NO_CONTEXT, |
|
693 |
(char *)NULL); |
|
694 |
|
|
695 |
} |
|
696 |
} |