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