[GTK] [TextureMapper] Weird brightness with some videos with acceletared compositing
[WebKit.git] / Source / WebCore / platform / graphics / gstreamer / MediaPlayerPrivateGStreamerBase.cpp
1 /*
2  * Copyright (C) 2007, 2009 Apple Inc.  All rights reserved.
3  * Copyright (C) 2007 Collabora Ltd.  All rights reserved.
4  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5  * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
6  * Copyright (C) 2009, 2010 Igalia S.L
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * aint with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "MediaPlayerPrivateGStreamerBase.h"
26
27 #if ENABLE(VIDEO) && USE(GSTREAMER)
28
29 #include "ColorSpace.h"
30 #include "GStreamerUtilities.h"
31 #include "GraphicsContext.h"
32 #include "GraphicsTypes.h"
33 #include "ImageGStreamer.h"
34 #include "ImageOrientation.h"
35 #include "IntRect.h"
36 #include "MediaPlayer.h"
37 #include "NotImplemented.h"
38 #include "VideoSinkGStreamer.h"
39 #include "WebKitWebSourceGStreamer.h"
40 #include <gst/gst.h>
41 #include <wtf/gobject/GMutexLocker.h>
42 #include <wtf/text/CString.h>
43
44 #include <gst/audio/streamvolume.h>
45
46 #if GST_CHECK_VERSION(1, 1, 0) && USE(TEXTURE_MAPPER_GL)
47 #include "TextureMapperGL.h"
48 #endif
49
50 GST_DEBUG_CATEGORY(webkit_media_player_debug);
51 #define GST_CAT_DEFAULT webkit_media_player_debug
52
53 using namespace std;
54
55 namespace WebCore {
56
57 static int greatestCommonDivisor(int a, int b)
58 {
59     while (b) {
60         int temp = a;
61         a = b;
62         b = temp % b;
63     }
64
65     return ABS(a);
66 }
67
68 static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player)
69 {
70     // This is called when m_volumeElement receives the notify::volume signal.
71     LOG_MEDIA_MESSAGE("Volume changed to: %f", player->volume());
72     player->volumeChanged();
73 }
74
75 static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player)
76 {
77     // This is the callback of the timeout source created in ::volumeChanged.
78     player->notifyPlayerOfVolumeChange();
79     return FALSE;
80 }
81
82 static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player)
83 {
84     // This is called when m_volumeElement receives the notify::mute signal.
85     player->muteChanged();
86 }
87
88 static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player)
89 {
90     // This is the callback of the timeout source created in ::muteChanged.
91     player->notifyPlayerOfMute();
92     return FALSE;
93 }
94
95 static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamerBase* playerPrivate)
96 {
97     playerPrivate->triggerRepaint(buffer);
98 }
99
100 MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* player)
101     : m_player(player)
102     , m_fpsSink(0)
103     , m_readyState(MediaPlayer::HaveNothing)
104     , m_networkState(MediaPlayer::Empty)
105     , m_buffer(0)
106     , m_volumeTimerHandler(0)
107     , m_muteTimerHandler(0)
108     , m_repaintHandler(0)
109     , m_volumeSignalHandler(0)
110     , m_muteSignalHandler(0)
111 {
112 #if GLIB_CHECK_VERSION(2, 31, 0)
113     m_bufferMutex = new GMutex;
114     g_mutex_init(m_bufferMutex);
115 #else
116     m_bufferMutex = g_mutex_new();
117 #endif
118 }
119
120 MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase()
121 {
122     g_signal_handler_disconnect(m_webkitVideoSink.get(), m_repaintHandler);
123
124 #if GLIB_CHECK_VERSION(2, 31, 0)
125     g_mutex_clear(m_bufferMutex);
126     delete m_bufferMutex;
127 #else
128     g_mutex_free(m_bufferMutex);
129 #endif
130
131     if (m_buffer)
132         gst_buffer_unref(m_buffer);
133     m_buffer = 0;
134
135     m_player = 0;
136
137     if (m_muteTimerHandler)
138         g_source_remove(m_muteTimerHandler);
139
140     if (m_volumeTimerHandler)
141         g_source_remove(m_volumeTimerHandler);
142
143     if (m_volumeSignalHandler) {
144         g_signal_handler_disconnect(m_volumeElement.get(), m_volumeSignalHandler);
145         m_volumeSignalHandler = 0;
146     }
147
148     if (m_muteSignalHandler) {
149         g_signal_handler_disconnect(m_volumeElement.get(), m_muteSignalHandler);
150         m_muteSignalHandler = 0;
151     }
152 }
153
154 // Returns the size of the video
155 IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const
156 {
157     if (!hasVideo())
158         return IntSize();
159
160     if (!m_videoSize.isEmpty())
161         return m_videoSize;
162
163     GRefPtr<GstCaps> caps = currentVideoSinkCaps();
164     if (!caps)
165         return IntSize();
166
167
168     // TODO: handle possible clean aperture data. See
169     // https://bugzilla.gnome.org/show_bug.cgi?id=596571
170     // TODO: handle possible transformation matrix. See
171     // https://bugzilla.gnome.org/show_bug.cgi?id=596326
172
173     // Get the video PAR and original size, if this fails the
174     // video-sink has likely not yet negotiated its caps.
175     int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
176     IntSize originalSize;
177     GstVideoFormat format;
178     if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
179         return IntSize();
180
181     LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height());
182     LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
183
184     // Calculate DAR based on PAR and video size.
185     int displayWidth = originalSize.width() * pixelAspectRatioNumerator;
186     int displayHeight = originalSize.height() * pixelAspectRatioDenominator;
187
188     // Divide display width and height by their GCD to avoid possible overflows.
189     int displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight);
190     displayWidth /= displayAspectRatioGCD;
191     displayHeight /= displayAspectRatioGCD;
192
193     // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
194     guint64 width = 0, height = 0;
195     if (!(originalSize.height() % displayHeight)) {
196         LOG_MEDIA_MESSAGE("Keeping video original height");
197         width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
198         height = static_cast<guint64>(originalSize.height());
199     } else if (!(originalSize.width() % displayWidth)) {
200         LOG_MEDIA_MESSAGE("Keeping video original width");
201         height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth);
202         width = static_cast<guint64>(originalSize.width());
203     } else {
204         LOG_MEDIA_MESSAGE("Approximating while keeping original video height");
205         width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
206         height = static_cast<guint64>(originalSize.height());
207     }
208
209     LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
210     m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height));
211     return m_videoSize;
212 }
213
214 void MediaPlayerPrivateGStreamerBase::setVolume(float volume)
215 {
216     if (!m_volumeElement)
217         return;
218
219     LOG_MEDIA_MESSAGE("Setting volume: %f", volume);
220     gst_stream_volume_set_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC, static_cast<double>(volume));
221 }
222
223 float MediaPlayerPrivateGStreamerBase::volume() const
224 {
225     if (!m_volumeElement)
226         return 0;
227
228     return gst_stream_volume_get_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC);
229 }
230
231
232 void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange()
233 {
234     m_volumeTimerHandler = 0;
235
236     if (!m_player || !m_volumeElement)
237         return;
238     double volume;
239     volume = gst_stream_volume_get_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC);
240     // get_volume() can return values superior to 1.0 if the user
241     // applies software user gain via third party application (GNOME
242     // volume control for instance).
243     volume = CLAMP(volume, 0.0, 1.0);
244     m_player->volumeChanged(static_cast<float>(volume));
245 }
246
247 void MediaPlayerPrivateGStreamerBase::volumeChanged()
248 {
249     if (m_volumeTimerHandler)
250         g_source_remove(m_volumeTimerHandler);
251     m_volumeTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this, 0);
252     g_source_set_name_by_id(m_volumeTimerHandler, "[WebKit] mediaPlayerPrivateVolumeChangeTimeoutCallback");
253 }
254
255 MediaPlayer::NetworkState MediaPlayerPrivateGStreamerBase::networkState() const
256 {
257     return m_networkState;
258 }
259
260 MediaPlayer::ReadyState MediaPlayerPrivateGStreamerBase::readyState() const
261 {
262     return m_readyState;
263 }
264
265 void MediaPlayerPrivateGStreamerBase::sizeChanged()
266 {
267     notImplemented();
268 }
269
270 void MediaPlayerPrivateGStreamerBase::setMuted(bool muted)
271 {
272     if (!m_volumeElement)
273         return;
274
275     g_object_set(m_volumeElement.get(), "mute", muted, NULL);
276 }
277
278 bool MediaPlayerPrivateGStreamerBase::muted() const
279 {
280     if (!m_volumeElement)
281         return false;
282
283     bool muted;
284     g_object_get(m_volumeElement.get(), "mute", &muted, NULL);
285     return muted;
286 }
287
288 void MediaPlayerPrivateGStreamerBase::notifyPlayerOfMute()
289 {
290     m_muteTimerHandler = 0;
291
292     if (!m_player || !m_volumeElement)
293         return;
294
295     gboolean muted;
296     g_object_get(m_volumeElement.get(), "mute", &muted, NULL);
297     m_player->muteChanged(static_cast<bool>(muted));
298 }
299
300 void MediaPlayerPrivateGStreamerBase::muteChanged()
301 {
302     if (m_muteTimerHandler)
303         g_source_remove(m_muteTimerHandler);
304     m_muteTimerHandler = g_idle_add_full(G_PRIORITY_DEFAULT, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this, 0);
305     g_source_set_name_by_id(m_muteTimerHandler, "[WebKit] mediaPlayerPrivateMuteChangeTimeoutCallback");
306 }
307
308
309 #if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
310 PassRefPtr<BitmapTexture> MediaPlayerPrivateGStreamerBase::updateTexture(TextureMapper* textureMapper)
311 {
312     GMutexLocker lock(m_bufferMutex);
313     if (!m_buffer)
314         return 0;
315
316     const void* srcData = 0;
317     GRefPtr<GstCaps> caps = currentVideoSinkCaps();
318     if (!caps)
319         return 0;
320
321     IntSize size;
322     GstVideoFormat format;
323     int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
324     if (!getVideoSizeAndFormatFromCaps(caps.get(), size, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
325         return 0;
326
327     const GstVideoFormatInfo* formatInfo = gst_video_format_get_info(format);
328
329     RefPtr<BitmapTexture> texture = textureMapper->acquireTextureFromPool(size, GST_VIDEO_FORMAT_INFO_HAS_ALPHA(formatInfo) ? BitmapTexture::SupportsAlpha : BitmapTexture::NoFlag);
330
331 #if GST_CHECK_VERSION(1, 1, 0)
332     GstVideoGLTextureUploadMeta* meta;
333     if ((meta = gst_buffer_get_video_gl_texture_upload_meta(m_buffer))) {
334         if (meta->n_textures == 1) { // BRGx & BGRA formats use only one texture.
335             const BitmapTextureGL* textureGL = static_cast<const BitmapTextureGL*>(texture.get());
336             guint ids[4] = { textureGL->id(), 0, 0, 0 };
337
338             if (gst_video_gl_texture_upload_meta_upload(meta, ids))
339                 return texture;
340         }
341     }
342 #endif
343
344     GstMapInfo srcInfo;
345     gst_buffer_map(m_buffer, &srcInfo, GST_MAP_READ);
346     srcData = srcInfo.data;
347
348     texture->updateContents(srcData, WebCore::IntRect(WebCore::IntPoint(0, 0), size), WebCore::IntPoint(0, 0), stride, BitmapTexture::UpdateCannotModifyOriginalImageData);
349
350     gst_buffer_unmap(m_buffer, &srcInfo);
351     return texture;
352 }
353 #endif
354
355 void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstBuffer* buffer)
356 {
357     g_return_if_fail(GST_IS_BUFFER(buffer));
358
359     {
360         GMutexLocker lock(m_bufferMutex);
361         gst_buffer_replace(&m_buffer, buffer);
362     }
363
364 #if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
365     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player) && client()) {
366         client()->setPlatformLayerNeedsDisplay();
367         return;
368     }
369 #endif
370
371     m_player->repaint();
372 }
373
374 void MediaPlayerPrivateGStreamerBase::setSize(const IntSize& size)
375 {
376     m_size = size;
377 }
378
379 void MediaPlayerPrivateGStreamerBase::paint(GraphicsContext* context, const IntRect& rect)
380 {
381 #if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
382     if (client())
383         return;
384 #endif
385
386     if (context->paintingDisabled())
387         return;
388
389     if (!m_player->visible())
390         return;
391
392     GMutexLocker lock(m_bufferMutex);
393     if (!m_buffer)
394         return;
395
396     GRefPtr<GstCaps> caps = currentVideoSinkCaps();
397     if (!caps)
398         return;
399
400     RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get());
401     if (!gstImage)
402         return;
403
404     context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
405         rect, gstImage->rect(), CompositeCopy, ImageOrientationDescription(), false);
406 }
407
408 #if USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS)
409 void MediaPlayerPrivateGStreamerBase::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
410 {
411     if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode)
412         return;
413
414     if (!m_player->visible())
415         return;
416
417     RefPtr<BitmapTexture> texture = updateTexture(textureMapper);
418     if (texture)
419         textureMapper->drawTexture(*texture.get(), targetRect, matrix, opacity);
420 }
421 #endif
422
423 bool MediaPlayerPrivateGStreamerBase::supportsFullscreen() const
424 {
425     return true;
426 }
427
428 PlatformMedia MediaPlayerPrivateGStreamerBase::platformMedia() const
429 {
430     return NoPlatformMedia;
431 }
432
433 MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamerBase::movieLoadType() const
434 {
435     if (m_readyState == MediaPlayer::HaveNothing)
436         return MediaPlayer::Unknown;
437
438     if (isLiveStream())
439         return MediaPlayer::LiveStream;
440
441     return MediaPlayer::Download;
442 }
443
444 GRefPtr<GstCaps> MediaPlayerPrivateGStreamerBase::currentVideoSinkCaps() const
445 {
446     if (!m_webkitVideoSink)
447         return 0;
448
449     GRefPtr<GstCaps> currentCaps;
450     g_object_get(G_OBJECT(m_webkitVideoSink.get()), "current-caps", &currentCaps.outPtr(), NULL);
451     return currentCaps;
452 }
453
454 GstElement* MediaPlayerPrivateGStreamerBase::createVideoSink()
455 {
456     ASSERT(initializeGStreamer());
457
458     GstElement* videoSink = nullptr;
459     m_webkitVideoSink = webkitVideoSinkNew();
460
461     m_repaintHandler = g_signal_connect(m_webkitVideoSink.get(), "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
462
463     m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
464     if (m_fpsSink) {
465         g_object_set(m_fpsSink.get(), "silent", TRUE , nullptr);
466
467         // Turn off text overlay unless logging is enabled.
468 #if LOG_DISABLED
469         g_object_set(m_fpsSink.get(), "text-overlay", FALSE , nullptr);
470 #else
471         if (!isLogChannelEnabled("Media"))
472             g_object_set(m_fpsSink.get(), "text-overlay", FALSE , nullptr);
473 #endif // LOG_DISABLED
474
475         if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink.get()), "video-sink")) {
476             g_object_set(m_fpsSink.get(), "video-sink", m_webkitVideoSink.get(), nullptr);
477             videoSink = m_fpsSink.get();
478         } else
479             m_fpsSink = nullptr;
480     }
481
482     if (!m_fpsSink)
483         videoSink = m_webkitVideoSink.get();
484
485     ASSERT(videoSink);
486
487     return videoSink;
488 }
489
490 void MediaPlayerPrivateGStreamerBase::setStreamVolumeElement(GstStreamVolume* volume)
491 {
492     ASSERT(!m_volumeElement);
493     m_volumeElement = volume;
494
495     // We don't set the initial volume because we trust the sink to keep it for us. See
496     // https://bugs.webkit.org/show_bug.cgi?id=118974 for more information.
497     if (!m_player->platformVolumeConfigurationRequired()) {
498         LOG_MEDIA_MESSAGE("Setting stream volume to %f", m_player->volume());
499         g_object_set(m_volumeElement.get(), "volume", m_player->volume(), NULL);
500     } else
501         LOG_MEDIA_MESSAGE("Not setting stream volume, trusting system one");
502
503     LOG_MEDIA_MESSAGE("Setting stream muted %d",  m_player->muted());
504     g_object_set(m_volumeElement.get(), "mute", m_player->muted(), NULL);
505
506     m_volumeSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
507     m_muteSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
508 }
509
510 unsigned MediaPlayerPrivateGStreamerBase::decodedFrameCount() const
511 {
512     guint64 decodedFrames = 0;
513     if (m_fpsSink)
514         g_object_get(m_fpsSink.get(), "frames-rendered", &decodedFrames, NULL);
515     return static_cast<unsigned>(decodedFrames);
516 }
517
518 unsigned MediaPlayerPrivateGStreamerBase::droppedFrameCount() const
519 {
520     guint64 framesDropped = 0;
521     if (m_fpsSink)
522         g_object_get(m_fpsSink.get(), "frames-dropped", &framesDropped, NULL);
523     return static_cast<unsigned>(framesDropped);
524 }
525
526 unsigned MediaPlayerPrivateGStreamerBase::audioDecodedByteCount() const
527 {
528     GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
529     gint64 position = 0;
530
531     if (audioSink() && gst_element_query(audioSink(), query))
532         gst_query_parse_position(query, 0, &position);
533
534     gst_query_unref(query);
535     return static_cast<unsigned>(position);
536 }
537
538 unsigned MediaPlayerPrivateGStreamerBase::videoDecodedByteCount() const
539 {
540     GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
541     gint64 position = 0;
542
543     if (gst_element_query(m_webkitVideoSink.get(), query))
544         gst_query_parse_position(query, 0, &position);
545
546     gst_query_unref(query);
547     return static_cast<unsigned>(position);
548 }
549
550 }
551
552 #endif // USE(GSTREAMER)