• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
58 using namespace std;
59 
60 namespace WebCore {
61 
mediaPlayerPrivateMessageCallback(GstBus * bus,GstMessage * message,gpointer data)62 gboolean 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 
mediaPlayerPrivateSourceChangedCallback(GObject * object,GParamSpec * pspec,gpointer data)133 void 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 
mediaPlayerPrivateVolumeChangedCallback(GObject * element,GParamSpec * pspec,gpointer data)149 void 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 
notifyVolumeIdleCallback(gpointer data)156 gboolean notifyVolumeIdleCallback(gpointer data)
157 {
158     MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
159     mp->volumeChangedCallback();
160     return FALSE;
161 }
162 
mediaPlayerPrivateMuteChangedCallback(GObject * element,GParamSpec * pspec,gpointer data)163 void 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 
notifyMuteIdleCallback(gpointer data)170 gboolean notifyMuteIdleCallback(gpointer data)
171 {
172     MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data);
173     mp->muteChangedCallback();
174     return FALSE;
175 }
176 
playbackPosition(GstElement * playbin)177 static 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 
mediaPlayerPrivateRepaintCallback(WebKitVideoSink *,GstBuffer * buffer,MediaPlayerPrivate * playerPrivate)205 void 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 
create(MediaPlayer * player)212 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
213 {
214     return new MediaPlayerPrivate(player);
215 }
216 
registerMediaEngine(MediaEngineRegistrar registrar)217 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
218 {
219     if (isAvailable())
220         registrar(create, getSupportedTypes, supportsType);
221 }
222 
223 static bool gstInitialized = false;
224 
doGstInit()225 static 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 
isAvailable()245 bool 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 
MediaPlayerPrivate(MediaPlayer * player)258 MediaPlayerPrivate::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 
~MediaPlayerPrivate()287 MediaPlayerPrivate::~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 
load(const String & url)329 void 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 
changePipelineState(GstState newState)345 bool 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 
play()364 void MediaPlayerPrivate::play()
365 {
366     if (changePipelineState(GST_STATE_PLAYING))
367         LOG_VERBOSE(Media, "Play");
368 }
369 
pause()370 void MediaPlayerPrivate::pause()
371 {
372     if (changePipelineState(GST_STATE_PAUSED))
373         LOG_VERBOSE(Media, "Pause");
374 }
375 
duration() const376 float 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 
currentTime() const401 float 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 
seek(float time)416 void 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 
startEndPointTimerIfNeeded()445 void MediaPlayerPrivate::startEndPointTimerIfNeeded()
446 {
447     notImplemented();
448 }
449 
cancelSeek()450 void MediaPlayerPrivate::cancelSeek()
451 {
452     notImplemented();
453 }
454 
endPointTimerFired(Timer<MediaPlayerPrivate> *)455 void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*)
456 {
457     notImplemented();
458 }
459 
paused() const460 bool MediaPlayerPrivate::paused() const
461 {
462     return m_paused;
463 }
464 
seeking() const465 bool MediaPlayerPrivate::seeking() const
466 {
467     return m_seeking;
468 }
469 
470 // Returns the size of the video
naturalSize() const471 IntSize 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 
hasVideo() const503 bool 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 
hasAudio() const511 bool 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 
setVolume(float volume)519 void 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 
volumeChangedCallback()527 void 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 
volumeChanged()534 void MediaPlayerPrivate::volumeChanged()
535 {
536     if (m_volumeIdleId)
537         g_source_remove(m_volumeIdleId);
538     m_volumeIdleId = g_idle_add((GSourceFunc) notifyVolumeIdleCallback, this);
539 }
540 
setRate(float rate)541 void 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 
networkState() const594 MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
595 {
596     return m_networkState;
597 }
598 
readyState() const599 MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
600 {
601     return m_readyState;
602 }
603 
buffered() const604 PassRefPtr<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 
maxTimeSeekable() const613 float 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 
maxTimeLoaded() const626 float 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 
bytesLoaded() const637 unsigned 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 
totalBytes() const651 unsigned 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 
cancelLoad()667 void 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 
updateStates()676 void 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 
mediaLocationChanged(GstMessage * message)780 void 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 
loadNextLocation()799 bool 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 
loadStateChanged()879 void MediaPlayerPrivate::loadStateChanged()
880 {
881     updateStates();
882 }
883 
sizeChanged()884 void MediaPlayerPrivate::sizeChanged()
885 {
886     notImplemented();
887 }
888 
timeChanged()889 void MediaPlayerPrivate::timeChanged()
890 {
891     updateStates();
892     m_player->timeChanged();
893 }
894 
didEnd()895 void 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 
durationChanged()908 void 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 
supportsMuting() const921 bool MediaPlayerPrivate::supportsMuting() const
922 {
923     return true;
924 }
925 
setMuted(bool muted)926 void MediaPlayerPrivate::setMuted(bool muted)
927 {
928     if (!m_playBin)
929         return;
930 
931     g_object_set(m_playBin, "mute", muted, NULL);
932 }
933 
muteChangedCallback()934 void 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 
muteChanged()941 void 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 
loadingFailed(MediaPlayer::NetworkState error)949 void 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 
setSize(const IntSize & size)962 void MediaPlayerPrivate::setSize(const IntSize& size)
963 {
964     m_size = size;
965 }
966 
setVisible(bool visible)967 void MediaPlayerPrivate::setVisible(bool visible)
968 {
969 }
970 
repaint()971 void MediaPlayerPrivate::repaint()
972 {
973     m_player->repaint();
974 }
975 
paint(GraphicsContext * context,const IntRect & rect)976 void 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 
mimeTypeCache()1024 static 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 
getSupportedTypes(HashSet<String> & types)1127 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
1128 {
1129     types = mimeTypeCache();
1130 }
1131 
supportsType(const String & type,const String & codecs)1132 MediaPlayer::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 
hasSingleSecurityOrigin() const1143 bool MediaPlayerPrivate::hasSingleSecurityOrigin() const
1144 {
1145     return true;
1146 }
1147 
supportsFullscreen() const1148 bool MediaPlayerPrivate::supportsFullscreen() const
1149 {
1150     return true;
1151 }
1152 
createGSTPlayBin(String url)1153 void 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