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
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.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
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.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "qvideosurfacegstsink.h"
43
#include "qabstractvideosurface.h"
44
#include "qgstutils.h"
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)),
94
        m_frameNumber(frameNumber)
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
110
        //acquire_frame should really be called at buffer construction time
111
        //but it conflicts with id-less implementation of gst texture sink.
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
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
138
        if (!bind_status)
139
            qWarning() << Q_FUNC_INFO << "unbind-frame failed";
140
141
        //release_frame should really be called in destructor
142
        //but this conflicts with id-less implementation of gst texture sink.
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);
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),
169
    m_glEnabled(true),
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()),
200
                         "egl-context", m_glEnabled ? eglGetCurrentContext()
201
                                                    : EGL_NO_CONTEXT,
202
                         "colorkey", m_colorKey.rgb(),
203
                         "autopaint-colorkey", false,
204
                         "use-framebuffer-memory", true,
205
                         "render-mode", m_overlayEnabled ? VIDEO_RENDERSWITCH_XOVERLAY_MODE
206
                                                         : VIDEO_RENDERSWITCH_TEXTURE_STREAMING_MODE,
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
241
        m_context = m_glEnabled ? const_cast<QGLContext*>(QGLContext::currentContext()) : NULL;
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
300
    if (!m_surface || !m_glEnabled) {
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
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
326
    if (m_surface->isActive() && m_surface->surfaceFormat().handleType() != EGLImageTextureHandle)
327
        m_surface->stop();
328
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) {
335
            QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps);
336
            if (m_nativeSize != newNativeSize) {
337
                m_nativeSize = newNativeSize;
338
                emit nativeSizeChanged();
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
{
362
    return m_surface != NULL;
363
}
364
365
void QGstreamerGLTextureRenderer::handleBusMessage(GstMessage* gm)
366
{
367
#ifdef GL_TEXTURE_SINK_DEBUG
368
    qDebug() << Q_FUNC_INFO << GST_MESSAGE_TYPE_NAME(gm);
369
#endif
370
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) {
381
            stopRenderer();
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)) {
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
        }
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
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
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
        }
443
    }
444
}
445
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)) {
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);
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) {
574
            m_nativeSize = QGstUtils::capsCorrectedResolution(caps);
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
}
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
}
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
}