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