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, 2011, 2012, 2013 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 ENABLE(VIDEO) && USE(GSTREAMER)
28
29#include "GStreamerUtilities.h"
30#include "GStreamerVersioning.h"
31#include "KURL.h"
32#include "Logging.h"
33#include "MIMETypeRegistry.h"
34#include "MediaPlayer.h"
35#include "NotImplemented.h"
36#include "SecurityOrigin.h"
37#include "TimeRanges.h"
38#include "WebKitWebSourceGStreamer.h"
39#include <gst/gst.h>
40#include <gst/pbutils/missing-plugins.h>
41#include <limits>
42#include <wtf/gobject/GOwnPtr.h>
43#include <wtf/text/CString.h>
44
45#ifdef GST_API_VERSION_1
46#include <gst/audio/streamvolume.h>
47#else
48#include <gst/interfaces/streamvolume.h>
49#endif
50
51// GstPlayFlags flags from playbin2. It is the policy of GStreamer to
52// not publicly expose element-specific enums. That's why this
53// GstPlayFlags enum has been copied here.
54typedef enum {
55    GST_PLAY_FLAG_VIDEO         = 0x00000001,
56    GST_PLAY_FLAG_AUDIO         = 0x00000002,
57    GST_PLAY_FLAG_TEXT          = 0x00000004,
58    GST_PLAY_FLAG_VIS           = 0x00000008,
59    GST_PLAY_FLAG_SOFT_VOLUME   = 0x00000010,
60    GST_PLAY_FLAG_NATIVE_AUDIO  = 0x00000020,
61    GST_PLAY_FLAG_NATIVE_VIDEO  = 0x00000040,
62    GST_PLAY_FLAG_DOWNLOAD      = 0x00000080,
63    GST_PLAY_FLAG_BUFFERING     = 0x000000100
64} GstPlayFlags;
65
66// gPercentMax is used when parsing buffering ranges with
67// gst_query_parse_nth_buffering_range as there was a bug in GStreamer
68// 0.10 that was using 100 instead of GST_FORMAT_PERCENT_MAX. This was
69// corrected in 1.0. gst_query_parse_buffering_range worked as
70// expected with GST_FORMAT_PERCENT_MAX in both cases.
71#ifdef GST_API_VERSION_1
72static const char* gPlaybinName = "playbin";
73static const gint64 gPercentMax = GST_FORMAT_PERCENT_MAX;
74#else
75static const char* gPlaybinName = "playbin2";
76static const gint64 gPercentMax = 100;
77#endif
78
79GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
80#define GST_CAT_DEFAULT webkit_media_player_debug
81
82using namespace std;
83
84namespace WebCore {
85
86static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
87{
88    return player->handleMessage(message);
89}
90
91static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
92{
93    player->sourceChanged();
94}
95
96static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
97{
98    player->videoChanged();
99}
100
101static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
102{
103    player->videoChanged();
104}
105
106static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
107{
108    player->audioChanged();
109}
110
111static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
112{
113    // This is the callback of the timeout source created in ::audioChanged.
114    player->notifyPlayerOfAudio();
115    return FALSE;
116}
117
118#ifdef GST_API_VERSION_1
119static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, gchar*,
120    MediaPlayerPrivateGStreamer* player)
121#else
122static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, MediaPlayerPrivateGStreamer* player)
123#endif
124{
125    player->setAudioStreamProperties(object);
126}
127
128static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
129{
130    // This is the callback of the timeout source created in ::videoChanged.
131    player->notifyPlayerOfVideo();
132    return FALSE;
133}
134
135static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData)
136{
137    MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData);
138    player->handlePluginInstallerResult(result);
139}
140
141static GstClockTime toGstClockTime(float time)
142{
143    // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to
144    // round the microseconds so no floating point precision is lost and we can perform an accurate seek.
145    float seconds;
146    float microSeconds = modf(time, &seconds) * 1000000;
147    GTimeVal timeValue;
148    timeValue.tv_sec = static_cast<glong>(seconds);
149    timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
150    return GST_TIMEVAL_TO_TIME(timeValue);
151}
152
153void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object)
154{
155    if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
156        return;
157
158    const char* role = m_player->mediaPlayerClient() && m_player->mediaPlayerClient()->mediaPlayerIsVideo()
159        ? "video" : "music";
160    GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL);
161    g_object_set(object, "stream-properties", structure, NULL);
162    gst_structure_free(structure);
163    GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object)));
164    LOG_MEDIA_MESSAGE("Set media.role as %s at %s", role, elementName.get());
165}
166
167PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
168{
169    return adoptPtr(new MediaPlayerPrivateGStreamer(player));
170}
171
172void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
173{
174    if (isAvailable())
175        registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
176}
177
178bool initializeGStreamerAndRegisterWebKitElements()
179{
180    if (!initializeGStreamer())
181        return false;
182
183    GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc");
184    if (!srcFactory) {
185        GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player");
186        return gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
187    }
188
189    return true;
190}
191
192bool MediaPlayerPrivateGStreamer::isAvailable()
193{
194    if (!initializeGStreamerAndRegisterWebKitElements())
195        return false;
196
197    GRefPtr<GstElementFactory> factory = gst_element_factory_find(gPlaybinName);
198    return factory;
199}
200
201MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
202    : MediaPlayerPrivateGStreamerBase(player)
203    , m_source(0)
204    , m_seekTime(0)
205    , m_changingRate(false)
206    , m_endTime(numeric_limits<float>::infinity())
207    , m_isEndReached(false)
208    , m_isStreaming(false)
209    , m_mediaLocations(0)
210    , m_mediaLocationCurrentIndex(0)
211    , m_resetPipeline(false)
212    , m_paused(true)
213    , m_seeking(false)
214    , m_seekIsPending(false)
215    , m_timeOfOverlappingSeek(-1)
216    , m_buffering(false)
217    , m_playbackRate(1)
218    , m_errorOccured(false)
219    , m_mediaDuration(0)
220    , m_startedBuffering(false)
221    , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
222    , m_maxTimeLoaded(0)
223    , m_bufferingPercentage(0)
224    , m_preload(player->preload())
225    , m_delayingLoad(false)
226    , m_mediaDurationKnown(true)
227    , m_maxTimeLoadedAtLastDidLoadingProgress(0)
228    , m_volumeAndMuteInitialized(false)
229    , m_hasVideo(false)
230    , m_hasAudio(false)
231    , m_audioTimerHandler(0)
232    , m_videoTimerHandler(0)
233    , m_webkitAudioSink(0)
234    , m_totalBytes(-1)
235    , m_preservesPitch(false)
236    , m_requestedState(GST_STATE_VOID_PENDING)
237    , m_missingPlugins(false)
238{
239}
240
241MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
242{
243    if (m_fillTimer.isActive())
244        m_fillTimer.stop();
245
246    if (m_mediaLocations) {
247        gst_structure_free(m_mediaLocations);
248        m_mediaLocations = 0;
249    }
250
251    if (m_autoAudioSink)
252        g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()),
253            reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this);
254
255    if (m_playBin) {
256        GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
257        ASSERT(bus);
258        g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this);
259        gst_bus_remove_signal_watch(bus.get());
260
261        g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateSourceChangedCallback), this);
262        g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoChangedCallback), this);
263        g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateAudioChangedCallback), this);
264
265        gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
266        m_playBin.clear();
267    }
268
269    GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
270    g_signal_handlers_disconnect_by_func(videoSinkPad.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
271
272    if (m_videoTimerHandler)
273        g_source_remove(m_videoTimerHandler);
274
275    if (m_audioTimerHandler)
276        g_source_remove(m_audioTimerHandler);
277}
278
279void MediaPlayerPrivateGStreamer::load(const String& url)
280{
281    if (!initializeGStreamerAndRegisterWebKitElements())
282        return;
283
284    KURL kurl(KURL(), url);
285    String cleanUrl(url);
286
287    // Clean out everything after file:// url path.
288    if (kurl.isLocalFile())
289        cleanUrl = cleanUrl.substring(0, kurl.pathEnd());
290
291    if (!m_playBin) {
292        createGSTPlayBin();
293        setDownloadBuffering();
294    }
295
296    ASSERT(m_playBin);
297
298    m_url = KURL(KURL(), cleanUrl);
299    g_object_set(m_playBin.get(), "uri", cleanUrl.utf8().data(), NULL);
300
301    INFO_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data());
302
303    if (m_preload == MediaPlayer::None) {
304        LOG_MEDIA_MESSAGE("Delaying load.");
305        m_delayingLoad = true;
306    }
307
308    // Reset network and ready states. Those will be set properly once
309    // the pipeline pre-rolled.
310    m_networkState = MediaPlayer::Loading;
311    m_player->networkStateChanged();
312    m_readyState = MediaPlayer::HaveNothing;
313    m_player->readyStateChanged();
314    m_volumeAndMuteInitialized = false;
315
316    if (!m_delayingLoad)
317        commitLoad();
318}
319
320#if ENABLE(MEDIA_SOURCE)
321void MediaPlayerPrivateGStreamer::load(const String& url, PassRefPtr<MediaSource>)
322{
323    notImplemented();
324}
325#endif
326
327void MediaPlayerPrivateGStreamer::commitLoad()
328{
329    ASSERT(!m_delayingLoad);
330    LOG_MEDIA_MESSAGE("Committing load.");
331
332    // GStreamer needs to have the pipeline set to a paused state to
333    // start providing anything useful.
334    gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
335
336    updateStates();
337}
338
339float MediaPlayerPrivateGStreamer::playbackPosition() const
340{
341    if (m_isEndReached) {
342        // Position queries on a null pipeline return 0. If we're at
343        // the end of the stream the pipeline is null but we want to
344        // report either the seek time or the duration because this is
345        // what the Media element spec expects us to do.
346        if (m_seeking)
347            return m_seekTime;
348        if (m_mediaDuration)
349            return m_mediaDuration;
350        return 0;
351    }
352
353    // Position is only available if no async state change is going on and the state is either paused or playing.
354    gint64 position = GST_CLOCK_TIME_NONE;
355    GstQuery* query= gst_query_new_position(GST_FORMAT_TIME);
356    if (gst_element_query(m_playBin.get(), query))
357        gst_query_parse_position(query, 0, &position);
358
359    float result = 0.0f;
360    if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE)
361        result = static_cast<double>(position) / GST_SECOND;
362    else if (m_canFallBackToLastFinishedSeekPositon)
363        result = m_seekTime;
364
365    LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
366
367    gst_query_unref(query);
368
369    return result;
370}
371
372bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
373{
374    ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
375
376    GstState currentState;
377    GstState pending;
378
379    gst_element_get_state(m_playBin.get(), &currentState, &pending, 0);
380    if (currentState == newState || pending == newState) {
381        LOG_MEDIA_MESSAGE("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState),
382            gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
383        return true;
384    }
385
386    LOG_MEDIA_MESSAGE("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
387        gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
388
389    GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState);
390    GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
391    if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) {
392        loadingFailed(MediaPlayer::Empty);
393        return false;
394    }
395    return true;
396}
397
398void MediaPlayerPrivateGStreamer::prepareToPlay()
399{
400    if (m_delayingLoad) {
401        m_delayingLoad = false;
402        commitLoad();
403    }
404}
405
406void MediaPlayerPrivateGStreamer::play()
407{
408    if (changePipelineState(GST_STATE_PLAYING)) {
409        m_isEndReached = false;
410        LOG_MEDIA_MESSAGE("Play");
411    }
412}
413
414void MediaPlayerPrivateGStreamer::pause()
415{
416    if (m_isEndReached)
417        return;
418
419    if (changePipelineState(GST_STATE_PAUSED))
420        INFO_MEDIA_MESSAGE("Pause");
421}
422
423float MediaPlayerPrivateGStreamer::duration() const
424{
425    if (!m_playBin)
426        return 0.0f;
427
428    if (m_errorOccured)
429        return 0.0f;
430
431    // Media duration query failed already, don't attempt new useless queries.
432    if (!m_mediaDurationKnown)
433        return numeric_limits<float>::infinity();
434
435    if (m_mediaDuration)
436        return m_mediaDuration;
437
438    GstFormat timeFormat = GST_FORMAT_TIME;
439    gint64 timeLength = 0;
440
441#ifdef GST_API_VERSION_1
442    bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
443#else
444    bool failure = !gst_element_query_duration(m_playBin.get(), &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
445#endif
446    if (failure) {
447        LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data());
448        return numeric_limits<float>::infinity();
449    }
450
451    LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
452
453    m_mediaDuration = static_cast<double>(timeLength) / GST_SECOND;
454    return m_mediaDuration;
455    // FIXME: handle 3.14.9.5 properly
456}
457
458float MediaPlayerPrivateGStreamer::currentTime() const
459{
460    if (!m_playBin)
461        return 0.0f;
462
463    if (m_errorOccured)
464        return 0.0f;
465
466    if (m_seeking)
467        return m_seekTime;
468
469    // Workaround for
470    // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer
471    // 0.10.35 basesink reports wrong duration in case of EOS and
472    // negative playback rate. There's no upstream accepted patch for
473    // this bug yet, hence this temporary workaround.
474    if (m_isEndReached && m_playbackRate < 0)
475        return 0.0f;
476
477    return playbackPosition();
478}
479
480void MediaPlayerPrivateGStreamer::seek(float time)
481{
482    if (!m_playBin)
483        return;
484
485    if (m_errorOccured)
486        return;
487
488    INFO_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time);
489
490    // Avoid useless seeking.
491    if (time == currentTime())
492        return;
493
494    if (isLiveStream())
495        return;
496
497    GstClockTime clockTime = toGstClockTime(time);
498    INFO_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
499
500    if (m_seeking) {
501        if (m_seekIsPending) {
502            m_seekTime = time;
503            return;
504        }
505        m_timeOfOverlappingSeek = time;
506    }
507
508    GstState state;
509    GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0);
510    if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
511        LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
512        return;
513    }
514    if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) {
515        m_seekIsPending = true;
516        if (m_isEndReached) {
517            LOG_MEDIA_MESSAGE("[Seek] reset pipeline");
518            m_resetPipeline = true;
519            changePipelineState(GST_STATE_PAUSED);
520        }
521    } else {
522        // We can seek now.
523        if (!gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
524            GST_SEEK_TYPE_SET, clockTime, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
525            LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time);
526            return;
527        }
528    }
529
530    m_seeking = true;
531    m_seekTime = time;
532    m_isEndReached = false;
533}
534
535bool MediaPlayerPrivateGStreamer::paused() const
536{
537    if (m_isEndReached) {
538        LOG_MEDIA_MESSAGE("Ignoring pause at EOS");
539        return true;
540    }
541
542    GstState state;
543    gst_element_get_state(m_playBin.get(), &state, 0, 0);
544    return state == GST_STATE_PAUSED;
545}
546
547bool MediaPlayerPrivateGStreamer::seeking() const
548{
549    return m_seeking;
550}
551
552void MediaPlayerPrivateGStreamer::videoChanged()
553{
554    if (m_videoTimerHandler)
555        g_source_remove(m_videoTimerHandler);
556    m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this);
557}
558
559void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
560{
561    m_videoTimerHandler = 0;
562
563    gint videoTracks = 0;
564    if (m_playBin)
565        g_object_get(m_playBin.get(), "n-video", &videoTracks, NULL);
566
567    m_hasVideo = videoTracks > 0;
568
569    m_videoSize = IntSize();
570
571    m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
572}
573
574void MediaPlayerPrivateGStreamer::audioChanged()
575{
576    if (m_audioTimerHandler)
577        g_source_remove(m_audioTimerHandler);
578    m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this);
579}
580
581void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
582{
583    m_audioTimerHandler = 0;
584
585    gint audioTracks = 0;
586    if (m_playBin)
587        g_object_get(m_playBin.get(), "n-audio", &audioTracks, NULL);
588    m_hasAudio = audioTracks > 0;
589    m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
590}
591
592void MediaPlayerPrivateGStreamer::setRate(float rate)
593{
594    // Avoid useless playback rate update.
595    if (m_playbackRate == rate)
596        return;
597
598    GstState state;
599    GstState pending;
600
601    gst_element_get_state(m_playBin.get(), &state, &pending, 0);
602    if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
603        || (pending == GST_STATE_PAUSED))
604        return;
605
606    if (isLiveStream())
607        return;
608
609    m_playbackRate = rate;
610    m_changingRate = true;
611
612    if (!rate) {
613        gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
614        return;
615    }
616
617    float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
618    GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
619    gint64 start, end;
620    bool mute = false;
621
622    INFO_MEDIA_MESSAGE("Set Rate to %f", rate);
623    if (rate > 0) {
624        // Mute the sound if the playback rate is too extreme and
625        // audio pitch is not adjusted.
626        mute = (!m_preservesPitch && (rate < 0.8 || rate > 2));
627        start = currentPosition;
628        end = GST_CLOCK_TIME_NONE;
629    } else {
630        start = 0;
631        mute = true;
632
633        // If we are at beginning of media, start from the end to
634        // avoid immediate EOS.
635        if (currentPosition <= 0)
636            end = static_cast<gint64>(duration() * GST_SECOND);
637        else
638            end = currentPosition;
639    }
640
641    INFO_MEDIA_MESSAGE("Need to mute audio?: %d", (int) mute);
642
643    if (!gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, flags,
644                          GST_SEEK_TYPE_SET, start,
645                          GST_SEEK_TYPE_SET, end))
646        ERROR_MEDIA_MESSAGE("Set rate to %f failed", rate);
647    else
648        g_object_set(m_playBin.get(), "mute", mute, NULL);
649}
650
651void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
652{
653    m_preservesPitch = preservesPitch;
654}
655
656PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
657{
658    RefPtr<TimeRanges> timeRanges = TimeRanges::create();
659    if (m_errorOccured || isLiveStream())
660        return timeRanges.release();
661
662#if GST_CHECK_VERSION(0, 10, 31)
663    float mediaDuration(duration());
664    if (!mediaDuration || std::isinf(mediaDuration))
665        return timeRanges.release();
666
667    GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
668
669    if (!gst_element_query(m_playBin.get(), query)) {
670        gst_query_unref(query);
671        return timeRanges.release();
672    }
673
674    for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
675        gint64 rangeStart = 0, rangeStop = 0;
676        if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
677            timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / gPercentMax),
678                static_cast<float>((rangeStop * mediaDuration) / gPercentMax));
679    }
680
681    // Fallback to the more general maxTimeLoaded() if no range has
682    // been found.
683    if (!timeRanges->length())
684        if (float loaded = maxTimeLoaded())
685            timeRanges->add(0, loaded);
686
687    gst_query_unref(query);
688#else
689    float loaded = maxTimeLoaded();
690    if (!m_errorOccured && !isLiveStream() && loaded > 0)
691        timeRanges->add(0, loaded);
692#endif
693    return timeRanges.release();
694}
695
696gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
697{
698    GOwnPtr<GError> err;
699    GOwnPtr<gchar> debug;
700    MediaPlayer::NetworkState error;
701    bool issueError = true;
702    bool attemptNextLocation = false;
703    const GstStructure* structure = gst_message_get_structure(message);
704    GstState requestedState, currentState;
705
706    m_canFallBackToLastFinishedSeekPositon = false;
707
708    if (structure) {
709        const gchar* messageTypeName = gst_structure_get_name(structure);
710
711        // Redirect messages are sent from elements, like qtdemux, to
712        // notify of the new location(s) of the media.
713        if (!g_strcmp0(messageTypeName, "redirect")) {
714            mediaLocationChanged(message);
715            return TRUE;
716        }
717    }
718
719    // We ignore state changes from internal elements. They are forwarded to playbin2 anyway.
720    bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get());
721
722    LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
723    switch (GST_MESSAGE_TYPE(message)) {
724    case GST_MESSAGE_ERROR:
725        if (m_resetPipeline)
726            break;
727        if (m_missingPlugins)
728            break;
729        gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
730        ERROR_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
731
732        GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
733
734        error = MediaPlayer::Empty;
735        if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
736            || err->code == GST_STREAM_ERROR_WRONG_TYPE
737            || err->code == GST_STREAM_ERROR_FAILED
738            || err->code == GST_CORE_ERROR_MISSING_PLUGIN
739            || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
740            error = MediaPlayer::FormatError;
741        else if (err->domain == GST_STREAM_ERROR) {
742            // Let the mediaPlayerClient handle the stream error, in
743            // this case the HTMLMediaElement will emit a stalled
744            // event.
745            if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
746                ERROR_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
747                break;
748            }
749            error = MediaPlayer::DecodeError;
750            attemptNextLocation = true;
751        } else if (err->domain == GST_RESOURCE_ERROR)
752            error = MediaPlayer::NetworkError;
753
754        if (attemptNextLocation)
755            issueError = !loadNextLocation();
756        if (issueError)
757            loadingFailed(error);
758        break;
759    case GST_MESSAGE_EOS:
760        didEnd();
761        break;
762    case GST_MESSAGE_ASYNC_DONE:
763        if (!messageSourceIsPlaybin || m_delayingLoad)
764            break;
765        asyncStateChangeDone();
766        break;
767    case GST_MESSAGE_STATE_CHANGED: {
768        if (!messageSourceIsPlaybin || m_delayingLoad)
769            break;
770        updateStates();
771
772        // Construct a filename for the graphviz dot file output.
773        GstState newState;
774        gst_message_parse_state_changed(message, &currentState, &newState, 0);
775        CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8();
776        GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
777
778        break;
779    }
780    case GST_MESSAGE_BUFFERING:
781        processBufferingStats(message);
782        break;
783#ifdef GST_API_VERSION_1
784    case GST_MESSAGE_DURATION_CHANGED:
785#else
786    case GST_MESSAGE_DURATION:
787#endif
788        if (messageSourceIsPlaybin)
789            durationChanged();
790        break;
791    case GST_MESSAGE_REQUEST_STATE:
792        gst_message_parse_request_state(message, &requestedState);
793        gst_element_get_state(m_playBin.get(), &currentState, NULL, 250);
794        if (requestedState < currentState) {
795            GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message)));
796            INFO_MEDIA_MESSAGE("Element %s requested state change to %s", elementName.get(),
797                gst_element_state_get_name(requestedState));
798            m_requestedState = requestedState;
799            changePipelineState(requestedState);
800        }
801        break;
802    case GST_MESSAGE_ELEMENT:
803        if (gst_is_missing_plugin_message(message)) {
804            gchar* detail = gst_missing_plugin_message_get_installer_detail(message);
805            gchar* detailArray[2] = {detail, 0};
806            GstInstallPluginsReturn result = gst_install_plugins_async(detailArray, 0, mediaPlayerPrivatePluginInstallerResultFunction, this);
807            m_missingPlugins = result == GST_INSTALL_PLUGINS_STARTED_OK;
808            g_free(detail);
809        }
810        break;
811    default:
812        LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s",
813                    GST_MESSAGE_TYPE_NAME(message));
814        break;
815    }
816    return TRUE;
817}
818
819void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsReturn result)
820{
821    m_missingPlugins = false;
822    if (result == GST_INSTALL_PLUGINS_SUCCESS) {
823        gst_element_set_state(m_playBin.get(), GST_STATE_READY);
824        gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
825    }
826}
827
828void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
829{
830    // This is the immediate buffering that needs to happen so we have
831    // enough to play right now.
832    m_buffering = true;
833    const GstStructure *structure = gst_message_get_structure(message);
834    gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
835
836    LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
837
838    GstBufferingMode mode;
839    gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
840    if (mode != GST_BUFFERING_DOWNLOAD) {
841        updateStates();
842        return;
843    }
844
845    // This is on-disk buffering, that allows us to download much more
846    // than needed for right now.
847    if (!m_startedBuffering) {
848        LOG_MEDIA_MESSAGE("[Buffering] Starting on-disk buffering.");
849
850        m_startedBuffering = true;
851
852        if (m_fillTimer.isActive())
853            m_fillTimer.stop();
854
855        m_fillTimer.startRepeating(0.2);
856    }
857
858    if (!m_paused && m_bufferingPercentage < 100) {
859        LOG_MEDIA_MESSAGE("[Buffering] Download in progress, pausing pipeline.");
860        gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
861    }
862}
863
864void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
865{
866    GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
867
868    if (!gst_element_query(m_playBin.get(), query)) {
869        gst_query_unref(query);
870        return;
871    }
872
873    gint64 start, stop;
874    gdouble fillStatus = 100.0;
875
876    gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
877    gst_query_unref(query);
878
879    if (stop != -1)
880        fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
881
882    LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus);
883
884    if (!m_mediaDuration)
885        durationChanged();
886
887    // Update maxTimeLoaded only if the media duration is
888    // available. Otherwise we can't compute it.
889    if (m_mediaDuration) {
890        if (fillStatus == 100.0)
891            m_maxTimeLoaded = m_mediaDuration;
892        else
893            m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
894        LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
895    }
896
897    if (fillStatus != 100.0) {
898        updateStates();
899        return;
900    }
901
902    // Media is now fully loaded. It will play even if network
903    // connection is cut. Buffering is done, remove the fill source
904    // from the main loop.
905    m_fillTimer.stop();
906    m_startedBuffering = false;
907    updateStates();
908}
909
910float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
911{
912    if (m_errorOccured)
913        return 0.0f;
914
915    LOG_MEDIA_MESSAGE("maxTimeSeekable");
916    // infinite duration means live stream
917    if (std::isinf(duration()))
918        return 0.0f;
919
920    return duration();
921}
922
923float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
924{
925    if (m_errorOccured)
926        return 0.0f;
927
928    float loaded = m_maxTimeLoaded;
929    if (!loaded && !m_fillTimer.isActive())
930        loaded = duration();
931    LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded);
932    return loaded;
933}
934
935bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
936{
937    if (!m_playBin || !m_mediaDuration || !totalBytes())
938        return false;
939    float currentMaxTimeLoaded = maxTimeLoaded();
940    bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
941    m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
942    LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress);
943    return didLoadingProgress;
944}
945
946unsigned MediaPlayerPrivateGStreamer::totalBytes() const
947{
948    if (m_errorOccured)
949        return 0;
950
951    if (m_totalBytes != -1)
952        return m_totalBytes;
953
954    if (!m_source)
955        return 0;
956
957    GstFormat fmt = GST_FORMAT_BYTES;
958    gint64 length = 0;
959#ifdef GST_API_VERSION_1
960    if (gst_element_query_duration(m_source.get(), fmt, &length)) {
961#else
962    if (gst_element_query_duration(m_source.get(), &fmt, &length)) {
963#endif
964        INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
965        m_totalBytes = static_cast<unsigned>(length);
966        m_isStreaming = !length;
967        return m_totalBytes;
968    }
969
970    // Fall back to querying the source pads manually.
971    // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
972    GstIterator* iter = gst_element_iterate_src_pads(m_source.get());
973    bool done = false;
974    while (!done) {
975#ifdef GST_API_VERSION_1
976        GValue item = G_VALUE_INIT;
977        switch (gst_iterator_next(iter, &item)) {
978        case GST_ITERATOR_OK: {
979            GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item));
980            gint64 padLength = 0;
981            if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length)
982                length = padLength;
983            break;
984        }
985#else
986        gpointer data;
987
988        switch (gst_iterator_next(iter, &data)) {
989        case GST_ITERATOR_OK: {
990            GRefPtr<GstPad> pad = adoptGRef(GST_PAD_CAST(data));
991            gint64 padLength = 0;
992            if (gst_pad_query_duration(pad.get(), &fmt, &padLength) && padLength > length)
993                length = padLength;
994            break;
995        }
996#endif
997        case GST_ITERATOR_RESYNC:
998            gst_iterator_resync(iter);
999            break;
1000        case GST_ITERATOR_ERROR:
1001            // Fall through.
1002        case GST_ITERATOR_DONE:
1003            done = true;
1004            break;
1005        }
1006
1007#ifdef GST_API_VERSION_1
1008        g_value_unset(&item);
1009#endif
1010    }
1011
1012    gst_iterator_free(iter);
1013
1014    INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1015    m_totalBytes = static_cast<unsigned>(length);
1016    m_isStreaming = !length;
1017    return m_totalBytes;
1018}
1019
1020void MediaPlayerPrivateGStreamer::updateAudioSink()
1021{
1022    if (!m_playBin)
1023        return;
1024
1025    GstElement* sinkPtr = 0;
1026
1027    g_object_get(m_playBin.get(), "audio-sink", &sinkPtr, NULL);
1028    m_webkitAudioSink = adoptGRef(sinkPtr);
1029
1030}
1031
1032GstElement* MediaPlayerPrivateGStreamer::audioSink() const
1033{
1034    return m_webkitAudioSink.get();
1035}
1036
1037void MediaPlayerPrivateGStreamer::sourceChanged()
1038{
1039    GstElement* srcPtr = 0;
1040
1041    g_object_get(m_playBin.get(), "source", &srcPtr, NULL);
1042    m_source = adoptGRef(srcPtr);
1043
1044    if (WEBKIT_IS_WEB_SRC(m_source.get()))
1045        webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
1046}
1047
1048void MediaPlayerPrivateGStreamer::cancelLoad()
1049{
1050    if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1051        return;
1052
1053    if (m_playBin)
1054        gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1055}
1056
1057void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
1058{
1059    if (!m_playBin || m_errorOccured)
1060        return;
1061
1062    if (m_seeking) {
1063        if (m_seekIsPending)
1064            updateStates();
1065        else {
1066            LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime);
1067            m_seeking = false;
1068            if (m_timeOfOverlappingSeek != -1) {
1069                seek(m_timeOfOverlappingSeek);
1070                m_timeOfOverlappingSeek = -1;
1071                return;
1072            }
1073
1074            // The pipeline can still have a pending state. In this case a position query will fail.
1075            // Right now we can use m_seekTime as a fallback.
1076            m_canFallBackToLastFinishedSeekPositon = true;
1077            timeChanged();
1078        }
1079    } else
1080        updateStates();
1081}
1082
1083void MediaPlayerPrivateGStreamer::updateStates()
1084{
1085    if (!m_playBin)
1086        return;
1087
1088    if (m_errorOccured)
1089        return;
1090
1091    MediaPlayer::NetworkState oldNetworkState = m_networkState;
1092    MediaPlayer::ReadyState oldReadyState = m_readyState;
1093    GstState state;
1094    GstState pending;
1095
1096    GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND);
1097
1098    bool shouldUpdatePlaybackState = false;
1099    switch (getStateResult) {
1100    case GST_STATE_CHANGE_SUCCESS:
1101        LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1102
1103        if (state <= GST_STATE_READY) {
1104            m_resetPipeline = true;
1105            m_mediaDuration = 0;
1106        } else
1107            cacheDuration();
1108
1109        // Try to figure out ready and network states.
1110        if (state == GST_STATE_READY) {
1111            m_readyState = MediaPlayer::HaveMetadata;
1112            m_networkState = MediaPlayer::Empty;
1113        } else if ((state == GST_STATE_NULL) || (maxTimeLoaded() == duration())) {
1114            m_networkState = MediaPlayer::Loaded;
1115            m_readyState = MediaPlayer::HaveEnoughData;
1116        } else {
1117            m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
1118            m_networkState = MediaPlayer::Loading;
1119        }
1120
1121        if (m_buffering && state != GST_STATE_READY) {
1122            m_readyState = MediaPlayer::HaveCurrentData;
1123            m_networkState = MediaPlayer::Loading;
1124        }
1125
1126        // Now let's try to get the states in more detail using
1127        // information from GStreamer, while we sync states where
1128        // needed.
1129        if (state == GST_STATE_PAUSED) {
1130            if (!m_webkitAudioSink)
1131                updateAudioSink();
1132
1133            if (!m_volumeAndMuteInitialized) {
1134                notifyPlayerOfVolumeChange();
1135                notifyPlayerOfMute();
1136                m_volumeAndMuteInitialized = true;
1137            }
1138
1139            if (m_buffering && m_bufferingPercentage == 100) {
1140                m_buffering = false;
1141                m_bufferingPercentage = 0;
1142                m_readyState = MediaPlayer::HaveEnoughData;
1143
1144                LOG_MEDIA_MESSAGE("[Buffering] Complete.");
1145
1146                if (!m_paused) {
1147                    LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
1148                    gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1149                }
1150            } else if (!m_buffering && (currentTime() < duration())) {
1151                m_paused = true;
1152            }
1153        } else if (state == GST_STATE_PLAYING) {
1154            m_readyState = MediaPlayer::HaveEnoughData;
1155            m_paused = false;
1156
1157            if (m_buffering && !isLiveStream()) {
1158                m_readyState = MediaPlayer::HaveCurrentData;
1159                m_networkState = MediaPlayer::Loading;
1160
1161                LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
1162
1163                gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1164            }
1165        } else
1166            m_paused = true;
1167
1168        // Is on-disk buffering in progress?
1169        if (m_fillTimer.isActive())
1170            m_networkState = MediaPlayer::Loading;
1171
1172        if (m_changingRate) {
1173            m_player->rateChanged();
1174            m_changingRate = false;
1175        }
1176
1177        if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
1178            shouldUpdatePlaybackState = true;
1179            LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state));
1180        }
1181
1182        break;
1183    case GST_STATE_CHANGE_ASYNC:
1184        LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1185        // Change in progress
1186
1187        // On-disk buffering was attempted but the media is live. This
1188        // can't work so disable on-disk buffering and reset the
1189        // pipeline.
1190        if (state == GST_STATE_READY && isLiveStream() && m_preload == MediaPlayer::Auto) {
1191            setPreload(MediaPlayer::None);
1192            gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1193            gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1194        }
1195
1196        // A live stream was paused, reset the pipeline.
1197        if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream()) {
1198            gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1199            gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1200        }
1201
1202        break;
1203    case GST_STATE_CHANGE_FAILURE:
1204        LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1205        // Change failed
1206        return;
1207    case GST_STATE_CHANGE_NO_PREROLL:
1208        LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1209
1210        if (state == GST_STATE_READY)
1211            m_readyState = MediaPlayer::HaveNothing;
1212        else if (state == GST_STATE_PAUSED) {
1213            m_readyState = MediaPlayer::HaveEnoughData;
1214            m_paused = true;
1215            // Live pipelines go in PAUSED without prerolling.
1216            m_isStreaming = true;
1217        } else if (state == GST_STATE_PLAYING)
1218            m_paused = false;
1219
1220        if (!m_paused)
1221            gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1222
1223        m_networkState = MediaPlayer::Loading;
1224        break;
1225    default:
1226        LOG_MEDIA_MESSAGE("Else : %d", getStateResult);
1227        break;
1228    }
1229
1230    m_requestedState = GST_STATE_VOID_PENDING;
1231
1232    if (shouldUpdatePlaybackState)
1233        m_player->playbackStateChanged();
1234
1235    if (m_networkState != oldNetworkState) {
1236        LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState);
1237        m_player->networkStateChanged();
1238    }
1239    if (m_readyState != oldReadyState) {
1240        LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState);
1241        m_player->readyStateChanged();
1242    }
1243
1244    if (m_seekIsPending && getStateResult == GST_STATE_CHANGE_SUCCESS && (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)) {
1245        LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime);
1246        m_seekIsPending = false;
1247        m_seeking = gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
1248            GST_SEEK_TYPE_SET, toGstClockTime(m_seekTime), GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1249        if (!m_seeking)
1250            LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime);
1251    }
1252}
1253
1254void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1255{
1256    if (m_mediaLocations)
1257        gst_structure_free(m_mediaLocations);
1258
1259    const GstStructure* structure = gst_message_get_structure(message);
1260    if (structure) {
1261        // This structure can contain:
1262        // - both a new-location string and embedded locations structure
1263        // - or only a new-location string.
1264        m_mediaLocations = gst_structure_copy(structure);
1265        const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1266
1267        if (locations)
1268            m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1269
1270        loadNextLocation();
1271    }
1272}
1273
1274bool MediaPlayerPrivateGStreamer::loadNextLocation()
1275{
1276    if (!m_mediaLocations)
1277        return false;
1278
1279    const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1280    const gchar* newLocation = 0;
1281
1282    if (!locations) {
1283        // Fallback on new-location string.
1284        newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1285        if (!newLocation)
1286            return false;
1287    }
1288
1289    if (!newLocation) {
1290        if (m_mediaLocationCurrentIndex < 0) {
1291            m_mediaLocations = 0;
1292            return false;
1293        }
1294
1295        const GValue* location = gst_value_list_get_value(locations,
1296                                                          m_mediaLocationCurrentIndex);
1297        const GstStructure* structure = gst_value_get_structure(location);
1298
1299        if (!structure) {
1300            m_mediaLocationCurrentIndex--;
1301            return false;
1302        }
1303
1304        newLocation = gst_structure_get_string(structure, "new-location");
1305    }
1306
1307    if (newLocation) {
1308        // Found a candidate. new-location is not always an absolute url
1309        // though. We need to take the base of the current url and
1310        // append the value of new-location to it.
1311        KURL baseUrl = gst_uri_is_valid(newLocation) ? KURL() : m_url;
1312        KURL newUrl = KURL(baseUrl, newLocation);
1313
1314        RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url);
1315        if (securityOrigin->canRequest(newUrl)) {
1316            INFO_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
1317
1318            // Reset player states.
1319            m_networkState = MediaPlayer::Loading;
1320            m_player->networkStateChanged();
1321            m_readyState = MediaPlayer::HaveNothing;
1322            m_player->readyStateChanged();
1323
1324            // Reset pipeline state.
1325            m_resetPipeline = true;
1326            gst_element_set_state(m_playBin.get(), GST_STATE_READY);
1327
1328            GstState state;
1329            gst_element_get_state(m_playBin.get(), &state, 0, 0);
1330            if (state <= GST_STATE_READY) {
1331                // Set the new uri and start playing.
1332                g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
1333                m_url = newUrl;
1334                gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1335                return true;
1336            }
1337        } else
1338            INFO_MEDIA_MESSAGE("Not allowed to load new media location: %s", newUrl.string().utf8().data());
1339    }
1340    m_mediaLocationCurrentIndex--;
1341    return false;
1342
1343}
1344
1345void MediaPlayerPrivateGStreamer::loadStateChanged()
1346{
1347    updateStates();
1348}
1349
1350void MediaPlayerPrivateGStreamer::timeChanged()
1351{
1352    updateStates();
1353    m_player->timeChanged();
1354}
1355
1356void MediaPlayerPrivateGStreamer::didEnd()
1357{
1358    // Synchronize position and duration values to not confuse the
1359    // HTMLMediaElement. In some cases like reverse playback the
1360    // position is not always reported as 0 for instance.
1361    float now = currentTime();
1362    if (now > 0 && now <= duration() && m_mediaDuration != now) {
1363        m_mediaDurationKnown = true;
1364        m_mediaDuration = now;
1365        m_player->durationChanged();
1366    }
1367
1368    m_isEndReached = true;
1369    timeChanged();
1370
1371    if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
1372        m_paused = true;
1373        gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1374    }
1375}
1376
1377void MediaPlayerPrivateGStreamer::cacheDuration()
1378{
1379    if (m_mediaDuration || !m_mediaDurationKnown)
1380        return;
1381
1382    float newDuration = duration();
1383    if (std::isinf(newDuration)) {
1384        // Only pretend that duration is not available if the the query failed in a stable pipeline state.
1385        GstState state;
1386        if (gst_element_get_state(m_playBin.get(), &state, 0, 0) == GST_STATE_CHANGE_SUCCESS && state > GST_STATE_READY)
1387            m_mediaDurationKnown = false;
1388        return;
1389    }
1390
1391    m_mediaDuration = newDuration;
1392}
1393
1394void MediaPlayerPrivateGStreamer::durationChanged()
1395{
1396    float previousDuration = m_mediaDuration;
1397
1398    cacheDuration();
1399    // Avoid emiting durationchanged in the case where the previous
1400    // duration was 0 because that case is already handled by the
1401    // HTMLMediaElement.
1402    if (previousDuration && m_mediaDuration != previousDuration)
1403        m_player->durationChanged();
1404}
1405
1406void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1407{
1408    m_errorOccured = true;
1409    if (m_networkState != error) {
1410        m_networkState = error;
1411        m_player->networkStateChanged();
1412    }
1413    if (m_readyState != MediaPlayer::HaveNothing) {
1414        m_readyState = MediaPlayer::HaveNothing;
1415        m_player->readyStateChanged();
1416    }
1417}
1418
1419static HashSet<String> mimeTypeCache()
1420{
1421    initializeGStreamerAndRegisterWebKitElements();
1422
1423    DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1424    static bool typeListInitialized = false;
1425
1426    if (typeListInitialized)
1427        return cache;
1428
1429    const char* mimeTypes[] = {
1430        "application/ogg",
1431        "application/vnd.apple.mpegurl",
1432        "application/vnd.rn-realmedia",
1433        "application/x-3gp",
1434        "application/x-pn-realaudio",
1435        "audio/3gpp",
1436        "audio/aac",
1437        "audio/flac",
1438        "audio/iLBC-sh",
1439        "audio/midi",
1440        "audio/mobile-xmf",
1441        "audio/mp1",
1442        "audio/mp2",
1443        "audio/mp3",
1444        "audio/mp4",
1445        "audio/mpeg",
1446        "audio/ogg",
1447        "audio/opus",
1448        "audio/qcelp",
1449        "audio/riff-midi",
1450        "audio/speex",
1451        "audio/wav",
1452        "audio/webm",
1453        "audio/x-ac3",
1454        "audio/x-aiff",
1455        "audio/x-amr-nb-sh",
1456        "audio/x-amr-wb-sh",
1457        "audio/x-au",
1458        "audio/x-ay",
1459        "audio/x-celt",
1460        "audio/x-dts",
1461        "audio/x-flac",
1462        "audio/x-gbs",
1463        "audio/x-gsm",
1464        "audio/x-gym",
1465        "audio/x-imelody",
1466        "audio/x-ircam",
1467        "audio/x-kss",
1468        "audio/x-m4a",
1469        "audio/x-mod",
1470        "audio/x-mp3",
1471        "audio/x-mpeg",
1472        "audio/x-musepack",
1473        "audio/x-nist",
1474        "audio/x-nsf",
1475        "audio/x-paris",
1476        "audio/x-sap",
1477        "audio/x-sbc",
1478        "audio/x-sds",
1479        "audio/x-shorten",
1480        "audio/x-sid",
1481        "audio/x-spc",
1482        "audio/x-speex",
1483        "audio/x-svx",
1484        "audio/x-ttafile",
1485        "audio/x-vgm",
1486        "audio/x-voc",
1487        "audio/x-vorbis+ogg",
1488        "audio/x-w64",
1489        "audio/x-wav",
1490        "audio/x-wavpack",
1491        "audio/x-wavpack-correction",
1492        "video/3gpp",
1493        "video/mj2",
1494        "video/mp4",
1495        "video/mpeg",
1496        "video/mpegts",
1497        "video/ogg",
1498        "video/quicktime",
1499        "video/vivo",
1500        "video/webm",
1501        "video/x-cdxa",
1502        "video/x-dirac",
1503        "video/x-dv",
1504        "video/x-fli",
1505        "video/x-flv",
1506        "video/x-h263",
1507        "video/x-ivf",
1508        "video/x-m4v",
1509        "video/x-matroska",
1510        "video/x-mng",
1511        "video/x-ms-asf",
1512        "video/x-msvideo",
1513        "video/x-mve",
1514        "video/x-nuv",
1515        "video/x-vcd"
1516    };
1517
1518    for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i)
1519        cache.add(String(mimeTypes[i]));
1520
1521    typeListInitialized = true;
1522    return cache;
1523}
1524
1525void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
1526{
1527    types = mimeTypeCache();
1528}
1529
1530MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs, const KURL&)
1531{
1532    if (type.isNull() || type.isEmpty())
1533        return MediaPlayer::IsNotSupported;
1534
1535    // spec says we should not return "probably" if the codecs string is empty
1536    if (mimeTypeCache().contains(type))
1537        return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1538    return MediaPlayer::IsNotSupported;
1539}
1540
1541void MediaPlayerPrivateGStreamer::setDownloadBuffering()
1542{
1543    if (!m_playBin)
1544        return;
1545
1546    GstPlayFlags flags;
1547    g_object_get(m_playBin.get(), "flags", &flags, NULL);
1548    if (m_preload == MediaPlayer::Auto) {
1549        LOG_MEDIA_MESSAGE("Enabling on-disk buffering");
1550        g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
1551    } else {
1552        LOG_MEDIA_MESSAGE("Disabling on-disk buffering");
1553        g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
1554    }
1555}
1556
1557void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
1558{
1559    if (preload == MediaPlayer::Auto && isLiveStream())
1560        return;
1561
1562    m_preload = preload;
1563
1564    setDownloadBuffering();
1565
1566    if (m_delayingLoad && m_preload != MediaPlayer::None) {
1567        m_delayingLoad = false;
1568        commitLoad();
1569    }
1570}
1571
1572void MediaPlayerPrivateGStreamer::createAudioSink()
1573{
1574    // Construct audio sink if pitch preserving is enabled.
1575    if (!m_preservesPitch)
1576        return;
1577
1578    if (!m_playBin)
1579        return;
1580
1581    GstElement* scale = gst_element_factory_make("scaletempo", 0);
1582    if (!scale) {
1583        GST_WARNING("Failed to create scaletempo");
1584        return;
1585    }
1586
1587    GstElement* convert = gst_element_factory_make("audioconvert", 0);
1588    GstElement* resample = gst_element_factory_make("audioresample", 0);
1589    GstElement* sink = gst_element_factory_make("autoaudiosink", 0);
1590
1591    m_autoAudioSink = sink;
1592
1593    g_signal_connect(sink, "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
1594
1595    GstElement* audioSink = gst_bin_new("audio-sink");
1596    gst_bin_add_many(GST_BIN(audioSink), scale, convert, resample, sink, NULL);
1597
1598    if (!gst_element_link_many(scale, convert, resample, sink, NULL)) {
1599        GST_WARNING("Failed to link audio sink elements");
1600        gst_object_unref(audioSink);
1601        return;
1602    }
1603
1604    GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
1605    gst_element_add_pad(audioSink, gst_ghost_pad_new("sink", pad.get()));
1606
1607    g_object_set(m_playBin.get(), "audio-sink", audioSink, NULL);
1608}
1609
1610void MediaPlayerPrivateGStreamer::createGSTPlayBin()
1611{
1612    ASSERT(!m_playBin);
1613
1614    // gst_element_factory_make() returns a floating reference so
1615    // we should not adopt.
1616    m_playBin = gst_element_factory_make(gPlaybinName, "play");
1617    setStreamVolumeElement(GST_STREAM_VOLUME(m_playBin.get()));
1618
1619    GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
1620    gst_bus_add_signal_watch(bus.get());
1621    g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
1622
1623    g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
1624
1625    g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
1626    g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
1627    g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
1628
1629    GstElement* videoElement = createVideoSink(m_playBin.get());
1630
1631    g_object_set(m_playBin.get(), "video-sink", videoElement, NULL);
1632
1633    GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
1634    if (videoSinkPad)
1635        g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
1636
1637    createAudioSink();
1638}
1639
1640void MediaPlayerPrivateGStreamer::simulateAudioInterruption()
1641{
1642    GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_playBin.get()), GST_STATE_PAUSED);
1643    gst_element_post_message(m_playBin.get(), message);
1644}
1645
1646}
1647
1648#endif // USE(GSTREAMER)
1649