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 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * aint with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24
25#if ENABLE(VIDEO)
26
27#include "MediaPlayerPrivateGStreamer.h"
28
29
30#include "CString.h"
31#include "DataSourceGStreamer.h"
32#include "Document.h"
33#include "Frame.h"
34#include "FrameView.h"
35#include "GOwnPtrGtk.h"
36#include "GraphicsContext.h"
37#include "IntRect.h"
38#include "KURL.h"
39#include "MIMETypeRegistry.h"
40#include "MediaPlayer.h"
41#include "NotImplemented.h"
42#include "ScrollView.h"
43#include "SecurityOrigin.h"
44#include "TimeRanges.h"
45#include "VideoSinkGStreamer.h"
46#include "WebKitWebSourceGStreamer.h"
47#include "Widget.h"
48
49#include <gst/gst.h>
50#include <gst/interfaces/mixer.h>
51#include <gst/interfaces/xoverlay.h>
52#include <gst/video/video.h>
53#include <limits>
54#include <math.h>
55#include <webkit/webkitwebview.h>
56#include <wtf/gtk/GOwnPtr.h>
57
58using namespace std;
59
60namespace WebCore {
61
62gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data)
63{
64    GOwnPtr<GError> err;
65    GOwnPtr<gchar> debug;
66    MediaPlayer::NetworkState error;
67    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
68    gint percent = 0;
69    bool issueError = true;
70    bool attemptNextLocation = false;
71
72    if (message->structure) {
73        const gchar* messageTypeName = gst_structure_get_name(message->structure);
74
75        // Redirect messages are sent from elements, like qtdemux, to
76        // notify of the new location(s) of the media.
77        if (!g_strcmp0(messageTypeName, "redirect")) {
78            mp->mediaLocationChanged(message);
79            return true;
80        }
81    }
82
83    switch (GST_MESSAGE_TYPE(message)) {
84    case GST_MESSAGE_ERROR:
85        if (mp && mp->pipelineReset())
86            break;
87        gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
88        LOG_VERBOSE(Media, "Error: %d, %s", err->code,  err->message);
89
90        error = MediaPlayer::Empty;
91        if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
92            || err->code == GST_STREAM_ERROR_WRONG_TYPE
93            || err->code == GST_STREAM_ERROR_FAILED
94            || err->code == GST_CORE_ERROR_MISSING_PLUGIN
95            || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
96            error = MediaPlayer::FormatError;
97        else if (err->domain == GST_STREAM_ERROR) {
98            error = MediaPlayer::DecodeError;
99            attemptNextLocation = true;
100        } else if (err->domain == GST_RESOURCE_ERROR)
101            error = MediaPlayer::NetworkError;
102
103        if (mp) {
104            if (attemptNextLocation)
105                issueError = !mp->loadNextLocation();
106            if (issueError)
107                mp->loadingFailed(error);
108        }
109        break;
110    case GST_MESSAGE_EOS:
111        LOG_VERBOSE(Media, "End of Stream");
112        mp->didEnd();
113        break;
114    case GST_MESSAGE_STATE_CHANGED:
115        mp->updateStates();
116        break;
117    case GST_MESSAGE_BUFFERING:
118        gst_message_parse_buffering(message, &percent);
119        LOG_VERBOSE(Media, "Buffering %d", percent);
120        break;
121    case GST_MESSAGE_DURATION:
122        LOG_VERBOSE(Media, "Duration changed");
123        mp->durationChanged();
124        break;
125    default:
126        LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s",
127                    GST_MESSAGE_TYPE_NAME(message));
128        break;
129    }
130    return true;
131}
132
133void mediaPlayerPrivateSourceChangedCallback(GObject *object, GParamSpec *pspec, gpointer data)
134{
135    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
136    GOwnPtr<GstElement> element;
137
138    g_object_get(mp->m_playBin, "source", &element.outPtr(), NULL);
139    gst_object_replace((GstObject**) &mp->m_source, (GstObject*) element.get());
140
141    if (WEBKIT_IS_WEB_SRC(element.get())) {
142        Frame* frame = mp->m_player->frameView() ? mp->m_player->frameView()->frame() : 0;
143
144        if (frame)
145            webKitWebSrcSetFrame(WEBKIT_WEB_SRC(element.get()), frame);
146    }
147}
148
149void mediaPlayerPrivateVolumeChangedCallback(GObject *element, GParamSpec *pspec, gpointer data)
150{
151    // This is called when playbin receives the notify::volume signal.
152    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
153    mp->volumeChanged();
154}
155
156gboolean notifyVolumeIdleCallback(gpointer data)
157{
158    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
159    mp->volumeChangedCallback();
160    return FALSE;
161}
162
163void mediaPlayerPrivateMuteChangedCallback(GObject *element, GParamSpec *pspec, gpointer data)
164{
165    // This is called when playbin receives the notify::mute signal.
166    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
167    mp->muteChanged();
168}
169
170gboolean notifyMuteIdleCallback(gpointer data)
171{
172    MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
173    mp->muteChangedCallback();
174    return FALSE;
175}
176
177static float playbackPosition(GstElement* playbin)
178{
179
180    float ret = 0.0;
181
182    GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
183    if (!gst_element_query(playbin, query)) {
184        LOG_VERBOSE(Media, "Position query failed...");
185        gst_query_unref(query);
186        return ret;
187    }
188
189    gint64 position;
190    gst_query_parse_position(query, 0, &position);
191
192    // Position is available only if the pipeline is not in GST_STATE_NULL or
193    // GST_STATE_READY state.
194    if (position !=  static_cast<gint64>(GST_CLOCK_TIME_NONE))
195        ret = static_cast<float>(position) / static_cast<float>(GST_SECOND);
196
197    LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
198
199    gst_query_unref(query);
200
201    return ret;
202}
203
204
205void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate)
206{
207    g_return_if_fail(GST_IS_BUFFER(buffer));
208    gst_buffer_replace(&playerPrivate->m_buffer, buffer);
209    playerPrivate->repaint();
210}
211
212MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
213{
214    return new MediaPlayerPrivate(player);
215}
216
217void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
218{
219    if (isAvailable())
220        registrar(create, getSupportedTypes, supportsType);
221}
222
223static bool gstInitialized = false;
224
225static bool doGstInit()
226{
227    // FIXME: We should pass the arguments from the command line
228    if (!gstInitialized) {
229        GOwnPtr<GError> error;
230        gstInitialized = gst_init_check(0, 0, &error.outPtr());
231        if (!gstInitialized) {
232            LOG_VERBOSE(Media, "Could not initialize GStreamer: %s",
233                        error ? error->message : "unknown error occurred");
234        } else {
235            gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY,
236                                 WEBKIT_TYPE_DATA_SRC);
237            gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100,
238                                 WEBKIT_TYPE_WEB_SRC);
239        }
240
241    }
242    return gstInitialized;
243}
244
245bool MediaPlayerPrivate::isAvailable()
246{
247    if (!doGstInit())
248        return false;
249
250    GstElementFactory* factory = gst_element_factory_find("playbin2");
251    if (factory) {
252        gst_object_unref(GST_OBJECT(factory));
253        return true;
254    }
255    return false;
256}
257
258MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
259    : m_player(player)
260    , m_playBin(0)
261    , m_videoSink(0)
262    , m_fpsSink(0)
263    , m_source(0)
264    , m_seekTime(0)
265    , m_changingRate(false)
266    , m_endTime(numeric_limits<float>::infinity())
267    , m_networkState(MediaPlayer::Empty)
268    , m_readyState(MediaPlayer::HaveNothing)
269    , m_startedPlaying(false)
270    , m_isStreaming(false)
271    , m_size(IntSize())
272    , m_buffer(0)
273    , m_mediaLocations(0)
274    , m_mediaLocationCurrentIndex(0)
275    , m_resetPipeline(false)
276    , m_paused(true)
277    , m_seeking(false)
278    , m_playbackRate(1)
279    , m_errorOccured(false)
280    , m_volumeIdleId(0)
281    , m_mediaDuration(0.0)
282    , m_muteIdleId(0)
283{
284    doGstInit();
285}
286
287MediaPlayerPrivate::~MediaPlayerPrivate()
288{
289    if (m_volumeIdleId) {
290        g_source_remove(m_volumeIdleId);
291        m_volumeIdleId = 0;
292    }
293
294    if (m_muteIdleId) {
295        g_source_remove(m_muteIdleId);
296        m_muteIdleId = 0;
297    }
298
299    if (m_buffer)
300        gst_buffer_unref(m_buffer);
301    m_buffer = 0;
302
303    if (m_mediaLocations) {
304        gst_structure_free(m_mediaLocations);
305        m_mediaLocations = 0;
306    }
307
308    if (m_source) {
309        gst_object_unref(m_source);
310        m_source = 0;
311    }
312
313    if (m_playBin) {
314        gst_element_set_state(m_playBin, GST_STATE_NULL);
315        gst_object_unref(GST_OBJECT(m_playBin));
316    }
317
318    if (m_videoSink) {
319        g_object_unref(m_videoSink);
320        m_videoSink = 0;
321    }
322
323    if (m_fpsSink) {
324        g_object_unref(m_fpsSink);
325        m_fpsSink = 0;
326    }
327}
328
329void MediaPlayerPrivate::load(const String& url)
330{
331    LOG_VERBOSE(Media, "Load %s", url.utf8().data());
332    if (m_networkState != MediaPlayer::Loading) {
333        m_networkState = MediaPlayer::Loading;
334        m_player->networkStateChanged();
335    }
336    if (m_readyState != MediaPlayer::HaveNothing) {
337        m_readyState = MediaPlayer::HaveNothing;
338        m_player->readyStateChanged();
339    }
340
341    createGSTPlayBin(url);
342    pause();
343}
344
345bool MediaPlayerPrivate::changePipelineState(GstState newState)
346{
347    ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
348
349    GstState currentState;
350    GstState pending;
351
352    gst_element_get_state(m_playBin, &currentState, &pending, 0);
353    if (currentState != newState && pending != newState) {
354        GstStateChangeReturn ret = gst_element_set_state(m_playBin, newState);
355        GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
356        if (currentState != pausedOrPlaying && ret == GST_STATE_CHANGE_FAILURE) {
357            loadingFailed(MediaPlayer::Empty);
358            return false;
359        }
360    }
361    return true;
362}
363
364void MediaPlayerPrivate::play()
365{
366    if (changePipelineState(GST_STATE_PLAYING))
367        LOG_VERBOSE(Media, "Play");
368}
369
370void MediaPlayerPrivate::pause()
371{
372    if (changePipelineState(GST_STATE_PAUSED))
373        LOG_VERBOSE(Media, "Pause");
374}
375
376float MediaPlayerPrivate::duration() const
377{
378    if (!m_playBin)
379        return 0.0;
380
381    if (m_errorOccured)
382        return 0.0;
383
384    if (m_mediaDuration)
385        return m_mediaDuration;
386
387    GstFormat timeFormat = GST_FORMAT_TIME;
388    gint64 timeLength = 0;
389
390    if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) {
391        LOG_VERBOSE(Media, "Time duration query failed.");
392        return numeric_limits<float>::infinity();
393    }
394
395    LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
396
397    return (float) ((guint64) timeLength / 1000000000.0);
398    // FIXME: handle 3.14.9.5 properly
399}
400
401float MediaPlayerPrivate::currentTime() const
402{
403    if (!m_playBin)
404        return 0;
405
406    if (m_errorOccured)
407        return 0;
408
409    if (m_seeking)
410        return m_seekTime;
411
412    return playbackPosition(m_playBin);
413
414}
415
416void MediaPlayerPrivate::seek(float time)
417{
418    // Avoid useless seeking.
419    if (time == playbackPosition(m_playBin))
420        return;
421
422    if (!m_playBin)
423        return;
424
425    if (m_isStreaming)
426        return;
427
428    if (m_errorOccured)
429        return;
430
431    GstClockTime sec = (GstClockTime)(time * GST_SECOND);
432    LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(sec));
433    if (!gst_element_seek(m_playBin, m_player->rate(),
434            GST_FORMAT_TIME,
435            (GstSeekFlags)(GST_SEEK_FLAG_FLUSH),
436            GST_SEEK_TYPE_SET, sec,
437            GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
438        LOG_VERBOSE(Media, "Seek to %f failed", time);
439    else {
440        m_seeking = true;
441        m_seekTime = sec;
442    }
443}
444
445void MediaPlayerPrivate::startEndPointTimerIfNeeded()
446{
447    notImplemented();
448}
449
450void MediaPlayerPrivate::cancelSeek()
451{
452    notImplemented();
453}
454
455void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*)
456{
457    notImplemented();
458}
459
460bool MediaPlayerPrivate::paused() const
461{
462    return m_paused;
463}
464
465bool MediaPlayerPrivate::seeking() const
466{
467    return m_seeking;
468}
469
470// Returns the size of the video
471IntSize MediaPlayerPrivate::naturalSize() const
472{
473    if (!hasVideo())
474        return IntSize();
475
476    // TODO: handle possible clean aperture data. See
477    // https://bugzilla.gnome.org/show_bug.cgi?id=596571
478    // TODO: handle possible transformation matrix. See
479    // https://bugzilla.gnome.org/show_bug.cgi?id=596326
480    int width = 0, height = 0;
481    if (GstPad* pad = gst_element_get_static_pad(m_videoSink, "sink")) {
482        GstCaps* caps = GST_PAD_CAPS(pad);
483        gfloat pixelAspectRatio;
484        gint pixelAspectRatioNumerator, pixelAspectRatioDenominator;
485
486        if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps)
487            || !gst_video_format_parse_caps(caps, 0, &width, &height)
488            || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator,
489                                                        &pixelAspectRatioDenominator)) {
490            gst_object_unref(GST_OBJECT(pad));
491            return IntSize();
492        }
493
494        pixelAspectRatio = (gfloat) pixelAspectRatioNumerator / (gfloat) pixelAspectRatioDenominator;
495        width *= pixelAspectRatio;
496        height /= pixelAspectRatio;
497        gst_object_unref(GST_OBJECT(pad));
498    }
499
500    return IntSize(width, height);
501}
502
503bool MediaPlayerPrivate::hasVideo() const
504{
505    gint currentVideo = -1;
506    if (m_playBin)
507        g_object_get(m_playBin, "current-video", &currentVideo, NULL);
508    return currentVideo > -1;
509}
510
511bool MediaPlayerPrivate::hasAudio() const
512{
513    gint currentAudio = -1;
514    if (m_playBin)
515        g_object_get(m_playBin, "current-audio", &currentAudio, NULL);
516    return currentAudio > -1;
517}
518
519void MediaPlayerPrivate::setVolume(float volume)
520{
521    if (!m_playBin)
522        return;
523
524    g_object_set(m_playBin, "volume", static_cast<double>(volume), NULL);
525}
526
527void MediaPlayerPrivate::volumeChangedCallback()
528{
529    double volume;
530    g_object_get(m_playBin, "volume", &volume, NULL);
531    m_player->volumeChanged(static_cast<float>(volume));
532}
533
534void MediaPlayerPrivate::volumeChanged()
535{
536    if (m_volumeIdleId)
537        g_source_remove(m_volumeIdleId);
538    m_volumeIdleId = g_idle_add((GSourceFunc) notifyVolumeIdleCallback, this);
539}
540
541void MediaPlayerPrivate::setRate(float rate)
542{
543    // Avoid useless playback rate update.
544    if (m_playbackRate == rate)
545        return;
546
547    GstState state;
548    GstState pending;
549
550    gst_element_get_state(m_playBin, &state, &pending, 0);
551    if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
552        || (pending == GST_STATE_PAUSED))
553        return;
554
555    if (m_isStreaming)
556        return;
557
558    m_playbackRate = rate;
559    m_changingRate = true;
560    float currentPosition = playbackPosition(m_playBin) * GST_SECOND;
561    GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
562    gint64 start, end;
563    bool mute = false;
564
565    LOG_VERBOSE(Media, "Set Rate to %f", rate);
566    if (rate >= 0) {
567        // Mute the sound if the playback rate is too extreme.
568        // TODO: in other cases we should perform pitch adjustments.
569        mute = (bool) (rate < 0.8 || rate > 2);
570        start = currentPosition;
571        end = GST_CLOCK_TIME_NONE;
572    } else {
573        start = 0;
574        mute = true;
575
576        // If we are at beginning of media, start from the end to
577        // avoid immediate EOS.
578        if (currentPosition <= 0)
579            end = duration() * GST_SECOND;
580        else
581            end = currentPosition;
582    }
583
584    LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute);
585
586    if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags,
587                          GST_SEEK_TYPE_SET, start,
588                          GST_SEEK_TYPE_SET, end))
589        LOG_VERBOSE(Media, "Set rate to %f failed", rate);
590    else
591        g_object_set(m_playBin, "mute", mute, NULL);
592}
593
594MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
595{
596    return m_networkState;
597}
598
599MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
600{
601    return m_readyState;
602}
603
604PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
605{
606    RefPtr<TimeRanges> timeRanges = TimeRanges::create();
607    float loaded = maxTimeLoaded();
608    if (!m_errorOccured && !m_isStreaming && loaded > 0)
609        timeRanges->add(0, loaded);
610    return timeRanges.release();
611}
612
613float MediaPlayerPrivate::maxTimeSeekable() const
614{
615    if (m_errorOccured)
616        return 0.0;
617
618    // TODO
619    LOG_VERBOSE(Media, "maxTimeSeekable");
620    if (m_isStreaming)
621        return numeric_limits<float>::infinity();
622    // infinite duration means live stream
623    return maxTimeLoaded();
624}
625
626float MediaPlayerPrivate::maxTimeLoaded() const
627{
628    if (m_errorOccured)
629        return 0.0;
630
631    // TODO
632    LOG_VERBOSE(Media, "maxTimeLoaded");
633    notImplemented();
634    return duration();
635}
636
637unsigned MediaPlayerPrivate::bytesLoaded() const
638{
639    notImplemented();
640    LOG_VERBOSE(Media, "bytesLoaded");
641    /*if (!m_playBin)
642        return 0;
643    float dur = duration();
644    float maxTime = maxTimeLoaded();
645    if (!dur)
646        return 0;*/
647
648    return 1; // totalBytes() * maxTime / dur;
649}
650
651unsigned MediaPlayerPrivate::totalBytes() const
652{
653    LOG_VERBOSE(Media, "totalBytes");
654    if (!m_source)
655        return 0;
656
657    if (m_errorOccured)
658        return 0;
659
660    GstFormat fmt = GST_FORMAT_BYTES;
661    gint64 length = 0;
662    gst_element_query_duration(m_source, &fmt, &length);
663
664    return length;
665}
666
667void MediaPlayerPrivate::cancelLoad()
668{
669    if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
670        return;
671
672    if (m_playBin)
673        gst_element_set_state(m_playBin, GST_STATE_NULL);
674}
675
676void MediaPlayerPrivate::updateStates()
677{
678    // There is no (known) way to get such level of information about
679    // the state of GStreamer, therefore, when in PAUSED state,
680    // we are sure we can display the first frame and go to play
681
682    if (!m_playBin)
683        return;
684
685    if (m_errorOccured)
686        return;
687
688    MediaPlayer::NetworkState oldNetworkState = m_networkState;
689    MediaPlayer::ReadyState oldReadyState = m_readyState;
690    GstState state;
691    GstState pending;
692
693    GstStateChangeReturn ret = gst_element_get_state(m_playBin,
694        &state, &pending, 250 * GST_NSECOND);
695
696    bool shouldUpdateAfterSeek = false;
697    switch (ret) {
698    case GST_STATE_CHANGE_SUCCESS:
699        LOG_VERBOSE(Media, "State: %s, pending: %s",
700            gst_element_state_get_name(state),
701            gst_element_state_get_name(pending));
702
703        m_resetPipeline = state <= GST_STATE_READY;
704
705        if (state == GST_STATE_READY)
706            m_readyState = MediaPlayer::HaveNothing;
707        else if (state == GST_STATE_PAUSED)
708            m_readyState = MediaPlayer::HaveEnoughData;
709
710        if (state == GST_STATE_PLAYING) {
711            m_readyState = MediaPlayer::HaveEnoughData;
712            m_paused = false;
713            if (!m_mediaDuration) {
714                float newDuration = duration();
715                if (!isinf(newDuration))
716                    m_mediaDuration = newDuration;
717            }
718        } else
719            m_paused = true;
720
721        if (m_changingRate) {
722            m_player->rateChanged();
723            m_changingRate = false;
724        }
725
726        if (m_seeking) {
727            shouldUpdateAfterSeek = true;
728            m_seeking = false;
729        }
730
731        m_networkState = MediaPlayer::Loaded;
732        break;
733    case GST_STATE_CHANGE_ASYNC:
734        LOG_VERBOSE(Media, "Async: State: %s, pending: %s",
735            gst_element_state_get_name(state),
736            gst_element_state_get_name(pending));
737        // Change in progress
738        return;
739    case GST_STATE_CHANGE_FAILURE:
740        LOG_VERBOSE(Media, "Failure: State: %s, pending: %s",
741            gst_element_state_get_name(state),
742            gst_element_state_get_name(pending));
743        // Change failed
744        return;
745    case GST_STATE_CHANGE_NO_PREROLL:
746        LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s",
747            gst_element_state_get_name(state),
748            gst_element_state_get_name(pending));
749
750        if (state == GST_STATE_READY)
751            m_readyState = MediaPlayer::HaveNothing;
752        else if (state == GST_STATE_PAUSED)
753            m_readyState = MediaPlayer::HaveCurrentData;
754
755        m_networkState = MediaPlayer::Loading;
756        break;
757    default:
758        LOG_VERBOSE(Media, "Else : %d", ret);
759        break;
760    }
761
762    if (seeking())
763        m_readyState = MediaPlayer::HaveNothing;
764
765    if (shouldUpdateAfterSeek)
766        timeChanged();
767
768    if (m_networkState != oldNetworkState) {
769        LOG_VERBOSE(Media, "Network State Changed from %u to %u",
770            oldNetworkState, m_networkState);
771        m_player->networkStateChanged();
772    }
773    if (m_readyState != oldReadyState) {
774        LOG_VERBOSE(Media, "Ready State Changed from %u to %u",
775            oldReadyState, m_readyState);
776        m_player->readyStateChanged();
777    }
778}
779
780void MediaPlayerPrivate::mediaLocationChanged(GstMessage* message)
781{
782    if (m_mediaLocations)
783        gst_structure_free(m_mediaLocations);
784
785    if (message->structure) {
786        // This structure can contain:
787        // - both a new-location string and embedded locations structure
788        // - or only a new-location string.
789        m_mediaLocations = gst_structure_copy(message->structure);
790        const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
791
792        if (locations)
793            m_mediaLocationCurrentIndex = gst_value_list_get_size(locations) -1;
794
795        loadNextLocation();
796    }
797}
798
799bool MediaPlayerPrivate::loadNextLocation()
800{
801    if (!m_mediaLocations)
802        return false;
803
804    const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
805    const gchar* newLocation = 0;
806
807    if (!locations) {
808        // Fallback on new-location string.
809        newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
810        if (!newLocation)
811            return false;
812    }
813
814    if (!newLocation) {
815        if (m_mediaLocationCurrentIndex < 0) {
816            m_mediaLocations = 0;
817            return false;
818        }
819
820        const GValue* location = gst_value_list_get_value(locations,
821                                                          m_mediaLocationCurrentIndex);
822        const GstStructure* structure = gst_value_get_structure(location);
823
824        if (!structure) {
825            m_mediaLocationCurrentIndex--;
826            return false;
827        }
828
829        newLocation = gst_structure_get_string(structure, "new-location");
830    }
831
832    if (newLocation) {
833        // Found a candidate. new-location is not always an absolute url
834        // though. We need to take the base of the current url and
835        // append the value of new-location to it.
836
837        gchar* currentLocation = 0;
838        g_object_get(m_playBin, "uri", &currentLocation, NULL);
839
840        KURL currentUrl(KURL(), currentLocation);
841        g_free(currentLocation);
842
843        KURL newUrl;
844
845        if (gst_uri_is_valid(newLocation))
846            newUrl = KURL(KURL(), newLocation);
847        else
848            newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation);
849
850        RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl);
851        if (securityOrigin->canRequest(newUrl)) {
852            LOG_VERBOSE(Media, "New media url: %s", newUrl.string().utf8().data());
853
854            // Reset player states.
855            m_networkState = MediaPlayer::Loading;
856            m_player->networkStateChanged();
857            m_readyState = MediaPlayer::HaveNothing;
858            m_player->readyStateChanged();
859
860            // Reset pipeline state.
861            m_resetPipeline = true;
862            gst_element_set_state(m_playBin, GST_STATE_READY);
863
864            GstState state;
865            gst_element_get_state(m_playBin, &state, 0, 0);
866            if (state <= GST_STATE_READY) {
867                // Set the new uri and start playing.
868                g_object_set(m_playBin, "uri", newUrl.string().utf8().data(), NULL);
869                gst_element_set_state(m_playBin, GST_STATE_PLAYING);
870                return true;
871            }
872        }
873    }
874    m_mediaLocationCurrentIndex--;
875    return false;
876
877}
878
879void MediaPlayerPrivate::loadStateChanged()
880{
881    updateStates();
882}
883
884void MediaPlayerPrivate::sizeChanged()
885{
886    notImplemented();
887}
888
889void MediaPlayerPrivate::timeChanged()
890{
891    updateStates();
892    m_player->timeChanged();
893}
894
895void MediaPlayerPrivate::didEnd()
896{
897    // EOS was reached but in case of reverse playback the position is
898    // not always 0. So to not confuse the HTMLMediaElement we
899    // synchronize position and duration values.
900    float now = currentTime();
901    if (now > 0)
902        m_mediaDuration = now;
903    gst_element_set_state(m_playBin, GST_STATE_PAUSED);
904
905    timeChanged();
906}
907
908void MediaPlayerPrivate::durationChanged()
909{
910    // Reset cached media duration
911    m_mediaDuration = 0;
912
913    // And re-cache it if possible.
914    float newDuration = duration();
915    if (!isinf(newDuration))
916        m_mediaDuration = newDuration;
917
918    m_player->durationChanged();
919}
920
921bool MediaPlayerPrivate::supportsMuting() const
922{
923    return true;
924}
925
926void MediaPlayerPrivate::setMuted(bool muted)
927{
928    if (!m_playBin)
929        return;
930
931    g_object_set(m_playBin, "mute", muted, NULL);
932}
933
934void MediaPlayerPrivate::muteChangedCallback()
935{
936    gboolean muted;
937    g_object_get(m_playBin, "mute", &muted, NULL);
938    m_player->muteChanged(static_cast<bool>(muted));
939}
940
941void MediaPlayerPrivate::muteChanged()
942{
943    if (m_muteIdleId)
944        g_source_remove(m_muteIdleId);
945
946    m_muteIdleId = g_idle_add((GSourceFunc) notifyMuteIdleCallback, this);
947}
948
949void MediaPlayerPrivate::loadingFailed(MediaPlayer::NetworkState error)
950{
951    m_errorOccured = true;
952    if (m_networkState != error) {
953        m_networkState = error;
954        m_player->networkStateChanged();
955    }
956    if (m_readyState != MediaPlayer::HaveNothing) {
957        m_readyState = MediaPlayer::HaveNothing;
958        m_player->readyStateChanged();
959    }
960}
961
962void MediaPlayerPrivate::setSize(const IntSize& size)
963{
964    m_size = size;
965}
966
967void MediaPlayerPrivate::setVisible(bool visible)
968{
969}
970
971void MediaPlayerPrivate::repaint()
972{
973    m_player->repaint();
974}
975
976void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect)
977{
978    if (context->paintingDisabled())
979        return;
980
981    if (!m_player->visible())
982        return;
983    if (!m_buffer)
984        return;
985
986    int width = 0, height = 0;
987    GstCaps* caps = gst_buffer_get_caps(m_buffer);
988    GstVideoFormat format;
989
990    if (!gst_video_format_parse_caps(caps, &format, &width, &height)) {
991      gst_caps_unref(caps);
992      return;
993    }
994
995    cairo_format_t cairoFormat;
996    if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA)
997        cairoFormat = CAIRO_FORMAT_ARGB32;
998    else
999        cairoFormat = CAIRO_FORMAT_RGB24;
1000
1001    cairo_t* cr = context->platformContext();
1002    cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(m_buffer),
1003                                                               cairoFormat,
1004                                                               width, height,
1005                                                               4 * width);
1006
1007    cairo_save(cr);
1008
1009    // translate and scale the context to correct size
1010    cairo_translate(cr, rect.x(), rect.y());
1011    cairo_scale(cr, static_cast<double>(rect.width()) / width, static_cast<double>(rect.height()) / height);
1012
1013    // And paint it.
1014    cairo_set_source_surface(cr, src, 0, 0);
1015    cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_PAD);
1016    cairo_rectangle(cr, 0, 0, width, height);
1017    cairo_fill(cr);
1018    cairo_restore(cr);
1019
1020    cairo_surface_destroy(src);
1021    gst_caps_unref(caps);
1022}
1023
1024static HashSet<String> mimeTypeCache()
1025{
1026
1027    doGstInit();
1028
1029    static HashSet<String> cache;
1030    static bool typeListInitialized = false;
1031
1032    if (!typeListInitialized) {
1033        // Build a whitelist of mime-types known to be supported by
1034        // GStreamer.
1035        HashSet<String> handledApplicationSubtypes;
1036        handledApplicationSubtypes.add(String("ogg"));
1037        handledApplicationSubtypes.add(String("x-3gp"));
1038        handledApplicationSubtypes.add(String("vnd.rn-realmedia"));
1039        handledApplicationSubtypes.add(String("x-pn-realaudio"));
1040
1041        GList* factories = gst_type_find_factory_get_list();
1042        for (GList* iterator = factories; iterator; iterator = iterator->next) {
1043            GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data);
1044            GstCaps* caps = gst_type_find_factory_get_caps(factory);
1045
1046            if (!caps)
1047                continue;
1048
1049            for (guint structureIndex = 0; structureIndex < gst_caps_get_size(caps); structureIndex++) {
1050                GstStructure* structure = gst_caps_get_structure(caps, structureIndex);
1051                const gchar* name = gst_structure_get_name(structure);
1052                bool cached = false;
1053
1054                // These formats are supported by GStreamer, but not
1055                // correctly advertised.
1056                if (g_str_equal(name, "video/x-h264")
1057                    || g_str_equal(name, "audio/x-m4a")) {
1058                    cache.add(String("video/mp4"));
1059                    cache.add(String("audio/aac"));
1060                    cached = true;
1061                }
1062
1063                if (g_str_equal(name, "video/x-theora")) {
1064                    cache.add(String("video/ogg"));
1065                    cached = true;
1066                }
1067
1068                if (g_str_equal(name, "audio/x-vorbis")) {
1069                    cache.add(String("audio/ogg"));
1070                    cached = true;
1071                }
1072
1073                if (g_str_equal(name, "audio/x-wav")) {
1074                    cache.add(String("audio/wav"));
1075                    cached = true;
1076                }
1077
1078                if (g_str_equal(name, "audio/mpeg")) {
1079                    cache.add(String(name));
1080                    cached = true;
1081
1082                    // This is what we are handling:
1083                    // mpegversion=(int)1, layer=(int)[ 1, 3 ]
1084                    gint mpegVersion = 0;
1085                    if (gst_structure_get_int(structure, "mpegversion", &mpegVersion) && (mpegVersion == 1)) {
1086                        const GValue* layer = gst_structure_get_value(structure, "layer");
1087                        if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) {
1088                            gint minLayer = gst_value_get_int_range_min(layer);
1089                            gint maxLayer = gst_value_get_int_range_max(layer);
1090                            if (minLayer <= 1 && 1 <= maxLayer)
1091                                cache.add(String("audio/mp1"));
1092                            if (minLayer <= 2 && 2 <= maxLayer)
1093                                cache.add(String("audio/mp2"));
1094                            if (minLayer <= 3 && 3 <= maxLayer)
1095                                cache.add(String("audio/mp3"));
1096                        }
1097                    }
1098                }
1099
1100                if (!cached) {
1101                    // GStreamer plugins can be capable of supporting
1102                    // types which WebKit supports by default. In that
1103                    // case, we should not consider these types
1104                    // supportable by GStreamer.  Examples of what
1105                    // GStreamer can support but should not be added:
1106                    // text/plain, text/html, image/jpeg,
1107                    // application/xml
1108                    gchar** mimetype = g_strsplit(name, "/", 2);
1109                    if (g_str_equal(mimetype[0], "audio")
1110                        || g_str_equal(mimetype[0], "video")
1111                        || (g_str_equal(mimetype[0], "application")
1112                            && handledApplicationSubtypes.contains(String(mimetype[1]))))
1113                        cache.add(String(name));
1114
1115                    g_strfreev(mimetype);
1116                }
1117            }
1118        }
1119
1120        gst_plugin_feature_list_free(factories);
1121        typeListInitialized = true;
1122    }
1123
1124    return cache;
1125}
1126
1127void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
1128{
1129    types = mimeTypeCache();
1130}
1131
1132MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
1133{
1134    if (type.isNull() || type.isEmpty())
1135        return MediaPlayer::IsNotSupported;
1136
1137    // spec says we should not return "probably" if the codecs string is empty
1138    if (mimeTypeCache().contains(type))
1139        return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1140    return MediaPlayer::IsNotSupported;
1141}
1142
1143bool MediaPlayerPrivate::hasSingleSecurityOrigin() const
1144{
1145    return true;
1146}
1147
1148bool MediaPlayerPrivate::supportsFullscreen() const
1149{
1150    return true;
1151}
1152
1153void MediaPlayerPrivate::createGSTPlayBin(String url)
1154{
1155    ASSERT(!m_playBin);
1156    m_playBin = gst_element_factory_make("playbin2", "play");
1157
1158    GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin));
1159    gst_bus_add_signal_watch(bus);
1160    g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
1161    gst_object_unref(bus);
1162
1163    g_object_set(m_playBin, "uri", url.utf8().data(), NULL);
1164
1165    g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
1166    g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
1167    g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
1168
1169    m_videoSink = webkit_video_sink_new();
1170
1171    g_object_ref_sink(m_videoSink);
1172
1173    WTFLogChannel* channel = getChannelFromName("Media");
1174    if (channel->state == WTFLogChannelOn) {
1175        m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
1176        if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) {
1177            g_object_set(m_fpsSink, "video-sink", m_videoSink, NULL);
1178            g_object_ref_sink(m_fpsSink);
1179            g_object_set(m_playBin, "video-sink", m_fpsSink, NULL);
1180        } else {
1181            m_fpsSink = 0;
1182            g_object_set(m_playBin, "video-sink", m_videoSink, NULL);
1183            LOG(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18");
1184        }
1185    } else
1186        g_object_set(m_playBin, "video-sink", m_videoSink, NULL);
1187
1188    g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
1189}
1190
1191}
1192
1193#endif
1194