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 "MediaPlayerPrivateGStreamer.h"
26 
27 #if USE(GSTREAMER)
28 
29 #include "ColorSpace.h"
30 #include "Document.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "GOwnPtrGStreamer.h"
34 #include "GStreamerGWorld.h"
35 #include "GraphicsContext.h"
36 #include "GraphicsTypes.h"
37 #include "ImageGStreamer.h"
38 #include "IntRect.h"
39 #include "KURL.h"
40 #include "MIMETypeRegistry.h"
41 #include "MediaPlayer.h"
42 #include "NotImplemented.h"
43 #include "SecurityOrigin.h"
44 #include "TimeRanges.h"
45 #include "VideoSinkGStreamer.h"
46 #include "WebKitWebSourceGStreamer.h"
47 #include <GOwnPtr.h>
48 #include <gst/gst.h>
49 #include <gst/interfaces/streamvolume.h>
50 #include <gst/video/video.h>
51 #include <limits>
52 #include <math.h>
53 
54 // GstPlayFlags flags from playbin2. It is the policy of GStreamer to
55 // not publicly expose element-specific enums. That's why this
56 // GstPlayFlags enum has been copied here.
57 typedef enum {
58     GST_PLAY_FLAG_VIDEO         = 0x00000001,
59     GST_PLAY_FLAG_AUDIO         = 0x00000002,
60     GST_PLAY_FLAG_TEXT          = 0x00000004,
61     GST_PLAY_FLAG_VIS           = 0x00000008,
62     GST_PLAY_FLAG_SOFT_VOLUME   = 0x00000010,
63     GST_PLAY_FLAG_NATIVE_AUDIO  = 0x00000020,
64     GST_PLAY_FLAG_NATIVE_VIDEO  = 0x00000040,
65     GST_PLAY_FLAG_DOWNLOAD      = 0x00000080,
66     GST_PLAY_FLAG_BUFFERING     = 0x000000100
67 } GstPlayFlags;
68 
69 using namespace std;
70 
71 namespace WebCore {
72 
greatestCommonDivisor(int a,int b)73 static int greatestCommonDivisor(int a, int b)
74 {
75     while (b) {
76         int temp = a;
77         a = b;
78         b = temp % b;
79     }
80 
81     return ABS(a);
82 }
83 
mediaPlayerPrivateMessageCallback(GstBus *,GstMessage * message,MediaPlayerPrivateGStreamer * player)84 static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
85 {
86     return player->handleMessage(message);
87 }
88 
mediaPlayerPrivateSourceChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)89 static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
90 {
91     player->sourceChanged();
92 }
93 
mediaPlayerPrivateVolumeChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)94 static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
95 {
96     // This is called when playbin receives the notify::volume signal.
97     player->volumeChanged();
98 }
99 
mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)100 static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
101 {
102     // This is the callback of the timeout source created in ::volumeChanged.
103     player->notifyPlayerOfVolumeChange();
104     return FALSE;
105 }
106 
mediaPlayerPrivateMuteChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)107 static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
108 {
109     // This is called when playbin receives the notify::mute signal.
110     player->muteChanged();
111 }
112 
mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)113 static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
114 {
115     // This is the callback of the timeout source created in ::muteChanged.
116     player->notifyPlayerOfMute();
117     return FALSE;
118 }
119 
mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)120 static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
121 {
122     player->videoChanged();
123 }
124 
mediaPlayerPrivateVideoChangedCallback(GObject *,MediaPlayerPrivateGStreamer * player)125 static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
126 {
127     player->videoChanged();
128 }
129 
mediaPlayerPrivateAudioChangedCallback(GObject *,MediaPlayerPrivateGStreamer * player)130 static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
131 {
132     player->audioChanged();
133 }
134 
mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)135 static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
136 {
137     // This is the callback of the timeout source created in ::audioChanged.
138     player->notifyPlayerOfAudio();
139     return FALSE;
140 }
141 
mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)142 static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
143 {
144     // This is the callback of the timeout source created in ::videoChanged.
145     player->notifyPlayerOfVideo();
146     return FALSE;
147 }
148 
mediaPlayerPrivateRepaintCallback(WebKitVideoSink *,GstBuffer * buffer,MediaPlayerPrivateGStreamer * playerPrivate)149 static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamer* playerPrivate)
150 {
151     playerPrivate->triggerRepaint(buffer);
152 }
153 
create(MediaPlayer * player)154 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
155 {
156     return adoptPtr(new MediaPlayerPrivateGStreamer(player));
157 }
158 
registerMediaEngine(MediaEngineRegistrar registrar)159 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
160 {
161     if (isAvailable())
162         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
163 }
164 
165 static bool gstInitialized = false;
166 
doGstInit()167 static bool doGstInit()
168 {
169     // FIXME: We should pass the arguments from the command line
170     if (!gstInitialized) {
171         GOwnPtr<GError> error;
172         gstInitialized = gst_init_check(0, 0, &error.outPtr());
173         if (!gstInitialized)
174             LOG_VERBOSE(Media, "Could not initialize GStreamer: %s",
175                         error ? error->message : "unknown error occurred");
176         else
177             gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100,
178                                  WEBKIT_TYPE_WEB_SRC);
179     }
180     return gstInitialized;
181 }
182 
isAvailable()183 bool MediaPlayerPrivateGStreamer::isAvailable()
184 {
185     if (!doGstInit())
186         return false;
187 
188     GstElementFactory* factory = gst_element_factory_find("playbin2");
189     if (factory) {
190         gst_object_unref(GST_OBJECT(factory));
191         return true;
192     }
193     return false;
194 }
195 
MediaPlayerPrivateGStreamer(MediaPlayer * player)196 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
197     : m_player(player)
198     , m_playBin(0)
199     , m_webkitVideoSink(0)
200     , m_fpsSink(0)
201     , m_source(0)
202     , m_seekTime(0)
203     , m_changingRate(false)
204     , m_endTime(numeric_limits<float>::infinity())
205     , m_networkState(MediaPlayer::Empty)
206     , m_readyState(MediaPlayer::HaveNothing)
207     , m_isStreaming(false)
208     , m_size(IntSize())
209     , m_buffer(0)
210     , m_mediaLocations(0)
211     , m_mediaLocationCurrentIndex(0)
212     , m_resetPipeline(false)
213     , m_paused(true)
214     , m_seeking(false)
215     , m_buffering(false)
216     , m_playbackRate(1)
217     , m_errorOccured(false)
218     , m_mediaDuration(0)
219     , m_startedBuffering(false)
220     , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
221     , m_maxTimeLoaded(0)
222     , m_bufferingPercentage(0)
223     , m_preload(MediaPlayer::Auto)
224     , m_delayingLoad(false)
225     , m_mediaDurationKnown(true)
226     , m_volumeTimerHandler(0)
227     , m_muteTimerHandler(0)
228     , m_hasVideo(false)
229     , m_hasAudio(false)
230     , m_audioTimerHandler(0)
231     , m_videoTimerHandler(0)
232     , m_webkitAudioSink(0)
233 {
234     if (doGstInit())
235         createGSTPlayBin();
236 }
237 
~MediaPlayerPrivateGStreamer()238 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
239 {
240     if (m_fillTimer.isActive())
241         m_fillTimer.stop();
242 
243     if (m_buffer)
244         gst_buffer_unref(m_buffer);
245     m_buffer = 0;
246 
247     if (m_mediaLocations) {
248         gst_structure_free(m_mediaLocations);
249         m_mediaLocations = 0;
250     }
251 
252     if (m_source) {
253         gst_object_unref(m_source);
254         m_source = 0;
255     }
256 
257     if (m_videoSinkBin) {
258         gst_object_unref(m_videoSinkBin);
259         m_videoSinkBin = 0;
260     }
261 
262     if (m_playBin) {
263         gst_element_set_state(m_playBin, GST_STATE_NULL);
264         gst_object_unref(GST_OBJECT(m_playBin));
265         m_playBin = 0;
266     }
267 
268     m_player = 0;
269 
270     if (m_muteTimerHandler)
271         g_source_remove(m_muteTimerHandler);
272 
273     if (m_volumeTimerHandler)
274         g_source_remove(m_volumeTimerHandler);
275 
276     if (m_videoTimerHandler)
277         g_source_remove(m_videoTimerHandler);
278 
279     if (m_audioTimerHandler)
280         g_source_remove(m_audioTimerHandler);
281 }
282 
load(const String & url)283 void MediaPlayerPrivateGStreamer::load(const String& url)
284 {
285     g_object_set(m_playBin, "uri", url.utf8().data(), NULL);
286 
287     LOG_VERBOSE(Media, "Load %s", url.utf8().data());
288 
289     if (m_preload == MediaPlayer::None) {
290         LOG_VERBOSE(Media, "Delaying load.");
291         m_delayingLoad = true;
292     }
293 
294     // GStreamer needs to have the pipeline set to a paused state to
295     // start providing anything useful.
296     gst_element_set_state(m_playBin, GST_STATE_PAUSED);
297 
298     if (!m_delayingLoad)
299         commitLoad();
300 }
301 
commitLoad()302 void MediaPlayerPrivateGStreamer::commitLoad()
303 {
304     ASSERT(!m_delayingLoad);
305     LOG_VERBOSE(Media, "Committing load.");
306     updateStates();
307 }
308 
playbackPosition() const309 float MediaPlayerPrivateGStreamer::playbackPosition() const
310 {
311     float ret = 0.0f;
312 
313     GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
314     if (!gst_element_query(m_playBin, query)) {
315         LOG_VERBOSE(Media, "Position query failed...");
316         gst_query_unref(query);
317         return ret;
318     }
319 
320     gint64 position;
321     gst_query_parse_position(query, 0, &position);
322 
323     // Position is available only if the pipeline is not in GST_STATE_NULL or
324     // GST_STATE_READY state.
325     if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
326         ret = static_cast<float>(position) / static_cast<float>(GST_SECOND);
327 
328     LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
329 
330     gst_query_unref(query);
331 
332     return ret;
333 }
334 
changePipelineState(GstState newState)335 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
336 {
337     ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
338 
339     GstState currentState;
340     GstState pending;
341 
342     gst_element_get_state(m_playBin, &currentState, &pending, 0);
343     if (currentState != newState && pending != newState) {
344         GstStateChangeReturn ret = gst_element_set_state(m_playBin, newState);
345         GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
346         if (currentState != pausedOrPlaying && ret == GST_STATE_CHANGE_FAILURE) {
347             loadingFailed(MediaPlayer::Empty);
348             return false;
349         }
350     }
351     return true;
352 }
353 
prepareToPlay()354 void MediaPlayerPrivateGStreamer::prepareToPlay()
355 {
356     if (m_delayingLoad) {
357         m_delayingLoad = false;
358         commitLoad();
359     }
360 }
361 
play()362 void MediaPlayerPrivateGStreamer::play()
363 {
364     if (changePipelineState(GST_STATE_PLAYING))
365         LOG_VERBOSE(Media, "Play");
366 }
367 
pause()368 void MediaPlayerPrivateGStreamer::pause()
369 {
370     if (changePipelineState(GST_STATE_PAUSED))
371         LOG_VERBOSE(Media, "Pause");
372 }
373 
duration() const374 float MediaPlayerPrivateGStreamer::duration() const
375 {
376     if (!m_playBin)
377         return 0.0f;
378 
379     if (m_errorOccured)
380         return 0.0f;
381 
382     // Media duration query failed already, don't attempt new useless queries.
383     if (!m_mediaDurationKnown)
384         return numeric_limits<float>::infinity();
385 
386     if (m_mediaDuration)
387         return m_mediaDuration;
388 
389     GstFormat timeFormat = GST_FORMAT_TIME;
390     gint64 timeLength = 0;
391 
392     if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) {
393         LOG_VERBOSE(Media, "Time duration query failed.");
394         return numeric_limits<float>::infinity();
395     }
396 
397     LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
398 
399     return (float) ((guint64) timeLength / 1000000000.0);
400     // FIXME: handle 3.14.9.5 properly
401 }
402 
currentTime() const403 float MediaPlayerPrivateGStreamer::currentTime() const
404 {
405     if (!m_playBin)
406         return 0.0f;
407 
408     if (m_errorOccured)
409         return 0.0f;
410 
411     if (m_seeking)
412         return m_seekTime;
413 
414     return playbackPosition();
415 
416 }
417 
seek(float time)418 void MediaPlayerPrivateGStreamer::seek(float time)
419 {
420     // Avoid useless seeking.
421     if (time == playbackPosition())
422         return;
423 
424     if (!m_playBin)
425         return;
426 
427     if (m_errorOccured)
428         return;
429 
430     // Extract the integer part of the time (seconds) and the
431     // fractional part (microseconds). Attempt to round the
432     // microseconds so no floating point precision is lost and we can
433     // perform an accurate seek.
434     float seconds;
435     float microSeconds = modf(time, &seconds) * 1000000;
436     GTimeVal timeValue;
437     timeValue.tv_sec = static_cast<glong>(seconds);
438     timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
439 
440     GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue);
441     LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime));
442 
443     if (!gst_element_seek(m_playBin, m_player->rate(),
444             GST_FORMAT_TIME,
445             (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
446             GST_SEEK_TYPE_SET, clockTime,
447             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
448         LOG_VERBOSE(Media, "Seek to %f failed", time);
449     else {
450         m_seeking = true;
451         m_seekTime = time;
452     }
453 }
454 
paused() const455 bool MediaPlayerPrivateGStreamer::paused() const
456 {
457     return m_paused;
458 }
459 
seeking() const460 bool MediaPlayerPrivateGStreamer::seeking() const
461 {
462     return m_seeking;
463 }
464 
465 // Returns the size of the video
naturalSize() const466 IntSize MediaPlayerPrivateGStreamer::naturalSize() const
467 {
468     if (!hasVideo())
469         return IntSize();
470 
471     GstPad* pad = gst_element_get_static_pad(m_webkitVideoSink, "sink");
472     if (!pad)
473         return IntSize();
474 
475     guint64 width = 0, height = 0;
476     GstCaps* caps = GST_PAD_CAPS(pad);
477     int pixelAspectRatioNumerator, pixelAspectRatioDenominator;
478     int displayWidth, displayHeight, displayAspectRatioGCD;
479     int originalWidth = 0, originalHeight = 0;
480 
481     // TODO: handle possible clean aperture data. See
482     // https://bugzilla.gnome.org/show_bug.cgi?id=596571
483     // TODO: handle possible transformation matrix. See
484     // https://bugzilla.gnome.org/show_bug.cgi?id=596326
485 
486     // Get the video PAR and original size.
487     if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps)
488         || !gst_video_format_parse_caps(caps, 0, &originalWidth, &originalHeight)
489         || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator,
490                                                     &pixelAspectRatioDenominator)) {
491         gst_object_unref(GST_OBJECT(pad));
492         // The video-sink has likely not yet negotiated its caps.
493         return IntSize();
494     }
495 
496     gst_object_unref(GST_OBJECT(pad));
497 
498     LOG_VERBOSE(Media, "Original video size: %dx%d", originalWidth, originalHeight);
499     LOG_VERBOSE(Media, "Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
500 
501     // Calculate DAR based on PAR and video size.
502     displayWidth = originalWidth * pixelAspectRatioNumerator;
503     displayHeight = originalHeight * pixelAspectRatioDenominator;
504 
505     // Divide display width and height by their GCD to avoid possible overflows.
506     displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight);
507     displayWidth /= displayAspectRatioGCD;
508     displayHeight /= displayAspectRatioGCD;
509 
510     // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
511     if (!(originalHeight % displayHeight)) {
512         LOG_VERBOSE(Media, "Keeping video original height");
513         width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight);
514         height = static_cast<guint64>(originalHeight);
515     } else if (!(originalWidth % displayWidth)) {
516         LOG_VERBOSE(Media, "Keeping video original width");
517         height = gst_util_uint64_scale_int(originalWidth, displayHeight, displayWidth);
518         width = static_cast<guint64>(originalWidth);
519     } else {
520         LOG_VERBOSE(Media, "Approximating while keeping original video height");
521         width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight);
522         height = static_cast<guint64>(originalHeight);
523     }
524 
525     LOG_VERBOSE(Media, "Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
526     return IntSize(static_cast<int>(width), static_cast<int>(height));
527 }
528 
videoChanged()529 void MediaPlayerPrivateGStreamer::videoChanged()
530 {
531     if (m_videoTimerHandler)
532         g_source_remove(m_videoTimerHandler);
533     m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this);
534 }
535 
notifyPlayerOfVideo()536 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
537 {
538     m_videoTimerHandler = 0;
539 
540     gint videoTracks = 0;
541     if (m_playBin)
542         g_object_get(m_playBin, "n-video", &videoTracks, NULL);
543 
544     m_hasVideo = videoTracks > 0;
545     m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
546 }
547 
audioChanged()548 void MediaPlayerPrivateGStreamer::audioChanged()
549 {
550     if (m_audioTimerHandler)
551         g_source_remove(m_audioTimerHandler);
552     m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this);
553 }
554 
notifyPlayerOfAudio()555 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
556 {
557     m_audioTimerHandler = 0;
558 
559     gint audioTracks = 0;
560     if (m_playBin)
561         g_object_get(m_playBin, "n-audio", &audioTracks, NULL);
562     m_hasAudio = audioTracks > 0;
563     m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
564 }
565 
setVolume(float volume)566 void MediaPlayerPrivateGStreamer::setVolume(float volume)
567 {
568     if (!m_playBin)
569         return;
570 
571     gst_stream_volume_set_volume(GST_STREAM_VOLUME(m_playBin), GST_STREAM_VOLUME_FORMAT_CUBIC,
572                                  static_cast<double>(volume));
573 }
574 
notifyPlayerOfVolumeChange()575 void MediaPlayerPrivateGStreamer::notifyPlayerOfVolumeChange()
576 {
577     m_volumeTimerHandler = 0;
578 
579     if (!m_player || !m_playBin)
580         return;
581     double volume;
582     volume = gst_stream_volume_get_volume(GST_STREAM_VOLUME(m_playBin), GST_STREAM_VOLUME_FORMAT_CUBIC);
583     // get_volume() can return values superior to 1.0 if the user
584     // applies software user gain via third party application (GNOME
585     // volume control for instance).
586     volume = CLAMP(volume, 0.0, 1.0);
587     m_player->volumeChanged(static_cast<float>(volume));
588 }
589 
volumeChanged()590 void MediaPlayerPrivateGStreamer::volumeChanged()
591 {
592     if (m_volumeTimerHandler)
593         g_source_remove(m_volumeTimerHandler);
594     m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this);
595 }
596 
setRate(float rate)597 void MediaPlayerPrivateGStreamer::setRate(float rate)
598 {
599     // Avoid useless playback rate update.
600     if (m_playbackRate == rate)
601         return;
602 
603     GstState state;
604     GstState pending;
605 
606     gst_element_get_state(m_playBin, &state, &pending, 0);
607     if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
608         || (pending == GST_STATE_PAUSED))
609         return;
610 
611     if (m_isStreaming)
612         return;
613 
614     m_playbackRate = rate;
615     m_changingRate = true;
616 
617     if (!rate) {
618         gst_element_set_state(m_playBin, GST_STATE_PAUSED);
619         return;
620     }
621 
622     float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
623     GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
624     gint64 start, end;
625     bool mute = false;
626 
627     LOG_VERBOSE(Media, "Set Rate to %f", rate);
628     if (rate > 0) {
629         // Mute the sound if the playback rate is too extreme.
630         // TODO: in other cases we should perform pitch adjustments.
631         mute = (bool) (rate < 0.8 || rate > 2);
632         start = currentPosition;
633         end = GST_CLOCK_TIME_NONE;
634     } else {
635         start = 0;
636         mute = true;
637 
638         // If we are at beginning of media, start from the end to
639         // avoid immediate EOS.
640         if (currentPosition <= 0)
641             end = static_cast<gint64>(duration() * GST_SECOND);
642         else
643             end = currentPosition;
644     }
645 
646     LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute);
647 
648     if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags,
649                           GST_SEEK_TYPE_SET, start,
650                           GST_SEEK_TYPE_SET, end))
651         LOG_VERBOSE(Media, "Set rate to %f failed", rate);
652     else
653         g_object_set(m_playBin, "mute", mute, NULL);
654 }
655 
networkState() const656 MediaPlayer::NetworkState MediaPlayerPrivateGStreamer::networkState() const
657 {
658     return m_networkState;
659 }
660 
readyState() const661 MediaPlayer::ReadyState MediaPlayerPrivateGStreamer::readyState() const
662 {
663     return m_readyState;
664 }
665 
buffered() const666 PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
667 {
668     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
669     if (m_errorOccured || m_isStreaming)
670         return timeRanges.release();
671 
672 #if GST_CHECK_VERSION(0, 10, 31)
673     float mediaDuration(duration());
674     if (!mediaDuration || isinf(mediaDuration))
675         return timeRanges.release();
676 
677     GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
678 
679     if (!gst_element_query(m_playBin, query)) {
680         gst_query_unref(query);
681         return timeRanges.release();
682     }
683 
684     gint64 rangeStart = 0, rangeStop = 0;
685     for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
686         if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
687             timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / 100),
688                             static_cast<float>((rangeStop * mediaDuration) / 100));
689     }
690 
691     // Fallback to the more general maxTimeLoaded() if no range has
692     // been found.
693     if (!timeRanges->length())
694         if (float loaded = maxTimeLoaded())
695             timeRanges->add(0, loaded);
696 
697     gst_query_unref(query);
698 #else
699     float loaded = maxTimeLoaded();
700     if (!m_errorOccured && !m_isStreaming && loaded > 0)
701         timeRanges->add(0, loaded);
702 #endif
703     return timeRanges.release();
704 }
705 
handleMessage(GstMessage * message)706 gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
707 {
708     GOwnPtr<GError> err;
709     GOwnPtr<gchar> debug;
710     MediaPlayer::NetworkState error;
711     bool issueError = true;
712     bool attemptNextLocation = false;
713 
714     if (message->structure) {
715         const gchar* messageTypeName = gst_structure_get_name(message->structure);
716 
717         // Redirect messages are sent from elements, like qtdemux, to
718         // notify of the new location(s) of the media.
719         if (!g_strcmp0(messageTypeName, "redirect")) {
720             mediaLocationChanged(message);
721             return TRUE;
722         }
723     }
724 
725     switch (GST_MESSAGE_TYPE(message)) {
726     case GST_MESSAGE_ERROR:
727         if (m_resetPipeline)
728             break;
729         gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
730         LOG_VERBOSE(Media, "Error: %d, %s", err->code,  err->message);
731 
732         error = MediaPlayer::Empty;
733         if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
734             || err->code == GST_STREAM_ERROR_WRONG_TYPE
735             || err->code == GST_STREAM_ERROR_FAILED
736             || err->code == GST_CORE_ERROR_MISSING_PLUGIN
737             || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
738             error = MediaPlayer::FormatError;
739         else if (err->domain == GST_STREAM_ERROR) {
740             // Let the mediaPlayerClient handle the stream error, in
741             // this case the HTMLMediaElement will emit a stalled
742             // event.
743             if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
744                 LOG_VERBOSE(Media, "Decode error, let the Media element emit a stalled event.");
745                 break;
746             }
747             error = MediaPlayer::DecodeError;
748             attemptNextLocation = true;
749         } else if (err->domain == GST_RESOURCE_ERROR)
750             error = MediaPlayer::NetworkError;
751 
752         if (attemptNextLocation)
753             issueError = !loadNextLocation();
754         if (issueError)
755             loadingFailed(error);
756         break;
757     case GST_MESSAGE_EOS:
758         LOG_VERBOSE(Media, "End of Stream");
759         didEnd();
760         break;
761     case GST_MESSAGE_STATE_CHANGED:
762         // Ignore state changes if load is delayed (preload=none). The
763         // player state will be updated once commitLoad() is called.
764         if (m_delayingLoad) {
765             LOG_VERBOSE(Media, "Media load has been delayed. Ignoring state changes for now");
766             break;
767         }
768 
769         // Ignore state changes from internal elements. They are
770         // forwarded to playbin2 anyway.
771         if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin))
772             updateStates();
773         break;
774     case GST_MESSAGE_BUFFERING:
775         processBufferingStats(message);
776         break;
777     case GST_MESSAGE_DURATION:
778         LOG_VERBOSE(Media, "Duration changed");
779         durationChanged();
780         break;
781     default:
782         LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s",
783                     GST_MESSAGE_TYPE_NAME(message));
784         break;
785     }
786     return TRUE;
787 }
788 
processBufferingStats(GstMessage * message)789 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
790 {
791     // This is the immediate buffering that needs to happen so we have
792     // enough to play right now.
793     m_buffering = true;
794     const GstStructure *structure = gst_message_get_structure(message);
795     gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
796 
797     LOG_VERBOSE(Media, "[Buffering] Buffering: %d%%.", m_bufferingPercentage);
798 
799     GstBufferingMode mode;
800     gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
801     if (mode != GST_BUFFERING_DOWNLOAD) {
802         updateStates();
803         return;
804     }
805 
806     // This is on-disk buffering, that allows us to download much more
807     // than needed for right now.
808     if (!m_startedBuffering) {
809         LOG_VERBOSE(Media, "[Buffering] Starting on-disk buffering.");
810 
811         m_startedBuffering = true;
812 
813         if (m_fillTimer.isActive())
814             m_fillTimer.stop();
815 
816         m_fillTimer.startRepeating(0.2);
817     }
818 }
819 
fillTimerFired(Timer<MediaPlayerPrivateGStreamer> *)820 void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
821 {
822     GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
823 
824     if (!gst_element_query(m_playBin, query)) {
825         gst_query_unref(query);
826         return;
827     }
828 
829     gint64 start, stop;
830     gdouble fillStatus = 100.0;
831 
832     gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
833     gst_query_unref(query);
834 
835     if (stop != -1)
836         fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
837 
838     LOG_VERBOSE(Media, "[Buffering] Download buffer filled up to %f%%", fillStatus);
839 
840     if (!m_mediaDuration)
841         durationChanged();
842 
843     // Update maxTimeLoaded only if the media duration is
844     // available. Otherwise we can't compute it.
845     if (m_mediaDuration) {
846         if (fillStatus == 100.0)
847             m_maxTimeLoaded = m_mediaDuration;
848         else
849             m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
850         LOG_VERBOSE(Media, "[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
851     }
852 
853     if (fillStatus != 100.0) {
854         updateStates();
855         return;
856     }
857 
858     // Media is now fully loaded. It will play even if network
859     // connection is cut. Buffering is done, remove the fill source
860     // from the main loop.
861     m_fillTimer.stop();
862     m_startedBuffering = false;
863     updateStates();
864 }
865 
maxTimeSeekable() const866 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
867 {
868     if (m_errorOccured)
869         return 0.0f;
870 
871     LOG_VERBOSE(Media, "maxTimeSeekable");
872     // infinite duration means live stream
873     if (isinf(duration()))
874         return 0.0f;
875 
876     return duration();
877 }
878 
maxTimeLoaded() const879 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
880 {
881     if (m_errorOccured)
882         return 0.0f;
883 
884     float loaded = m_maxTimeLoaded;
885     if (!loaded && !m_fillTimer.isActive())
886         loaded = duration();
887     LOG_VERBOSE(Media, "maxTimeLoaded: %f", loaded);
888     return loaded;
889 }
890 
bytesLoaded() const891 unsigned MediaPlayerPrivateGStreamer::bytesLoaded() const
892 {
893     if (!m_playBin)
894         return 0;
895 
896     if (!m_mediaDuration)
897         return 0;
898 
899     unsigned loaded = totalBytes() * maxTimeLoaded() / m_mediaDuration;
900     LOG_VERBOSE(Media, "bytesLoaded: %d", loaded);
901     return loaded;
902 }
903 
totalBytes() const904 unsigned MediaPlayerPrivateGStreamer::totalBytes() const
905 {
906     if (!m_source)
907         return 0;
908 
909     if (m_errorOccured)
910         return 0;
911 
912     GstFormat fmt = GST_FORMAT_BYTES;
913     gint64 length = 0;
914     if (gst_element_query_duration(m_source, &fmt, &length)) {
915         LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length);
916         return static_cast<unsigned>(length);
917     }
918 
919     // Fall back to querying the source pads manually.
920     // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
921     GstIterator* iter = gst_element_iterate_src_pads(m_source);
922     bool done = false;
923     while (!done) {
924         gpointer data;
925 
926         switch (gst_iterator_next(iter, &data)) {
927         case GST_ITERATOR_OK: {
928             GstPad* pad = GST_PAD_CAST(data);
929             gint64 padLength = 0;
930             if (gst_pad_query_duration(pad, &fmt, &padLength)
931                 && padLength > length)
932                 length = padLength;
933             gst_object_unref(pad);
934             break;
935         }
936         case GST_ITERATOR_RESYNC:
937             gst_iterator_resync(iter);
938             break;
939         case GST_ITERATOR_ERROR:
940             // Fall through.
941         case GST_ITERATOR_DONE:
942             done = true;
943             break;
944         }
945     }
946     gst_iterator_free(iter);
947 
948     LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length);
949 
950     return static_cast<unsigned>(length);
951 }
952 
decodedFrameCount() const953 unsigned MediaPlayerPrivateGStreamer::decodedFrameCount() const
954 {
955     guint64 decodedFrames = 0;
956     if (m_fpsSink)
957         g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL);
958     return static_cast<unsigned>(decodedFrames);
959 }
960 
droppedFrameCount() const961 unsigned MediaPlayerPrivateGStreamer::droppedFrameCount() const
962 {
963     guint64 framesDropped = 0;
964     if (m_fpsSink)
965         g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL);
966     return static_cast<unsigned>(framesDropped);
967 }
968 
audioDecodedByteCount() const969 unsigned MediaPlayerPrivateGStreamer::audioDecodedByteCount() const
970 {
971     GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
972     gint64 position = 0;
973 
974     if (m_webkitAudioSink && gst_element_query(m_webkitAudioSink, query))
975         gst_query_parse_position(query, 0, &position);
976 
977     gst_query_unref(query);
978     return static_cast<unsigned>(position);
979 }
980 
videoDecodedByteCount() const981 unsigned MediaPlayerPrivateGStreamer::videoDecodedByteCount() const
982 {
983     GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
984     gint64 position = 0;
985 
986     if (gst_element_query(m_webkitVideoSink, query))
987         gst_query_parse_position(query, 0, &position);
988 
989     gst_query_unref(query);
990     return static_cast<unsigned>(position);
991 }
992 
updateAudioSink()993 void MediaPlayerPrivateGStreamer::updateAudioSink()
994 {
995     if (!m_playBin)
996         return;
997 
998     GOwnPtr<GstElement> element;
999 
1000     g_object_get(m_playBin, "audio-sink", &element.outPtr(), NULL);
1001     gst_object_replace(reinterpret_cast<GstObject**>(&m_webkitAudioSink),
1002                        reinterpret_cast<GstObject*>(element.get()));
1003 }
1004 
1005 
sourceChanged()1006 void MediaPlayerPrivateGStreamer::sourceChanged()
1007 {
1008     GOwnPtr<GstElement> element;
1009 
1010     g_object_get(m_playBin, "source", &element.outPtr(), NULL);
1011     gst_object_replace(reinterpret_cast<GstObject**>(&m_source),
1012                        reinterpret_cast<GstObject*>(element.get()));
1013 
1014     if (WEBKIT_IS_WEB_SRC(element.get())) {
1015         Frame* frame = 0;
1016         Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
1017         if (document)
1018             frame = document->frame();
1019 
1020         if (frame)
1021             webKitWebSrcSetFrame(WEBKIT_WEB_SRC(element.get()), frame);
1022     }
1023 }
1024 
cancelLoad()1025 void MediaPlayerPrivateGStreamer::cancelLoad()
1026 {
1027     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1028         return;
1029 
1030     if (m_playBin)
1031         gst_element_set_state(m_playBin, GST_STATE_NULL);
1032 }
1033 
updateStates()1034 void MediaPlayerPrivateGStreamer::updateStates()
1035 {
1036     if (!m_playBin)
1037         return;
1038 
1039     if (m_errorOccured)
1040         return;
1041 
1042     MediaPlayer::NetworkState oldNetworkState = m_networkState;
1043     MediaPlayer::ReadyState oldReadyState = m_readyState;
1044     GstState state;
1045     GstState pending;
1046 
1047     GstStateChangeReturn ret = gst_element_get_state(m_playBin,
1048         &state, &pending, 250 * GST_NSECOND);
1049 
1050     bool shouldUpdateAfterSeek = false;
1051     switch (ret) {
1052     case GST_STATE_CHANGE_SUCCESS:
1053         LOG_VERBOSE(Media, "State: %s, pending: %s",
1054             gst_element_state_get_name(state),
1055             gst_element_state_get_name(pending));
1056 
1057         m_resetPipeline = state <= GST_STATE_READY;
1058 
1059         // Try to figure out ready and network states.
1060         if (state == GST_STATE_READY) {
1061             m_readyState = MediaPlayer::HaveMetadata;
1062             m_networkState = MediaPlayer::Empty;
1063             // Cache the duration without emiting the durationchange
1064             // event because it's taken care of by the media element
1065             // in this precise case.
1066             cacheDuration();
1067         } else if (maxTimeLoaded() == duration()) {
1068             m_networkState = MediaPlayer::Loaded;
1069             m_readyState = MediaPlayer::HaveEnoughData;
1070         } else {
1071             m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
1072             m_networkState = MediaPlayer::Loading;
1073         }
1074 
1075         if (m_buffering && state != GST_STATE_READY) {
1076             m_readyState = MediaPlayer::HaveCurrentData;
1077             m_networkState = MediaPlayer::Loading;
1078         }
1079 
1080         // Now let's try to get the states in more detail using
1081         // information from GStreamer, while we sync states where
1082         // needed.
1083         if (state == GST_STATE_PAUSED) {
1084             if (!m_webkitAudioSink)
1085                 updateAudioSink();
1086             if (m_buffering && m_bufferingPercentage == 100) {
1087                 m_buffering = false;
1088                 m_bufferingPercentage = 0;
1089                 m_readyState = MediaPlayer::HaveEnoughData;
1090 
1091                 LOG_VERBOSE(Media, "[Buffering] Complete.");
1092 
1093                 if (!m_paused) {
1094                     LOG_VERBOSE(Media, "[Buffering] Restarting playback.");
1095                     gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1096                 }
1097             } else if (!m_buffering && (currentTime() < duration())) {
1098                 m_paused = true;
1099             }
1100         } else if (state == GST_STATE_PLAYING) {
1101             m_readyState = MediaPlayer::HaveEnoughData;
1102             m_paused = false;
1103 
1104             if (m_buffering) {
1105                 m_readyState = MediaPlayer::HaveCurrentData;
1106                 m_networkState = MediaPlayer::Loading;
1107 
1108                 LOG_VERBOSE(Media, "[Buffering] Pausing stream for buffering.");
1109 
1110                 gst_element_set_state(m_playBin, GST_STATE_PAUSED);
1111             }
1112         } else
1113             m_paused = true;
1114 
1115         // Is on-disk buffering in progress?
1116         if (m_fillTimer.isActive())
1117             m_networkState = MediaPlayer::Loading;
1118 
1119         if (m_changingRate) {
1120             m_player->rateChanged();
1121             m_changingRate = false;
1122         }
1123 
1124         if (m_seeking) {
1125             shouldUpdateAfterSeek = true;
1126             m_seeking = false;
1127         }
1128 
1129         break;
1130     case GST_STATE_CHANGE_ASYNC:
1131         LOG_VERBOSE(Media, "Async: State: %s, pending: %s",
1132             gst_element_state_get_name(state),
1133             gst_element_state_get_name(pending));
1134         // Change in progress
1135 
1136         if (!m_isStreaming && !m_buffering)
1137             return;
1138 
1139         if (m_seeking) {
1140             shouldUpdateAfterSeek = true;
1141             m_seeking = false;
1142         }
1143         break;
1144     case GST_STATE_CHANGE_FAILURE:
1145         LOG_VERBOSE(Media, "Failure: State: %s, pending: %s",
1146             gst_element_state_get_name(state),
1147             gst_element_state_get_name(pending));
1148         // Change failed
1149         return;
1150     case GST_STATE_CHANGE_NO_PREROLL:
1151         LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s",
1152             gst_element_state_get_name(state),
1153             gst_element_state_get_name(pending));
1154 
1155         if (state == GST_STATE_READY)
1156             m_readyState = MediaPlayer::HaveNothing;
1157         else if (state == GST_STATE_PAUSED) {
1158             m_readyState = MediaPlayer::HaveEnoughData;
1159             m_paused = true;
1160             // Live pipelines go in PAUSED without prerolling.
1161             m_isStreaming = true;
1162         } else if (state == GST_STATE_PLAYING)
1163             m_paused = false;
1164 
1165         if (m_seeking) {
1166             shouldUpdateAfterSeek = true;
1167             m_seeking = false;
1168             if (!m_paused)
1169                 gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1170         } else if (!m_paused)
1171             gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1172 
1173         m_networkState = MediaPlayer::Loading;
1174         break;
1175     default:
1176         LOG_VERBOSE(Media, "Else : %d", ret);
1177         break;
1178     }
1179 
1180     if (seeking())
1181         m_readyState = MediaPlayer::HaveNothing;
1182 
1183     if (shouldUpdateAfterSeek)
1184         timeChanged();
1185 
1186     if (m_networkState != oldNetworkState) {
1187         LOG_VERBOSE(Media, "Network State Changed from %u to %u",
1188             oldNetworkState, m_networkState);
1189         m_player->networkStateChanged();
1190     }
1191     if (m_readyState != oldReadyState) {
1192         LOG_VERBOSE(Media, "Ready State Changed from %u to %u",
1193             oldReadyState, m_readyState);
1194         m_player->readyStateChanged();
1195     }
1196 }
1197 
mediaLocationChanged(GstMessage * message)1198 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1199 {
1200     if (m_mediaLocations)
1201         gst_structure_free(m_mediaLocations);
1202 
1203     if (message->structure) {
1204         // This structure can contain:
1205         // - both a new-location string and embedded locations structure
1206         // - or only a new-location string.
1207         m_mediaLocations = gst_structure_copy(message->structure);
1208         const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1209 
1210         if (locations)
1211             m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1212 
1213         loadNextLocation();
1214     }
1215 }
1216 
loadNextLocation()1217 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1218 {
1219     if (!m_mediaLocations)
1220         return false;
1221 
1222     const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1223     const gchar* newLocation = 0;
1224 
1225     if (!locations) {
1226         // Fallback on new-location string.
1227         newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1228         if (!newLocation)
1229             return false;
1230     }
1231 
1232     if (!newLocation) {
1233         if (m_mediaLocationCurrentIndex < 0) {
1234             m_mediaLocations = 0;
1235             return false;
1236         }
1237 
1238         const GValue* location = gst_value_list_get_value(locations,
1239                                                           m_mediaLocationCurrentIndex);
1240         const GstStructure* structure = gst_value_get_structure(location);
1241 
1242         if (!structure) {
1243             m_mediaLocationCurrentIndex--;
1244             return false;
1245         }
1246 
1247         newLocation = gst_structure_get_string(structure, "new-location");
1248     }
1249 
1250     if (newLocation) {
1251         // Found a candidate. new-location is not always an absolute url
1252         // though. We need to take the base of the current url and
1253         // append the value of new-location to it.
1254 
1255         gchar* currentLocation = 0;
1256         g_object_get(m_playBin, "uri", &currentLocation, NULL);
1257 
1258         KURL currentUrl(KURL(), currentLocation);
1259         g_free(currentLocation);
1260 
1261         KURL newUrl;
1262 
1263         if (gst_uri_is_valid(newLocation))
1264             newUrl = KURL(KURL(), newLocation);
1265         else
1266             newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation);
1267 
1268         RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl);
1269         if (securityOrigin->canRequest(newUrl)) {
1270             LOG_VERBOSE(Media, "New media url: %s", newUrl.string().utf8().data());
1271 
1272             // Reset player states.
1273             m_networkState = MediaPlayer::Loading;
1274             m_player->networkStateChanged();
1275             m_readyState = MediaPlayer::HaveNothing;
1276             m_player->readyStateChanged();
1277 
1278             // Reset pipeline state.
1279             m_resetPipeline = true;
1280             gst_element_set_state(m_playBin, GST_STATE_READY);
1281 
1282             GstState state;
1283             gst_element_get_state(m_playBin, &state, 0, 0);
1284             if (state <= GST_STATE_READY) {
1285                 // Set the new uri and start playing.
1286                 g_object_set(m_playBin, "uri", newUrl.string().utf8().data(), NULL);
1287                 gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1288                 return true;
1289             }
1290         }
1291     }
1292     m_mediaLocationCurrentIndex--;
1293     return false;
1294 
1295 }
1296 
loadStateChanged()1297 void MediaPlayerPrivateGStreamer::loadStateChanged()
1298 {
1299     updateStates();
1300 }
1301 
sizeChanged()1302 void MediaPlayerPrivateGStreamer::sizeChanged()
1303 {
1304     notImplemented();
1305 }
1306 
timeChanged()1307 void MediaPlayerPrivateGStreamer::timeChanged()
1308 {
1309     updateStates();
1310     m_player->timeChanged();
1311 }
1312 
didEnd()1313 void MediaPlayerPrivateGStreamer::didEnd()
1314 {
1315     // EOS was reached but in case of reverse playback the position is
1316     // not always 0. So to not confuse the HTMLMediaElement we
1317     // synchronize position and duration values.
1318     float now = currentTime();
1319     if (now > 0) {
1320         m_mediaDuration = now;
1321         m_mediaDurationKnown = true;
1322         m_player->durationChanged();
1323     }
1324 
1325     gst_element_set_state(m_playBin, GST_STATE_PAUSED);
1326 
1327     timeChanged();
1328 }
1329 
cacheDuration()1330 void MediaPlayerPrivateGStreamer::cacheDuration()
1331 {
1332     // Reset cached media duration
1333     m_mediaDuration = 0;
1334 
1335     // And re-cache it if possible.
1336     GstState state;
1337     gst_element_get_state(m_playBin, &state, 0, 0);
1338     float newDuration = duration();
1339 
1340     if (state <= GST_STATE_READY) {
1341         // Don't set m_mediaDurationKnown yet if the pipeline is not
1342         // paused. This allows duration() query to fail at least once
1343         // before playback starts and duration becomes known.
1344         if (!isinf(newDuration))
1345             m_mediaDuration = newDuration;
1346     } else {
1347         m_mediaDurationKnown = !isinf(newDuration);
1348         if (m_mediaDurationKnown)
1349             m_mediaDuration = newDuration;
1350     }
1351 
1352     if (!isinf(newDuration))
1353         m_mediaDuration = newDuration;
1354 }
1355 
durationChanged()1356 void MediaPlayerPrivateGStreamer::durationChanged()
1357 {
1358     float previousDuration = m_mediaDuration;
1359 
1360     cacheDuration();
1361     // Avoid emiting durationchanged in the case where the previous
1362     // duration was 0 because that case is already handled by the
1363     // HTMLMediaElement.
1364     if (previousDuration && m_mediaDuration != previousDuration)
1365         m_player->durationChanged();
1366 }
1367 
supportsMuting() const1368 bool MediaPlayerPrivateGStreamer::supportsMuting() const
1369 {
1370     return true;
1371 }
1372 
setMuted(bool muted)1373 void MediaPlayerPrivateGStreamer::setMuted(bool muted)
1374 {
1375     if (!m_playBin)
1376         return;
1377 
1378     g_object_set(m_playBin, "mute", muted, NULL);
1379 }
1380 
notifyPlayerOfMute()1381 void MediaPlayerPrivateGStreamer::notifyPlayerOfMute()
1382 {
1383     m_muteTimerHandler = 0;
1384 
1385     if (!m_player || !m_playBin)
1386         return;
1387 
1388     gboolean muted;
1389     g_object_get(m_playBin, "mute", &muted, NULL);
1390     m_player->muteChanged(static_cast<bool>(muted));
1391 }
1392 
muteChanged()1393 void MediaPlayerPrivateGStreamer::muteChanged()
1394 {
1395     if (m_muteTimerHandler)
1396         g_source_remove(m_muteTimerHandler);
1397     m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this);
1398 }
1399 
loadingFailed(MediaPlayer::NetworkState error)1400 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1401 {
1402     m_errorOccured = true;
1403     if (m_networkState != error) {
1404         m_networkState = error;
1405         m_player->networkStateChanged();
1406     }
1407     if (m_readyState != MediaPlayer::HaveNothing) {
1408         m_readyState = MediaPlayer::HaveNothing;
1409         m_player->readyStateChanged();
1410     }
1411 }
1412 
setSize(const IntSize & size)1413 void MediaPlayerPrivateGStreamer::setSize(const IntSize& size)
1414 {
1415     m_size = size;
1416 }
1417 
setVisible(bool visible)1418 void MediaPlayerPrivateGStreamer::setVisible(bool visible)
1419 {
1420 }
1421 
1422 
triggerRepaint(GstBuffer * buffer)1423 void MediaPlayerPrivateGStreamer::triggerRepaint(GstBuffer* buffer)
1424 {
1425     g_return_if_fail(GST_IS_BUFFER(buffer));
1426     gst_buffer_replace(&m_buffer, buffer);
1427     m_player->repaint();
1428 }
1429 
paint(GraphicsContext * context,const IntRect & rect)1430 void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& rect)
1431 {
1432     if (context->paintingDisabled())
1433         return;
1434 
1435     if (!m_player->visible())
1436         return;
1437 
1438     if (!m_buffer)
1439         return;
1440 
1441     RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer);
1442     if (!gstImage)
1443         return;
1444 
1445     context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
1446                        rect, CompositeCopy, false);
1447 }
1448 
mimeTypeCache()1449 static HashSet<String> mimeTypeCache()
1450 {
1451 
1452     doGstInit();
1453 
1454     DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1455     static bool typeListInitialized = false;
1456 
1457     if (!typeListInitialized) {
1458         // Build a whitelist of mime-types known to be supported by
1459         // GStreamer.
1460         HashSet<String> handledApplicationSubtypes;
1461         handledApplicationSubtypes.add(String("ogg"));
1462         handledApplicationSubtypes.add(String("vnd.rn-realmedia"));
1463         handledApplicationSubtypes.add(String("x-pn-realaudio"));
1464 
1465         GList* factories = gst_type_find_factory_get_list();
1466         for (GList* iterator = factories; iterator; iterator = iterator->next) {
1467             GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data);
1468             GstCaps* caps = gst_type_find_factory_get_caps(factory);
1469             gchar** extensions;
1470 
1471             if (!caps)
1472                 continue;
1473 
1474             for (guint structureIndex = 0; structureIndex < gst_caps_get_size(caps); structureIndex++) {
1475                 GstStructure* structure = gst_caps_get_structure(caps, structureIndex);
1476                 const gchar* name = gst_structure_get_name(structure);
1477                 bool cached = false;
1478 
1479                 // These formats are supported by GStreamer, but not
1480                 // correctly advertised.
1481                 if (g_str_equal(name, "video/x-h264")) {
1482                     cache.add(String("video/mp4"));
1483                     cached = true;
1484                 }
1485 
1486                 if (g_str_equal(name, "audio/x-m4a")) {
1487                     cache.add(String("audio/aac"));
1488                     cache.add(String("audio/mp4"));
1489                     cache.add(String("audio/x-m4a"));
1490                     cached = true;
1491                 }
1492 
1493                 if (g_str_equal(name, "application/x-3gp")) {
1494                     cache.add(String("audio/3gpp"));
1495                     cache.add(String("video/3gpp"));
1496                     cache.add(String("application/x-3gp"));
1497                     cached = true;
1498                 }
1499 
1500                 if (g_str_equal(name, "video/x-theora")) {
1501                     cache.add(String("video/ogg"));
1502                     cached = true;
1503                 }
1504 
1505                 if (g_str_equal(name, "audio/x-vorbis")) {
1506                     cache.add(String("audio/ogg"));
1507                     cache.add(String("audio/x-vorbis+ogg"));
1508                     cached = true;
1509                 }
1510 
1511                 if (g_str_equal(name, "audio/x-wav")) {
1512                     cache.add(String("audio/wav"));
1513                     cache.add(String("audio/x-wav"));
1514                     cached = true;
1515                 }
1516 
1517                 if (g_str_equal(name, "audio/mpeg")) {
1518                     cache.add(String(name));
1519                     cache.add(String("audio/x-mpeg"));
1520                     cached = true;
1521 
1522                     // This is what we are handling:
1523                     // mpegversion=(int)1, layer=(int)[ 1, 3 ]
1524                     gint mpegVersion = 0;
1525                     if (gst_structure_get_int(structure, "mpegversion", &mpegVersion) && (mpegVersion == 1)) {
1526                         const GValue* layer = gst_structure_get_value(structure, "layer");
1527                         if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) {
1528                             gint minLayer = gst_value_get_int_range_min(layer);
1529                             gint maxLayer = gst_value_get_int_range_max(layer);
1530                             if (minLayer <= 1 && 1 <= maxLayer)
1531                                 cache.add(String("audio/mp1"));
1532                             if (minLayer <= 2 && 2 <= maxLayer)
1533                                 cache.add(String("audio/mp2"));
1534                             if (minLayer <= 3 && 3 <= maxLayer) {
1535                                 cache.add(String("audio/x-mp3"));
1536                                 cache.add(String("audio/mp3"));
1537                             }
1538                         }
1539                     }
1540                 }
1541 
1542                 if (!cached) {
1543                     // GStreamer plugins can be capable of supporting
1544                     // types which WebKit supports by default. In that
1545                     // case, we should not consider these types
1546                     // supportable by GStreamer.  Examples of what
1547                     // GStreamer can support but should not be added:
1548                     // text/plain, text/html, image/jpeg,
1549                     // application/xml
1550                     gchar** mimetype = g_strsplit(name, "/", 2);
1551                     if (g_str_equal(mimetype[0], "audio")
1552                         || g_str_equal(mimetype[0], "video")
1553                         || (g_str_equal(mimetype[0], "application")
1554                             && handledApplicationSubtypes.contains(String(mimetype[1]))))
1555                         cache.add(String(name));
1556                     else if (g_str_equal(name, "application/x-hls"))
1557                         cache.add(String("application/vnd.apple.mpegurl"));
1558 
1559 
1560                     g_strfreev(mimetype);
1561                 }
1562 
1563                 // As a last resort try some special cases depending
1564                 // on the file extensions registered with the typefind
1565                 // factory.
1566                 if (!cached && (extensions = gst_type_find_factory_get_extensions(factory))) {
1567                     for (int index = 0; extensions[index]; index++) {
1568                         if (g_str_equal(extensions[index], "m4v"))
1569                             cache.add(String("video/x-m4v"));
1570 
1571                         // Workaround for
1572                         // https://bugzilla.gnome.org/show_bug.cgi?id=640709.
1573                         // typefindfunctions <= 0.10.32 doesn't
1574                         // register the H264 typefinder correctly so
1575                         // as a workaround we check the registered
1576                         // file extensions for it.
1577                         if (g_str_equal(extensions[index], "h264"))
1578                             cache.add(String("video/mp4"));
1579                     }
1580                 }
1581             }
1582         }
1583 
1584         gst_plugin_feature_list_free(factories);
1585         typeListInitialized = true;
1586     }
1587 
1588     return cache;
1589 }
1590 
getSupportedTypes(HashSet<String> & types)1591 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
1592 {
1593     types = mimeTypeCache();
1594 }
1595 
supportsType(const String & type,const String & codecs)1596 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs)
1597 {
1598     if (type.isNull() || type.isEmpty())
1599         return MediaPlayer::IsNotSupported;
1600 
1601     // spec says we should not return "probably" if the codecs string is empty
1602     if (mimeTypeCache().contains(type))
1603         return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1604     return MediaPlayer::IsNotSupported;
1605 }
1606 
hasSingleSecurityOrigin() const1607 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
1608 {
1609     return true;
1610 }
1611 
supportsFullscreen() const1612 bool MediaPlayerPrivateGStreamer::supportsFullscreen() const
1613 {
1614 #if defined(BUILDING_ON_LEOPARD)
1615     // See <rdar://problem/7389945>
1616     return false;
1617 #else
1618     return true;
1619 #endif
1620 }
1621 
platformMedia() const1622 PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const
1623 {
1624     PlatformMedia p;
1625     p.type = PlatformMedia::GStreamerGWorldType;
1626     p.media.gstreamerGWorld = m_gstGWorld.get();
1627     return p;
1628 }
1629 
setPreload(MediaPlayer::Preload preload)1630 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
1631 {
1632     ASSERT(m_playBin);
1633 
1634     m_preload = preload;
1635 
1636     GstPlayFlags flags;
1637     g_object_get(m_playBin, "flags", &flags, NULL);
1638     if (preload == MediaPlayer::None)
1639         g_object_set(m_playBin, "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
1640     else
1641         g_object_set(m_playBin, "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
1642 
1643     if (m_delayingLoad && m_preload != MediaPlayer::None) {
1644         m_delayingLoad = false;
1645         commitLoad();
1646     }
1647 }
1648 
createGSTPlayBin()1649 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
1650 {
1651     ASSERT(!m_playBin);
1652     m_playBin = gst_element_factory_make("playbin2", "play");
1653 
1654     m_gstGWorld = GStreamerGWorld::createGWorld(m_playBin);
1655 
1656     GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin));
1657     gst_bus_add_signal_watch(bus);
1658     g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
1659     gst_object_unref(bus);
1660 
1661     g_object_set(m_playBin, "mute", m_player->muted(), NULL);
1662 
1663     g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
1664     g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
1665     g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
1666     g_signal_connect(m_playBin, "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
1667     g_signal_connect(m_playBin, "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
1668 
1669     m_webkitVideoSink = webkit_video_sink_new();
1670 
1671     g_signal_connect(m_webkitVideoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
1672 
1673     m_videoSinkBin = gst_bin_new("sink");
1674     GstElement* videoTee = gst_element_factory_make("tee", "videoTee");
1675     GstElement* queue = gst_element_factory_make("queue", 0);
1676     GstElement* identity = gst_element_factory_make("identity", "videoValve");
1677 
1678     // Take ownership.
1679     gst_object_ref_sink(m_videoSinkBin);
1680 
1681     // Build a new video sink consisting of a bin containing a tee
1682     // (meant to distribute data to multiple video sinks) and our
1683     // internal video sink. For fullscreen we create an autovideosink
1684     // and initially block the data flow towards it and configure it
1685 
1686     gst_bin_add_many(GST_BIN(m_videoSinkBin), videoTee, queue, identity, NULL);
1687 
1688     // Link a new src pad from tee to queue1.
1689     GstPad* srcPad = gst_element_get_request_pad(videoTee, "src%d");
1690     GstPad* sinkPad = gst_element_get_static_pad(queue, "sink");
1691     gst_pad_link(srcPad, sinkPad);
1692     gst_object_unref(GST_OBJECT(srcPad));
1693     gst_object_unref(GST_OBJECT(sinkPad));
1694 
1695     GstElement* actualVideoSink = 0;
1696     m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
1697     if (m_fpsSink) {
1698         // The verbose property has been added in -bad 0.10.22. Making
1699         // this whole code depend on it because we don't want
1700         // fpsdiplaysink to spit data on stdout.
1701         GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory);
1702         if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) {
1703             g_object_set(m_fpsSink, "silent", TRUE , NULL);
1704 
1705             // Turn off text overlay unless logging is enabled.
1706             WTFLogChannel* channel = getChannelFromName("Media");
1707             if (channel->state != WTFLogChannelOn)
1708                 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
1709 
1710             if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) {
1711                 g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink, NULL);
1712                 gst_bin_add(GST_BIN(m_videoSinkBin), m_fpsSink);
1713                 actualVideoSink = m_fpsSink;
1714             } else
1715                 m_fpsSink = 0;
1716         } else
1717             m_fpsSink = 0;
1718     }
1719 
1720     if (!m_fpsSink) {
1721         gst_bin_add(GST_BIN(m_videoSinkBin), m_webkitVideoSink);
1722         actualVideoSink = m_webkitVideoSink;
1723     }
1724 
1725     ASSERT(actualVideoSink);
1726 #if GST_CHECK_VERSION(0, 10, 30)
1727         // Faster elements linking, if possible.
1728         gst_element_link_pads_full(queue, "src", identity, "sink", GST_PAD_LINK_CHECK_NOTHING);
1729         gst_element_link_pads_full(identity, "src", actualVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING);
1730 #else
1731         gst_element_link_many(queue, identity, actualVideoSink, NULL);
1732 #endif
1733 
1734     // Add a ghostpad to the bin so it can proxy to tee.
1735     GstPad* pad = gst_element_get_static_pad(videoTee, "sink");
1736     gst_element_add_pad(m_videoSinkBin, gst_ghost_pad_new("sink", pad));
1737     gst_object_unref(GST_OBJECT(pad));
1738 
1739     // Set the bin as video sink of playbin.
1740     g_object_set(m_playBin, "video-sink", m_videoSinkBin, NULL);
1741 
1742 
1743     pad = gst_element_get_static_pad(m_webkitVideoSink, "sink");
1744     if (pad) {
1745         g_signal_connect(pad, "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
1746         gst_object_unref(GST_OBJECT(pad));
1747     }
1748 
1749 }
1750 
1751 }
1752 
1753 #endif // USE(GSTREAMER)
1754