2009-11-24 Philippe Normand <pnormand@igalia.com>
[WebKit-https.git] / WebCore / platform / graphics / gtk / 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  *
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 "GraphicsContext.h"
33 #include "IntRect.h"
34 #include "KURL.h"
35 #include "MIMETypeRegistry.h"
36 #include "MediaPlayer.h"
37 #include "NotImplemented.h"
38 #include "ScrollView.h"
39 #include "TimeRanges.h"
40 #include "VideoSinkGStreamer.h"
41 #include "Widget.h"
42
43 #include <gst/gst.h>
44 #include <gst/interfaces/mixer.h>
45 #include <gst/interfaces/xoverlay.h>
46 #include <gst/video/video.h>
47 #include <limits>
48 #include <math.h>
49 #include <wtf/GOwnPtr.h>
50
51 using namespace std;
52
53 namespace WebCore {
54
55 gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data)
56 {
57     GOwnPtr<GError> err;
58     GOwnPtr<gchar> debug;
59     MediaPlayer::NetworkState error;
60     MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
61     gint percent = 0;
62
63     switch (GST_MESSAGE_TYPE(message)) {
64     case GST_MESSAGE_ERROR:
65         gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
66         LOG_VERBOSE(Media, "Error: %d, %s", err->code,  err->message);
67
68         error = MediaPlayer::Empty;
69         if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
70             || err->code == GST_STREAM_ERROR_WRONG_TYPE
71             || err->code == GST_STREAM_ERROR_FAILED
72             || err->code == GST_CORE_ERROR_MISSING_PLUGIN
73             || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
74             error = MediaPlayer::FormatError;
75         else if (err->domain == GST_STREAM_ERROR)
76             error = MediaPlayer::DecodeError;
77         else if (err->domain == GST_RESOURCE_ERROR)
78             error = MediaPlayer::NetworkError;
79
80         if (mp)
81             mp->loadingFailed(error);
82         break;
83     case GST_MESSAGE_EOS:
84         LOG_VERBOSE(Media, "End of Stream");
85         mp->didEnd();
86         break;
87     case GST_MESSAGE_STATE_CHANGED:
88         mp->updateStates();
89         break;
90     case GST_MESSAGE_BUFFERING:
91         gst_message_parse_buffering(message, &percent);
92         LOG_VERBOSE(Media, "Buffering %d", percent);
93         break;
94     default:
95         LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s",
96                     GST_MESSAGE_TYPE_NAME(message));
97         break;
98     }
99     return true;
100 }
101
102 static float playbackPosition(GstElement* playbin)
103 {
104
105     float ret = 0.0;
106
107     GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
108     if (!gst_element_query(playbin, query)) {
109         LOG_VERBOSE(Media, "Position query failed...");
110         gst_query_unref(query);
111         return ret;
112     }
113
114     gint64 position;
115     gst_query_parse_position(query, 0, &position);
116
117     // Position is available only if the pipeline is not in NULL or
118     // READY state.
119     if (position !=  static_cast<gint64>(GST_CLOCK_TIME_NONE))
120         ret = static_cast<float>(position) / static_cast<float>(GST_SECOND);
121
122     LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
123
124     gst_query_unref(query);
125
126     return ret;
127 }
128
129 void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate)
130 {
131     g_return_if_fail(GST_IS_BUFFER(buffer));
132     gst_buffer_replace(&playerPrivate->m_buffer, buffer);
133     playerPrivate->repaint();
134 }
135
136 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
137 {
138     return new MediaPlayerPrivate(player);
139 }
140
141 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
142 {
143     if (isAvailable())
144         registrar(create, getSupportedTypes, supportsType);
145 }
146
147 static bool gstInitialized = false;
148
149 static void do_gst_init()
150 {
151     // FIXME: We should pass the arguments from the command line
152     if (!gstInitialized) {
153         gst_init(0, 0);
154         gstInitialized = true;
155         gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY,
156                              WEBKIT_TYPE_DATA_SRC);
157
158     }
159 }
160
161 bool MediaPlayerPrivate::isAvailable()
162 {
163     do_gst_init();
164     GstElementFactory* factory = gst_element_factory_find("playbin2");
165     if (factory) {
166         gst_object_unref(GST_OBJECT(factory));
167         return true;
168     }
169     return false;
170 }
171
172 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
173     : m_player(player)
174     , m_playBin(0)
175     , m_videoSink(0)
176     , m_source(0)
177     , m_seekTime(0)
178     , m_changingRate(false)
179     , m_endTime(numeric_limits<float>::infinity())
180     , m_networkState(MediaPlayer::Empty)
181     , m_readyState(MediaPlayer::HaveNothing)
182     , m_startedPlaying(false)
183     , m_isStreaming(false)
184     , m_size(IntSize())
185     , m_buffer(0)
186     , m_paused(true)
187     , m_seeking(false)
188     , m_errorOccured(false)
189 {
190     do_gst_init();
191 }
192
193 MediaPlayerPrivate::~MediaPlayerPrivate()
194 {
195     if (m_buffer)
196         gst_buffer_unref(m_buffer);
197     m_buffer = 0;
198
199     if (m_playBin) {
200         gst_element_set_state(m_playBin, GST_STATE_NULL);
201         gst_object_unref(GST_OBJECT(m_playBin));
202     }
203
204     if (m_videoSink) {
205         g_object_unref(m_videoSink);
206         m_videoSink = 0;
207     }
208 }
209
210 void MediaPlayerPrivate::load(const String& url)
211 {
212     LOG_VERBOSE(Media, "Load %s", url.utf8().data());
213     if (m_networkState != MediaPlayer::Loading) {
214         m_networkState = MediaPlayer::Loading;
215         m_player->networkStateChanged();
216     }
217     if (m_readyState != MediaPlayer::HaveNothing) {
218         m_readyState = MediaPlayer::HaveNothing;
219         m_player->readyStateChanged();
220     }
221
222     createGSTPlayBin(url);
223     pause();
224 }
225
226 void MediaPlayerPrivate::play()
227 {
228     GstState state;
229     GstState pending;
230
231     gst_element_get_state(m_playBin, &state, &pending, 0);
232     if (state != GST_STATE_PLAYING && pending != GST_STATE_PLAYING) {
233         LOG_VERBOSE(Media, "Play");
234         gst_element_set_state(m_playBin, GST_STATE_PLAYING);
235     }
236 }
237
238 void MediaPlayerPrivate::pause()
239 {
240     GstState state;
241     GstState pending;
242
243     gst_element_get_state(m_playBin, &state, &pending, 0);
244     if (state != GST_STATE_PAUSED  && pending != GST_STATE_PAUSED) {
245         LOG_VERBOSE(Media, "Pause");
246         gst_element_set_state(m_playBin, GST_STATE_PAUSED);
247     }
248 }
249
250 float MediaPlayerPrivate::duration() const
251 {
252     if (!m_playBin)
253         return 0.0;
254
255     if (m_errorOccured)
256         return 0.0;
257
258     GstFormat timeFormat = GST_FORMAT_TIME;
259     gint64 timeLength = 0;
260
261     if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) {
262         LOG_VERBOSE(Media, "Time duration query failed.");
263         return numeric_limits<float>::infinity();
264     }
265
266     LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
267
268     return (float) ((guint64) timeLength / 1000000000.0);
269     // FIXME: handle 3.14.9.5 properly
270 }
271
272 float MediaPlayerPrivate::currentTime() const
273 {
274     if (!m_playBin)
275         return 0;
276
277     if (m_errorOccured)
278         return 0;
279
280     if (m_seeking)
281         return m_seekTime;
282
283     return playbackPosition(m_playBin);
284
285 }
286
287 void MediaPlayerPrivate::seek(float time)
288 {
289     GstClockTime sec = (GstClockTime)(time * GST_SECOND);
290
291     if (!m_playBin)
292         return;
293
294     if (m_isStreaming)
295         return;
296
297     if (m_errorOccured)
298         return;
299
300     LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(sec));
301     if (!gst_element_seek(m_playBin, m_player->rate(),
302             GST_FORMAT_TIME,
303             (GstSeekFlags)(GST_SEEK_FLAG_FLUSH),
304             GST_SEEK_TYPE_SET, sec,
305             GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
306         LOG_VERBOSE(Media, "Seek to %f failed", time);
307     else {
308         m_seeking = true;
309         m_seekTime = sec;
310     }
311 }
312
313 void MediaPlayerPrivate::setEndTime(float time)
314 {
315     notImplemented();
316 }
317
318 void MediaPlayerPrivate::startEndPointTimerIfNeeded()
319 {
320     notImplemented();
321 }
322
323 void MediaPlayerPrivate::cancelSeek()
324 {
325     notImplemented();
326 }
327
328 void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*)
329 {
330     notImplemented();
331 }
332
333 bool MediaPlayerPrivate::paused() const
334 {
335     return m_paused;
336 }
337
338 bool MediaPlayerPrivate::seeking() const
339 {
340     return m_seeking;
341 }
342
343 // Returns the size of the video
344 IntSize MediaPlayerPrivate::naturalSize() const
345 {
346     if (!hasVideo())
347         return IntSize();
348
349     // TODO: handle possible clean aperture data. See
350     // https://bugzilla.gnome.org/show_bug.cgi?id=596571
351     // TODO: handle possible transformation matrix. See
352     // https://bugzilla.gnome.org/show_bug.cgi?id=596326
353     int width = 0, height = 0;
354     if (GstPad* pad = gst_element_get_static_pad(m_videoSink, "sink")) {
355         GstCaps* caps = GST_PAD_CAPS(pad);
356         gfloat pixelAspectRatio;
357         gint pixelAspectRatioNumerator, pixelAspectRatioDenominator;
358
359         if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps)
360             || !gst_video_format_parse_caps(caps, 0, &width, &height)
361             || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator,
362                                                         &pixelAspectRatioDenominator)) {
363             gst_object_unref(GST_OBJECT(pad));
364             return IntSize();
365         }
366
367         pixelAspectRatio = (gfloat) pixelAspectRatioNumerator / (gfloat) pixelAspectRatioDenominator;
368         width *= pixelAspectRatio;
369         height /= pixelAspectRatio;
370         gst_object_unref(GST_OBJECT(pad));
371     }
372
373     return IntSize(width, height);
374 }
375
376 bool MediaPlayerPrivate::hasVideo() const
377 {
378     gint currentVideo = -1;
379     if (m_playBin)
380         g_object_get(G_OBJECT(m_playBin), "current-video", &currentVideo, NULL);
381     return currentVideo > -1;
382 }
383
384 bool MediaPlayerPrivate::hasAudio() const
385 {
386     gint currentAudio = -1;
387     if (m_playBin)
388         g_object_get(G_OBJECT(m_playBin), "current-audio", &currentAudio, NULL);
389     return currentAudio > -1;
390 }
391
392 void MediaPlayerPrivate::setVolume(float volume)
393 {
394     if (!m_playBin)
395         return;
396
397     g_object_set(G_OBJECT(m_playBin), "volume", static_cast<double>(volume), NULL);
398 }
399
400 void MediaPlayerPrivate::setRate(float rate)
401 {
402     GstState state;
403     GstState pending;
404
405     gst_element_get_state(m_playBin, &state, &pending, 0);
406     if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
407         || (pending == GST_STATE_PAUSED))
408         return;
409
410     if (m_isStreaming)
411         return;
412
413     m_changingRate = true;
414     float currentPosition = playbackPosition(m_playBin) * GST_SECOND;
415     GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
416     gint64 start, end;
417     bool mute = false;
418
419     LOG_VERBOSE(Media, "Set Rate to %f", rate);
420     if (rate >= 0) {
421         // Mute the sound if the playback rate is too extreme.
422         // TODO: in other cases we should perform pitch adjustments.
423         mute = (bool) (rate < 0.8 || rate > 2);
424         start = currentPosition;
425         end = GST_CLOCK_TIME_NONE;
426     } else {
427         start = 0;
428         mute = true;
429
430         // If we are at beginning of media, start from the end to
431         // avoid immediate EOS.
432         if (currentPosition <= 0)
433             end = duration() * GST_SECOND;
434         else
435             end = currentPosition;
436     }
437
438     LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute);
439
440     if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags,
441                           GST_SEEK_TYPE_SET, start,
442                           GST_SEEK_TYPE_SET, end))
443         LOG_VERBOSE(Media, "Set rate to %f failed", rate);
444     else
445         g_object_set(m_playBin, "mute", mute, NULL);
446 }
447
448 int MediaPlayerPrivate::dataRate() const
449 {
450     notImplemented();
451     return 1;
452 }
453
454 MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
455 {
456     return m_networkState;
457 }
458
459 MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
460 {
461     return m_readyState;
462 }
463
464 PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
465 {
466     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
467     float loaded = maxTimeLoaded();
468     if (!m_errorOccured && !m_isStreaming && loaded > 0)
469         timeRanges->add(0, loaded);
470     return timeRanges.release();
471 }
472
473 float MediaPlayerPrivate::maxTimeSeekable() const
474 {
475     if (m_errorOccured)
476         return 0.0;
477
478     // TODO
479     LOG_VERBOSE(Media, "maxTimeSeekable");
480     if (m_isStreaming)
481         return numeric_limits<float>::infinity();
482     // infinite duration means live stream
483     return maxTimeLoaded();
484 }
485
486 float MediaPlayerPrivate::maxTimeLoaded() const
487 {
488     if (m_errorOccured)
489         return 0.0;
490
491     // TODO
492     LOG_VERBOSE(Media, "maxTimeLoaded");
493     notImplemented();
494     return duration();
495 }
496
497 unsigned MediaPlayerPrivate::bytesLoaded() const
498 {
499     notImplemented();
500     LOG_VERBOSE(Media, "bytesLoaded");
501     /*if (!m_playBin)
502         return 0;
503     float dur = duration();
504     float maxTime = maxTimeLoaded();
505     if (!dur)
506         return 0;*/
507
508     return 1;//totalBytes() * maxTime / dur;
509 }
510
511 bool MediaPlayerPrivate::totalBytesKnown() const
512 {
513     LOG_VERBOSE(Media, "totalBytesKnown");
514     return totalBytes() > 0;
515 }
516
517 unsigned MediaPlayerPrivate::totalBytes() const
518 {
519     LOG_VERBOSE(Media, "totalBytes");
520     if (!m_source)
521         return 0;
522
523     if (m_errorOccured)
524         return 0;
525
526     GstFormat fmt = GST_FORMAT_BYTES;
527     gint64 length = 0;
528     gst_element_query_duration(m_source, &fmt, &length);
529
530     return length;
531 }
532
533 void MediaPlayerPrivate::cancelLoad()
534 {
535     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
536         return;
537
538     if (m_playBin)
539         gst_element_set_state(m_playBin, GST_STATE_NULL);
540 }
541
542 void MediaPlayerPrivate::updateStates()
543 {
544     // There is no (known) way to get such level of information about
545     // the state of GStreamer, therefore, when in PAUSED state,
546     // we are sure we can display the first frame and go to play
547
548     if (!m_playBin)
549         return;
550
551     if (m_errorOccured)
552         return;
553
554     MediaPlayer::NetworkState oldNetworkState = m_networkState;
555     MediaPlayer::ReadyState oldReadyState = m_readyState;
556     GstState state;
557     GstState pending;
558
559     GstStateChangeReturn ret = gst_element_get_state(m_playBin,
560         &state, &pending, 250 * GST_NSECOND);
561
562     bool shouldUpdateAfterSeek = false;
563     switch (ret) {
564     case GST_STATE_CHANGE_SUCCESS:
565         LOG_VERBOSE(Media, "State: %s, pending: %s",
566             gst_element_state_get_name(state),
567             gst_element_state_get_name(pending));
568
569         if (state == GST_STATE_READY)
570             m_readyState = MediaPlayer::HaveNothing;
571         else if (state == GST_STATE_PAUSED)
572             m_readyState = MediaPlayer::HaveEnoughData;
573
574         if (state == GST_STATE_PLAYING) {
575             m_readyState = MediaPlayer::HaveEnoughData;
576             m_paused = false;
577         } else
578             m_paused = true;
579
580         if (m_changingRate) {
581             m_player->rateChanged();
582             m_changingRate = false;
583         }
584
585         if (m_seeking) {
586             shouldUpdateAfterSeek = true;
587             m_seeking = false;
588         }
589
590         m_networkState = MediaPlayer::Loaded;
591
592         g_object_get(m_playBin, "source", &m_source, NULL);
593         if (!m_source)
594             LOG_VERBOSE(Media, "m_source is 0");
595         break;
596     case GST_STATE_CHANGE_ASYNC:
597         LOG_VERBOSE(Media, "Async: State: %s, pending: %s",
598             gst_element_state_get_name(state),
599             gst_element_state_get_name(pending));
600         // Change in progress
601         return;
602     case GST_STATE_CHANGE_FAILURE:
603         LOG_VERBOSE(Media, "Failure: State: %s, pending: %s",
604             gst_element_state_get_name(state),
605             gst_element_state_get_name(pending));
606         // Change failed
607         return;
608     case GST_STATE_CHANGE_NO_PREROLL:
609         LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s",
610             gst_element_state_get_name(state),
611             gst_element_state_get_name(pending));
612
613         if (state == GST_STATE_READY)
614             m_readyState = MediaPlayer::HaveNothing;
615         else if (state == GST_STATE_PAUSED)
616             m_readyState = MediaPlayer::HaveCurrentData;
617
618         m_networkState = MediaPlayer::Loading;
619         break;
620     default:
621         LOG_VERBOSE(Media, "Else : %d", ret);
622         break;
623     }
624
625     if (seeking())
626         m_readyState = MediaPlayer::HaveNothing;
627
628     if (shouldUpdateAfterSeek)
629         timeChanged();
630
631     if (m_networkState != oldNetworkState) {
632         LOG_VERBOSE(Media, "Network State Changed from %u to %u",
633             oldNetworkState, m_networkState);
634         m_player->networkStateChanged();
635     }
636     if (m_readyState != oldReadyState) {
637         LOG_VERBOSE(Media, "Ready State Changed from %u to %u",
638             oldReadyState, m_readyState);
639         m_player->readyStateChanged();
640     }
641 }
642
643 void MediaPlayerPrivate::loadStateChanged()
644 {
645     updateStates();
646 }
647
648 void MediaPlayerPrivate::sizeChanged()
649 {
650     notImplemented();
651 }
652
653 void MediaPlayerPrivate::timeChanged()
654 {
655     updateStates();
656     m_player->timeChanged();
657 }
658
659 void MediaPlayerPrivate::volumeChanged()
660 {
661     m_player->volumeChanged();
662 }
663
664 void MediaPlayerPrivate::didEnd()
665 {
666     timeChanged();
667 }
668
669 void MediaPlayerPrivate::loadingFailed(MediaPlayer::NetworkState error)
670 {
671     m_errorOccured = true;
672     if (m_networkState != error) {
673         m_networkState = error;
674         m_player->networkStateChanged();
675     }
676     if (m_readyState != MediaPlayer::HaveNothing) {
677         m_readyState = MediaPlayer::HaveNothing;
678         m_player->readyStateChanged();
679     }
680 }
681
682 void MediaPlayerPrivate::setSize(const IntSize& size)
683 {
684     m_size = size;
685 }
686
687 void MediaPlayerPrivate::setVisible(bool visible)
688 {
689 }
690
691 void MediaPlayerPrivate::repaint()
692 {
693     m_player->repaint();
694 }
695
696 void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect)
697 {
698     if (context->paintingDisabled())
699         return;
700
701     if (!m_player->visible())
702         return;
703     if (!m_buffer)
704         return;
705
706     int width = 0, height = 0;
707     GstCaps *caps = gst_buffer_get_caps(m_buffer);
708     GstVideoFormat format;
709
710     if (!gst_video_format_parse_caps(caps, &format, &width, &height)) {
711       gst_caps_unref(caps);
712       return;
713     }
714
715     cairo_format_t cairoFormat;
716     if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA)
717         cairoFormat = CAIRO_FORMAT_ARGB32;
718     else
719         cairoFormat = CAIRO_FORMAT_RGB24;
720
721     cairo_t* cr = context->platformContext();
722     cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(m_buffer),
723                                                                cairoFormat,
724                                                                width, height,
725                                                                4 * width);
726
727     cairo_save(cr);
728
729     // translate and scale the context to correct size
730     cairo_translate(cr, rect.x(), rect.y());
731     cairo_scale(cr, static_cast<double>(rect.width()) / width, static_cast<double>(rect.height()) / height);
732
733     // And paint it.
734     cairo_set_source_surface(cr, src, 0, 0);
735     cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_PAD);
736     cairo_rectangle(cr, 0, 0, width, height);
737     cairo_fill(cr);
738     cairo_restore(cr);
739
740     cairo_surface_destroy(src);
741     gst_caps_unref(caps);
742 }
743
744 static HashSet<String> mimeTypeCache()
745 {
746
747     do_gst_init();
748
749     static HashSet<String> cache;
750     static bool typeListInitialized = false;
751
752     if (!typeListInitialized) {
753         // These subtypes are already beeing supported by WebKit itself
754         HashSet<String> ignoredApplicationSubtypes;
755         ignoredApplicationSubtypes.add(String("javascript"));
756         ignoredApplicationSubtypes.add(String("ecmascript"));
757         ignoredApplicationSubtypes.add(String("x-javascript"));
758         ignoredApplicationSubtypes.add(String("xml"));
759         ignoredApplicationSubtypes.add(String("xhtml+xml"));
760         ignoredApplicationSubtypes.add(String("rss+xml"));
761         ignoredApplicationSubtypes.add(String("atom+xml"));
762         ignoredApplicationSubtypes.add(String("x-ftp-directory"));
763         ignoredApplicationSubtypes.add(String("x-java-applet"));
764         ignoredApplicationSubtypes.add(String("x-java-bean"));
765         ignoredApplicationSubtypes.add(String("x-java-vm"));
766         ignoredApplicationSubtypes.add(String("x-shockwave-flash"));
767
768         GList* factories = gst_type_find_factory_get_list();
769         for (GList* iterator = factories; iterator; iterator = iterator->next) {
770             GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data);
771             GstCaps* caps = gst_type_find_factory_get_caps(factory);
772
773             // Splitting the capability by comma and taking the first part
774             // as capability can be something like "audio/x-wavpack, framed=(boolean)false"
775             GOwnPtr<gchar> capabilityString(gst_caps_to_string(caps));
776             gchar** capability = g_strsplit(capabilityString.get(), ",", 2);
777             gchar** mimetype = g_strsplit(capability[0], "/", 2);
778
779             // GStreamer plugins can be capable of supporting types which WebKit supports
780             // by default. In that case, we should not consider these types supportable by GStreamer.
781             // Examples of what GStreamer can support but should not be added:
782             // text/plain, text/html, image/jpeg, application/xml
783             if (g_str_equal(mimetype[0], "audio")
784                 || g_str_equal(mimetype[0], "video")
785                 || (g_str_equal(mimetype[0], "application")
786                     && !ignoredApplicationSubtypes.contains(String(mimetype[1])))) {
787                 cache.add(String(capability[0]));
788
789                 // These formats are supported by GStreamer, but not correctly advertised
790                 if (g_str_equal(capability[0], "video/x-h264")
791                     || g_str_equal(capability[0], "audio/x-m4a")) {
792                     cache.add(String("video/mp4"));
793                     cache.add(String("audio/aac"));
794                 }
795
796                 if (g_str_equal(capability[0], "video/x-theora"))
797                     cache.add(String("video/ogg"));
798
799                 if (g_str_equal(capability[0], "audio/x-wav"))
800                     cache.add(String("audio/wav"));
801
802                 if (g_str_equal(capability[0], "audio/mpeg")) {
803                     // This is what we are handling: mpegversion=(int)1, layer=(int)[ 1, 3 ]
804                     gchar** versionAndLayer = g_strsplit(capability[1], ",", 2);
805
806                     if (g_str_has_suffix(versionAndLayer[0], "(int)1")) {
807                         for (int i = 0; versionAndLayer[1][i] != '\0'; i++) {
808                             if (versionAndLayer[1][i] == '1')
809                                 cache.add(String("audio/mp1"));
810                             else if (versionAndLayer[1][i] == '2')
811                                 cache.add(String("audio/mp2"));
812                             else if (versionAndLayer[1][i] == '3')
813                                 cache.add(String("audio/mp3"));
814                         }
815                     }
816
817                     g_strfreev(versionAndLayer);
818                 }
819             }
820
821             g_strfreev(capability);
822             g_strfreev(mimetype);
823         }
824
825         gst_plugin_feature_list_free(factories);
826         typeListInitialized = true;
827     }
828
829     return cache;
830 }
831
832 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
833 {
834     types = mimeTypeCache();
835 }
836
837 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
838 {
839     if (type.isNull() || type.isEmpty())
840         return MediaPlayer::IsNotSupported;
841
842     // spec says we should not return "probably" if the codecs string is empty
843     if (mimeTypeCache().contains(type))
844         return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
845     return MediaPlayer::IsNotSupported;
846 }
847
848 bool MediaPlayerPrivate::hasSingleSecurityOrigin() const
849 {
850     return true;
851 }
852
853 bool MediaPlayerPrivate::supportsFullscreen() const
854 {
855     return true;
856 }
857
858 void MediaPlayerPrivate::createGSTPlayBin(String url)
859 {
860     ASSERT(!m_playBin);
861     m_playBin = gst_element_factory_make("playbin2", "play");
862
863     GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin));
864     gst_bus_add_signal_watch(bus);
865     g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
866     gst_object_unref(bus);
867
868     g_object_set(G_OBJECT(m_playBin), "uri", url.utf8().data(),
869         "volume", static_cast<double>(m_player->volume()), NULL);
870
871     m_videoSink = webkit_video_sink_new();
872
873     g_object_ref_sink(m_videoSink);
874     g_object_set(m_playBin, "video-sink", m_videoSink, NULL);
875
876     g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
877 }
878
879 }
880
881 #endif