1 /*
2 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Collabora Ltd. All rights reserved.
4 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5 * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
6 * Copyright (C) 2009, 2010 Igalia S.L
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * aint with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "MediaPlayerPrivateGStreamer.h"
26
27 #if USE(GSTREAMER)
28
29 #include "ColorSpace.h"
30 #include "Document.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "GOwnPtrGStreamer.h"
34 #include "GStreamerGWorld.h"
35 #include "GraphicsContext.h"
36 #include "GraphicsTypes.h"
37 #include "ImageGStreamer.h"
38 #include "IntRect.h"
39 #include "KURL.h"
40 #include "MIMETypeRegistry.h"
41 #include "MediaPlayer.h"
42 #include "NotImplemented.h"
43 #include "SecurityOrigin.h"
44 #include "TimeRanges.h"
45 #include "VideoSinkGStreamer.h"
46 #include "WebKitWebSourceGStreamer.h"
47 #include <GOwnPtr.h>
48 #include <gst/gst.h>
49 #include <gst/interfaces/streamvolume.h>
50 #include <gst/video/video.h>
51 #include <limits>
52 #include <math.h>
53
54 // GstPlayFlags flags from playbin2. It is the policy of GStreamer to
55 // not publicly expose element-specific enums. That's why this
56 // GstPlayFlags enum has been copied here.
57 typedef enum {
58 GST_PLAY_FLAG_VIDEO = 0x00000001,
59 GST_PLAY_FLAG_AUDIO = 0x00000002,
60 GST_PLAY_FLAG_TEXT = 0x00000004,
61 GST_PLAY_FLAG_VIS = 0x00000008,
62 GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
63 GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
64 GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
65 GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
66 GST_PLAY_FLAG_BUFFERING = 0x000000100
67 } GstPlayFlags;
68
69 using namespace std;
70
71 namespace WebCore {
72
greatestCommonDivisor(int a,int b)73 static int greatestCommonDivisor(int a, int b)
74 {
75 while (b) {
76 int temp = a;
77 a = b;
78 b = temp % b;
79 }
80
81 return ABS(a);
82 }
83
mediaPlayerPrivateMessageCallback(GstBus *,GstMessage * message,MediaPlayerPrivateGStreamer * player)84 static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
85 {
86 return player->handleMessage(message);
87 }
88
mediaPlayerPrivateSourceChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)89 static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
90 {
91 player->sourceChanged();
92 }
93
mediaPlayerPrivateVolumeChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)94 static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
95 {
96 // This is called when playbin receives the notify::volume signal.
97 player->volumeChanged();
98 }
99
mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)100 static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
101 {
102 // This is the callback of the timeout source created in ::volumeChanged.
103 player->notifyPlayerOfVolumeChange();
104 return FALSE;
105 }
106
mediaPlayerPrivateMuteChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)107 static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
108 {
109 // This is called when playbin receives the notify::mute signal.
110 player->muteChanged();
111 }
112
mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)113 static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
114 {
115 // This is the callback of the timeout source created in ::muteChanged.
116 player->notifyPlayerOfMute();
117 return FALSE;
118 }
119
mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject *,GParamSpec *,MediaPlayerPrivateGStreamer * player)120 static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
121 {
122 player->videoChanged();
123 }
124
mediaPlayerPrivateVideoChangedCallback(GObject *,MediaPlayerPrivateGStreamer * player)125 static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
126 {
127 player->videoChanged();
128 }
129
mediaPlayerPrivateAudioChangedCallback(GObject *,MediaPlayerPrivateGStreamer * player)130 static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
131 {
132 player->audioChanged();
133 }
134
mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)135 static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
136 {
137 // This is the callback of the timeout source created in ::audioChanged.
138 player->notifyPlayerOfAudio();
139 return FALSE;
140 }
141
mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer * player)142 static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
143 {
144 // This is the callback of the timeout source created in ::videoChanged.
145 player->notifyPlayerOfVideo();
146 return FALSE;
147 }
148
mediaPlayerPrivateRepaintCallback(WebKitVideoSink *,GstBuffer * buffer,MediaPlayerPrivateGStreamer * playerPrivate)149 static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamer* playerPrivate)
150 {
151 playerPrivate->triggerRepaint(buffer);
152 }
153
create(MediaPlayer * player)154 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
155 {
156 return adoptPtr(new MediaPlayerPrivateGStreamer(player));
157 }
158
registerMediaEngine(MediaEngineRegistrar registrar)159 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
160 {
161 if (isAvailable())
162 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
163 }
164
165 static bool gstInitialized = false;
166
doGstInit()167 static bool doGstInit()
168 {
169 // FIXME: We should pass the arguments from the command line
170 if (!gstInitialized) {
171 GOwnPtr<GError> error;
172 gstInitialized = gst_init_check(0, 0, &error.outPtr());
173 if (!gstInitialized)
174 LOG_VERBOSE(Media, "Could not initialize GStreamer: %s",
175 error ? error->message : "unknown error occurred");
176 else
177 gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100,
178 WEBKIT_TYPE_WEB_SRC);
179 }
180 return gstInitialized;
181 }
182
isAvailable()183 bool MediaPlayerPrivateGStreamer::isAvailable()
184 {
185 if (!doGstInit())
186 return false;
187
188 GstElementFactory* factory = gst_element_factory_find("playbin2");
189 if (factory) {
190 gst_object_unref(GST_OBJECT(factory));
191 return true;
192 }
193 return false;
194 }
195
MediaPlayerPrivateGStreamer(MediaPlayer * player)196 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
197 : m_player(player)
198 , m_playBin(0)
199 , m_webkitVideoSink(0)
200 , m_fpsSink(0)
201 , m_source(0)
202 , m_seekTime(0)
203 , m_changingRate(false)
204 , m_endTime(numeric_limits<float>::infinity())
205 , m_networkState(MediaPlayer::Empty)
206 , m_readyState(MediaPlayer::HaveNothing)
207 , m_isStreaming(false)
208 , m_size(IntSize())
209 , m_buffer(0)
210 , m_mediaLocations(0)
211 , m_mediaLocationCurrentIndex(0)
212 , m_resetPipeline(false)
213 , m_paused(true)
214 , m_seeking(false)
215 , m_buffering(false)
216 , m_playbackRate(1)
217 , m_errorOccured(false)
218 , m_mediaDuration(0)
219 , m_startedBuffering(false)
220 , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
221 , m_maxTimeLoaded(0)
222 , m_bufferingPercentage(0)
223 , m_preload(MediaPlayer::Auto)
224 , m_delayingLoad(false)
225 , m_mediaDurationKnown(true)
226 , m_volumeTimerHandler(0)
227 , m_muteTimerHandler(0)
228 , m_hasVideo(false)
229 , m_hasAudio(false)
230 , m_audioTimerHandler(0)
231 , m_videoTimerHandler(0)
232 , m_webkitAudioSink(0)
233 {
234 if (doGstInit())
235 createGSTPlayBin();
236 }
237
~MediaPlayerPrivateGStreamer()238 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
239 {
240 if (m_fillTimer.isActive())
241 m_fillTimer.stop();
242
243 if (m_buffer)
244 gst_buffer_unref(m_buffer);
245 m_buffer = 0;
246
247 if (m_mediaLocations) {
248 gst_structure_free(m_mediaLocations);
249 m_mediaLocations = 0;
250 }
251
252 if (m_source) {
253 gst_object_unref(m_source);
254 m_source = 0;
255 }
256
257 if (m_videoSinkBin) {
258 gst_object_unref(m_videoSinkBin);
259 m_videoSinkBin = 0;
260 }
261
262 if (m_playBin) {
263 gst_element_set_state(m_playBin, GST_STATE_NULL);
264 gst_object_unref(GST_OBJECT(m_playBin));
265 m_playBin = 0;
266 }
267
268 m_player = 0;
269
270 if (m_muteTimerHandler)
271 g_source_remove(m_muteTimerHandler);
272
273 if (m_volumeTimerHandler)
274 g_source_remove(m_volumeTimerHandler);
275
276 if (m_videoTimerHandler)
277 g_source_remove(m_videoTimerHandler);
278
279 if (m_audioTimerHandler)
280 g_source_remove(m_audioTimerHandler);
281 }
282
load(const String & url)283 void MediaPlayerPrivateGStreamer::load(const String& url)
284 {
285 g_object_set(m_playBin, "uri", url.utf8().data(), NULL);
286
287 LOG_VERBOSE(Media, "Load %s", url.utf8().data());
288
289 if (m_preload == MediaPlayer::None) {
290 LOG_VERBOSE(Media, "Delaying load.");
291 m_delayingLoad = true;
292 }
293
294 // GStreamer needs to have the pipeline set to a paused state to
295 // start providing anything useful.
296 gst_element_set_state(m_playBin, GST_STATE_PAUSED);
297
298 if (!m_delayingLoad)
299 commitLoad();
300 }
301
commitLoad()302 void MediaPlayerPrivateGStreamer::commitLoad()
303 {
304 ASSERT(!m_delayingLoad);
305 LOG_VERBOSE(Media, "Committing load.");
306 updateStates();
307 }
308
playbackPosition() const309 float MediaPlayerPrivateGStreamer::playbackPosition() const
310 {
311 float ret = 0.0f;
312
313 GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
314 if (!gst_element_query(m_playBin, query)) {
315 LOG_VERBOSE(Media, "Position query failed...");
316 gst_query_unref(query);
317 return ret;
318 }
319
320 gint64 position;
321 gst_query_parse_position(query, 0, &position);
322
323 // Position is available only if the pipeline is not in GST_STATE_NULL or
324 // GST_STATE_READY state.
325 if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
326 ret = static_cast<float>(position) / static_cast<float>(GST_SECOND);
327
328 LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
329
330 gst_query_unref(query);
331
332 return ret;
333 }
334
changePipelineState(GstState newState)335 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
336 {
337 ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
338
339 GstState currentState;
340 GstState pending;
341
342 gst_element_get_state(m_playBin, ¤tState, &pending, 0);
343 if (currentState != newState && pending != newState) {
344 GstStateChangeReturn ret = gst_element_set_state(m_playBin, newState);
345 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
346 if (currentState != pausedOrPlaying && ret == GST_STATE_CHANGE_FAILURE) {
347 loadingFailed(MediaPlayer::Empty);
348 return false;
349 }
350 }
351 return true;
352 }
353
prepareToPlay()354 void MediaPlayerPrivateGStreamer::prepareToPlay()
355 {
356 if (m_delayingLoad) {
357 m_delayingLoad = false;
358 commitLoad();
359 }
360 }
361
play()362 void MediaPlayerPrivateGStreamer::play()
363 {
364 if (changePipelineState(GST_STATE_PLAYING))
365 LOG_VERBOSE(Media, "Play");
366 }
367
pause()368 void MediaPlayerPrivateGStreamer::pause()
369 {
370 if (changePipelineState(GST_STATE_PAUSED))
371 LOG_VERBOSE(Media, "Pause");
372 }
373
duration() const374 float MediaPlayerPrivateGStreamer::duration() const
375 {
376 if (!m_playBin)
377 return 0.0f;
378
379 if (m_errorOccured)
380 return 0.0f;
381
382 // Media duration query failed already, don't attempt new useless queries.
383 if (!m_mediaDurationKnown)
384 return numeric_limits<float>::infinity();
385
386 if (m_mediaDuration)
387 return m_mediaDuration;
388
389 GstFormat timeFormat = GST_FORMAT_TIME;
390 gint64 timeLength = 0;
391
392 if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) {
393 LOG_VERBOSE(Media, "Time duration query failed.");
394 return numeric_limits<float>::infinity();
395 }
396
397 LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
398
399 return (float) ((guint64) timeLength / 1000000000.0);
400 // FIXME: handle 3.14.9.5 properly
401 }
402
currentTime() const403 float MediaPlayerPrivateGStreamer::currentTime() const
404 {
405 if (!m_playBin)
406 return 0.0f;
407
408 if (m_errorOccured)
409 return 0.0f;
410
411 if (m_seeking)
412 return m_seekTime;
413
414 return playbackPosition();
415
416 }
417
seek(float time)418 void MediaPlayerPrivateGStreamer::seek(float time)
419 {
420 // Avoid useless seeking.
421 if (time == playbackPosition())
422 return;
423
424 if (!m_playBin)
425 return;
426
427 if (m_errorOccured)
428 return;
429
430 // Extract the integer part of the time (seconds) and the
431 // fractional part (microseconds). Attempt to round the
432 // microseconds so no floating point precision is lost and we can
433 // perform an accurate seek.
434 float seconds;
435 float microSeconds = modf(time, &seconds) * 1000000;
436 GTimeVal timeValue;
437 timeValue.tv_sec = static_cast<glong>(seconds);
438 timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
439
440 GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue);
441 LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime));
442
443 if (!gst_element_seek(m_playBin, m_player->rate(),
444 GST_FORMAT_TIME,
445 (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
446 GST_SEEK_TYPE_SET, clockTime,
447 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
448 LOG_VERBOSE(Media, "Seek to %f failed", time);
449 else {
450 m_seeking = true;
451 m_seekTime = time;
452 }
453 }
454
paused() const455 bool MediaPlayerPrivateGStreamer::paused() const
456 {
457 return m_paused;
458 }
459
seeking() const460 bool MediaPlayerPrivateGStreamer::seeking() const
461 {
462 return m_seeking;
463 }
464
465 // Returns the size of the video
naturalSize() const466 IntSize MediaPlayerPrivateGStreamer::naturalSize() const
467 {
468 if (!hasVideo())
469 return IntSize();
470
471 GstPad* pad = gst_element_get_static_pad(m_webkitVideoSink, "sink");
472 if (!pad)
473 return IntSize();
474
475 guint64 width = 0, height = 0;
476 GstCaps* caps = GST_PAD_CAPS(pad);
477 int pixelAspectRatioNumerator, pixelAspectRatioDenominator;
478 int displayWidth, displayHeight, displayAspectRatioGCD;
479 int originalWidth = 0, originalHeight = 0;
480
481 // TODO: handle possible clean aperture data. See
482 // https://bugzilla.gnome.org/show_bug.cgi?id=596571
483 // TODO: handle possible transformation matrix. See
484 // https://bugzilla.gnome.org/show_bug.cgi?id=596326
485
486 // Get the video PAR and original size.
487 if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps)
488 || !gst_video_format_parse_caps(caps, 0, &originalWidth, &originalHeight)
489 || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator,
490 &pixelAspectRatioDenominator)) {
491 gst_object_unref(GST_OBJECT(pad));
492 // The video-sink has likely not yet negotiated its caps.
493 return IntSize();
494 }
495
496 gst_object_unref(GST_OBJECT(pad));
497
498 LOG_VERBOSE(Media, "Original video size: %dx%d", originalWidth, originalHeight);
499 LOG_VERBOSE(Media, "Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
500
501 // Calculate DAR based on PAR and video size.
502 displayWidth = originalWidth * pixelAspectRatioNumerator;
503 displayHeight = originalHeight * pixelAspectRatioDenominator;
504
505 // Divide display width and height by their GCD to avoid possible overflows.
506 displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight);
507 displayWidth /= displayAspectRatioGCD;
508 displayHeight /= displayAspectRatioGCD;
509
510 // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
511 if (!(originalHeight % displayHeight)) {
512 LOG_VERBOSE(Media, "Keeping video original height");
513 width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight);
514 height = static_cast<guint64>(originalHeight);
515 } else if (!(originalWidth % displayWidth)) {
516 LOG_VERBOSE(Media, "Keeping video original width");
517 height = gst_util_uint64_scale_int(originalWidth, displayHeight, displayWidth);
518 width = static_cast<guint64>(originalWidth);
519 } else {
520 LOG_VERBOSE(Media, "Approximating while keeping original video height");
521 width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight);
522 height = static_cast<guint64>(originalHeight);
523 }
524
525 LOG_VERBOSE(Media, "Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
526 return IntSize(static_cast<int>(width), static_cast<int>(height));
527 }
528
videoChanged()529 void MediaPlayerPrivateGStreamer::videoChanged()
530 {
531 if (m_videoTimerHandler)
532 g_source_remove(m_videoTimerHandler);
533 m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this);
534 }
535
notifyPlayerOfVideo()536 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
537 {
538 m_videoTimerHandler = 0;
539
540 gint videoTracks = 0;
541 if (m_playBin)
542 g_object_get(m_playBin, "n-video", &videoTracks, NULL);
543
544 m_hasVideo = videoTracks > 0;
545 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
546 }
547
audioChanged()548 void MediaPlayerPrivateGStreamer::audioChanged()
549 {
550 if (m_audioTimerHandler)
551 g_source_remove(m_audioTimerHandler);
552 m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this);
553 }
554
notifyPlayerOfAudio()555 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
556 {
557 m_audioTimerHandler = 0;
558
559 gint audioTracks = 0;
560 if (m_playBin)
561 g_object_get(m_playBin, "n-audio", &audioTracks, NULL);
562 m_hasAudio = audioTracks > 0;
563 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
564 }
565
setVolume(float volume)566 void MediaPlayerPrivateGStreamer::setVolume(float volume)
567 {
568 if (!m_playBin)
569 return;
570
571 gst_stream_volume_set_volume(GST_STREAM_VOLUME(m_playBin), GST_STREAM_VOLUME_FORMAT_CUBIC,
572 static_cast<double>(volume));
573 }
574
notifyPlayerOfVolumeChange()575 void MediaPlayerPrivateGStreamer::notifyPlayerOfVolumeChange()
576 {
577 m_volumeTimerHandler = 0;
578
579 if (!m_player || !m_playBin)
580 return;
581 double volume;
582 volume = gst_stream_volume_get_volume(GST_STREAM_VOLUME(m_playBin), GST_STREAM_VOLUME_FORMAT_CUBIC);
583 // get_volume() can return values superior to 1.0 if the user
584 // applies software user gain via third party application (GNOME
585 // volume control for instance).
586 volume = CLAMP(volume, 0.0, 1.0);
587 m_player->volumeChanged(static_cast<float>(volume));
588 }
589
volumeChanged()590 void MediaPlayerPrivateGStreamer::volumeChanged()
591 {
592 if (m_volumeTimerHandler)
593 g_source_remove(m_volumeTimerHandler);
594 m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this);
595 }
596
setRate(float rate)597 void MediaPlayerPrivateGStreamer::setRate(float rate)
598 {
599 // Avoid useless playback rate update.
600 if (m_playbackRate == rate)
601 return;
602
603 GstState state;
604 GstState pending;
605
606 gst_element_get_state(m_playBin, &state, &pending, 0);
607 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
608 || (pending == GST_STATE_PAUSED))
609 return;
610
611 if (m_isStreaming)
612 return;
613
614 m_playbackRate = rate;
615 m_changingRate = true;
616
617 if (!rate) {
618 gst_element_set_state(m_playBin, GST_STATE_PAUSED);
619 return;
620 }
621
622 float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
623 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
624 gint64 start, end;
625 bool mute = false;
626
627 LOG_VERBOSE(Media, "Set Rate to %f", rate);
628 if (rate > 0) {
629 // Mute the sound if the playback rate is too extreme.
630 // TODO: in other cases we should perform pitch adjustments.
631 mute = (bool) (rate < 0.8 || rate > 2);
632 start = currentPosition;
633 end = GST_CLOCK_TIME_NONE;
634 } else {
635 start = 0;
636 mute = true;
637
638 // If we are at beginning of media, start from the end to
639 // avoid immediate EOS.
640 if (currentPosition <= 0)
641 end = static_cast<gint64>(duration() * GST_SECOND);
642 else
643 end = currentPosition;
644 }
645
646 LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute);
647
648 if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags,
649 GST_SEEK_TYPE_SET, start,
650 GST_SEEK_TYPE_SET, end))
651 LOG_VERBOSE(Media, "Set rate to %f failed", rate);
652 else
653 g_object_set(m_playBin, "mute", mute, NULL);
654 }
655
networkState() const656 MediaPlayer::NetworkState MediaPlayerPrivateGStreamer::networkState() const
657 {
658 return m_networkState;
659 }
660
readyState() const661 MediaPlayer::ReadyState MediaPlayerPrivateGStreamer::readyState() const
662 {
663 return m_readyState;
664 }
665
buffered() const666 PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
667 {
668 RefPtr<TimeRanges> timeRanges = TimeRanges::create();
669 if (m_errorOccured || m_isStreaming)
670 return timeRanges.release();
671
672 #if GST_CHECK_VERSION(0, 10, 31)
673 float mediaDuration(duration());
674 if (!mediaDuration || isinf(mediaDuration))
675 return timeRanges.release();
676
677 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
678
679 if (!gst_element_query(m_playBin, query)) {
680 gst_query_unref(query);
681 return timeRanges.release();
682 }
683
684 gint64 rangeStart = 0, rangeStop = 0;
685 for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
686 if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
687 timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / 100),
688 static_cast<float>((rangeStop * mediaDuration) / 100));
689 }
690
691 // Fallback to the more general maxTimeLoaded() if no range has
692 // been found.
693 if (!timeRanges->length())
694 if (float loaded = maxTimeLoaded())
695 timeRanges->add(0, loaded);
696
697 gst_query_unref(query);
698 #else
699 float loaded = maxTimeLoaded();
700 if (!m_errorOccured && !m_isStreaming && loaded > 0)
701 timeRanges->add(0, loaded);
702 #endif
703 return timeRanges.release();
704 }
705
handleMessage(GstMessage * message)706 gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
707 {
708 GOwnPtr<GError> err;
709 GOwnPtr<gchar> debug;
710 MediaPlayer::NetworkState error;
711 bool issueError = true;
712 bool attemptNextLocation = false;
713
714 if (message->structure) {
715 const gchar* messageTypeName = gst_structure_get_name(message->structure);
716
717 // Redirect messages are sent from elements, like qtdemux, to
718 // notify of the new location(s) of the media.
719 if (!g_strcmp0(messageTypeName, "redirect")) {
720 mediaLocationChanged(message);
721 return TRUE;
722 }
723 }
724
725 switch (GST_MESSAGE_TYPE(message)) {
726 case GST_MESSAGE_ERROR:
727 if (m_resetPipeline)
728 break;
729 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
730 LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message);
731
732 error = MediaPlayer::Empty;
733 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
734 || err->code == GST_STREAM_ERROR_WRONG_TYPE
735 || err->code == GST_STREAM_ERROR_FAILED
736 || err->code == GST_CORE_ERROR_MISSING_PLUGIN
737 || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
738 error = MediaPlayer::FormatError;
739 else if (err->domain == GST_STREAM_ERROR) {
740 // Let the mediaPlayerClient handle the stream error, in
741 // this case the HTMLMediaElement will emit a stalled
742 // event.
743 if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
744 LOG_VERBOSE(Media, "Decode error, let the Media element emit a stalled event.");
745 break;
746 }
747 error = MediaPlayer::DecodeError;
748 attemptNextLocation = true;
749 } else if (err->domain == GST_RESOURCE_ERROR)
750 error = MediaPlayer::NetworkError;
751
752 if (attemptNextLocation)
753 issueError = !loadNextLocation();
754 if (issueError)
755 loadingFailed(error);
756 break;
757 case GST_MESSAGE_EOS:
758 LOG_VERBOSE(Media, "End of Stream");
759 didEnd();
760 break;
761 case GST_MESSAGE_STATE_CHANGED:
762 // Ignore state changes if load is delayed (preload=none). The
763 // player state will be updated once commitLoad() is called.
764 if (m_delayingLoad) {
765 LOG_VERBOSE(Media, "Media load has been delayed. Ignoring state changes for now");
766 break;
767 }
768
769 // Ignore state changes from internal elements. They are
770 // forwarded to playbin2 anyway.
771 if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin))
772 updateStates();
773 break;
774 case GST_MESSAGE_BUFFERING:
775 processBufferingStats(message);
776 break;
777 case GST_MESSAGE_DURATION:
778 LOG_VERBOSE(Media, "Duration changed");
779 durationChanged();
780 break;
781 default:
782 LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s",
783 GST_MESSAGE_TYPE_NAME(message));
784 break;
785 }
786 return TRUE;
787 }
788
processBufferingStats(GstMessage * message)789 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
790 {
791 // This is the immediate buffering that needs to happen so we have
792 // enough to play right now.
793 m_buffering = true;
794 const GstStructure *structure = gst_message_get_structure(message);
795 gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
796
797 LOG_VERBOSE(Media, "[Buffering] Buffering: %d%%.", m_bufferingPercentage);
798
799 GstBufferingMode mode;
800 gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
801 if (mode != GST_BUFFERING_DOWNLOAD) {
802 updateStates();
803 return;
804 }
805
806 // This is on-disk buffering, that allows us to download much more
807 // than needed for right now.
808 if (!m_startedBuffering) {
809 LOG_VERBOSE(Media, "[Buffering] Starting on-disk buffering.");
810
811 m_startedBuffering = true;
812
813 if (m_fillTimer.isActive())
814 m_fillTimer.stop();
815
816 m_fillTimer.startRepeating(0.2);
817 }
818 }
819
fillTimerFired(Timer<MediaPlayerPrivateGStreamer> *)820 void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
821 {
822 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
823
824 if (!gst_element_query(m_playBin, query)) {
825 gst_query_unref(query);
826 return;
827 }
828
829 gint64 start, stop;
830 gdouble fillStatus = 100.0;
831
832 gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
833 gst_query_unref(query);
834
835 if (stop != -1)
836 fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
837
838 LOG_VERBOSE(Media, "[Buffering] Download buffer filled up to %f%%", fillStatus);
839
840 if (!m_mediaDuration)
841 durationChanged();
842
843 // Update maxTimeLoaded only if the media duration is
844 // available. Otherwise we can't compute it.
845 if (m_mediaDuration) {
846 if (fillStatus == 100.0)
847 m_maxTimeLoaded = m_mediaDuration;
848 else
849 m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
850 LOG_VERBOSE(Media, "[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
851 }
852
853 if (fillStatus != 100.0) {
854 updateStates();
855 return;
856 }
857
858 // Media is now fully loaded. It will play even if network
859 // connection is cut. Buffering is done, remove the fill source
860 // from the main loop.
861 m_fillTimer.stop();
862 m_startedBuffering = false;
863 updateStates();
864 }
865
maxTimeSeekable() const866 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
867 {
868 if (m_errorOccured)
869 return 0.0f;
870
871 LOG_VERBOSE(Media, "maxTimeSeekable");
872 // infinite duration means live stream
873 if (isinf(duration()))
874 return 0.0f;
875
876 return duration();
877 }
878
maxTimeLoaded() const879 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
880 {
881 if (m_errorOccured)
882 return 0.0f;
883
884 float loaded = m_maxTimeLoaded;
885 if (!loaded && !m_fillTimer.isActive())
886 loaded = duration();
887 LOG_VERBOSE(Media, "maxTimeLoaded: %f", loaded);
888 return loaded;
889 }
890
bytesLoaded() const891 unsigned MediaPlayerPrivateGStreamer::bytesLoaded() const
892 {
893 if (!m_playBin)
894 return 0;
895
896 if (!m_mediaDuration)
897 return 0;
898
899 unsigned loaded = totalBytes() * maxTimeLoaded() / m_mediaDuration;
900 LOG_VERBOSE(Media, "bytesLoaded: %d", loaded);
901 return loaded;
902 }
903
totalBytes() const904 unsigned MediaPlayerPrivateGStreamer::totalBytes() const
905 {
906 if (!m_source)
907 return 0;
908
909 if (m_errorOccured)
910 return 0;
911
912 GstFormat fmt = GST_FORMAT_BYTES;
913 gint64 length = 0;
914 if (gst_element_query_duration(m_source, &fmt, &length)) {
915 LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length);
916 return static_cast<unsigned>(length);
917 }
918
919 // Fall back to querying the source pads manually.
920 // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
921 GstIterator* iter = gst_element_iterate_src_pads(m_source);
922 bool done = false;
923 while (!done) {
924 gpointer data;
925
926 switch (gst_iterator_next(iter, &data)) {
927 case GST_ITERATOR_OK: {
928 GstPad* pad = GST_PAD_CAST(data);
929 gint64 padLength = 0;
930 if (gst_pad_query_duration(pad, &fmt, &padLength)
931 && padLength > length)
932 length = padLength;
933 gst_object_unref(pad);
934 break;
935 }
936 case GST_ITERATOR_RESYNC:
937 gst_iterator_resync(iter);
938 break;
939 case GST_ITERATOR_ERROR:
940 // Fall through.
941 case GST_ITERATOR_DONE:
942 done = true;
943 break;
944 }
945 }
946 gst_iterator_free(iter);
947
948 LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length);
949
950 return static_cast<unsigned>(length);
951 }
952
decodedFrameCount() const953 unsigned MediaPlayerPrivateGStreamer::decodedFrameCount() const
954 {
955 guint64 decodedFrames = 0;
956 if (m_fpsSink)
957 g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL);
958 return static_cast<unsigned>(decodedFrames);
959 }
960
droppedFrameCount() const961 unsigned MediaPlayerPrivateGStreamer::droppedFrameCount() const
962 {
963 guint64 framesDropped = 0;
964 if (m_fpsSink)
965 g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL);
966 return static_cast<unsigned>(framesDropped);
967 }
968
audioDecodedByteCount() const969 unsigned MediaPlayerPrivateGStreamer::audioDecodedByteCount() const
970 {
971 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
972 gint64 position = 0;
973
974 if (m_webkitAudioSink && gst_element_query(m_webkitAudioSink, query))
975 gst_query_parse_position(query, 0, &position);
976
977 gst_query_unref(query);
978 return static_cast<unsigned>(position);
979 }
980
videoDecodedByteCount() const981 unsigned MediaPlayerPrivateGStreamer::videoDecodedByteCount() const
982 {
983 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
984 gint64 position = 0;
985
986 if (gst_element_query(m_webkitVideoSink, query))
987 gst_query_parse_position(query, 0, &position);
988
989 gst_query_unref(query);
990 return static_cast<unsigned>(position);
991 }
992
updateAudioSink()993 void MediaPlayerPrivateGStreamer::updateAudioSink()
994 {
995 if (!m_playBin)
996 return;
997
998 GOwnPtr<GstElement> element;
999
1000 g_object_get(m_playBin, "audio-sink", &element.outPtr(), NULL);
1001 gst_object_replace(reinterpret_cast<GstObject**>(&m_webkitAudioSink),
1002 reinterpret_cast<GstObject*>(element.get()));
1003 }
1004
1005
sourceChanged()1006 void MediaPlayerPrivateGStreamer::sourceChanged()
1007 {
1008 GOwnPtr<GstElement> element;
1009
1010 g_object_get(m_playBin, "source", &element.outPtr(), NULL);
1011 gst_object_replace(reinterpret_cast<GstObject**>(&m_source),
1012 reinterpret_cast<GstObject*>(element.get()));
1013
1014 if (WEBKIT_IS_WEB_SRC(element.get())) {
1015 Frame* frame = 0;
1016 Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
1017 if (document)
1018 frame = document->frame();
1019
1020 if (frame)
1021 webKitWebSrcSetFrame(WEBKIT_WEB_SRC(element.get()), frame);
1022 }
1023 }
1024
cancelLoad()1025 void MediaPlayerPrivateGStreamer::cancelLoad()
1026 {
1027 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1028 return;
1029
1030 if (m_playBin)
1031 gst_element_set_state(m_playBin, GST_STATE_NULL);
1032 }
1033
updateStates()1034 void MediaPlayerPrivateGStreamer::updateStates()
1035 {
1036 if (!m_playBin)
1037 return;
1038
1039 if (m_errorOccured)
1040 return;
1041
1042 MediaPlayer::NetworkState oldNetworkState = m_networkState;
1043 MediaPlayer::ReadyState oldReadyState = m_readyState;
1044 GstState state;
1045 GstState pending;
1046
1047 GstStateChangeReturn ret = gst_element_get_state(m_playBin,
1048 &state, &pending, 250 * GST_NSECOND);
1049
1050 bool shouldUpdateAfterSeek = false;
1051 switch (ret) {
1052 case GST_STATE_CHANGE_SUCCESS:
1053 LOG_VERBOSE(Media, "State: %s, pending: %s",
1054 gst_element_state_get_name(state),
1055 gst_element_state_get_name(pending));
1056
1057 m_resetPipeline = state <= GST_STATE_READY;
1058
1059 // Try to figure out ready and network states.
1060 if (state == GST_STATE_READY) {
1061 m_readyState = MediaPlayer::HaveMetadata;
1062 m_networkState = MediaPlayer::Empty;
1063 // Cache the duration without emiting the durationchange
1064 // event because it's taken care of by the media element
1065 // in this precise case.
1066 cacheDuration();
1067 } else if (maxTimeLoaded() == duration()) {
1068 m_networkState = MediaPlayer::Loaded;
1069 m_readyState = MediaPlayer::HaveEnoughData;
1070 } else {
1071 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
1072 m_networkState = MediaPlayer::Loading;
1073 }
1074
1075 if (m_buffering && state != GST_STATE_READY) {
1076 m_readyState = MediaPlayer::HaveCurrentData;
1077 m_networkState = MediaPlayer::Loading;
1078 }
1079
1080 // Now let's try to get the states in more detail using
1081 // information from GStreamer, while we sync states where
1082 // needed.
1083 if (state == GST_STATE_PAUSED) {
1084 if (!m_webkitAudioSink)
1085 updateAudioSink();
1086 if (m_buffering && m_bufferingPercentage == 100) {
1087 m_buffering = false;
1088 m_bufferingPercentage = 0;
1089 m_readyState = MediaPlayer::HaveEnoughData;
1090
1091 LOG_VERBOSE(Media, "[Buffering] Complete.");
1092
1093 if (!m_paused) {
1094 LOG_VERBOSE(Media, "[Buffering] Restarting playback.");
1095 gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1096 }
1097 } else if (!m_buffering && (currentTime() < duration())) {
1098 m_paused = true;
1099 }
1100 } else if (state == GST_STATE_PLAYING) {
1101 m_readyState = MediaPlayer::HaveEnoughData;
1102 m_paused = false;
1103
1104 if (m_buffering) {
1105 m_readyState = MediaPlayer::HaveCurrentData;
1106 m_networkState = MediaPlayer::Loading;
1107
1108 LOG_VERBOSE(Media, "[Buffering] Pausing stream for buffering.");
1109
1110 gst_element_set_state(m_playBin, GST_STATE_PAUSED);
1111 }
1112 } else
1113 m_paused = true;
1114
1115 // Is on-disk buffering in progress?
1116 if (m_fillTimer.isActive())
1117 m_networkState = MediaPlayer::Loading;
1118
1119 if (m_changingRate) {
1120 m_player->rateChanged();
1121 m_changingRate = false;
1122 }
1123
1124 if (m_seeking) {
1125 shouldUpdateAfterSeek = true;
1126 m_seeking = false;
1127 }
1128
1129 break;
1130 case GST_STATE_CHANGE_ASYNC:
1131 LOG_VERBOSE(Media, "Async: State: %s, pending: %s",
1132 gst_element_state_get_name(state),
1133 gst_element_state_get_name(pending));
1134 // Change in progress
1135
1136 if (!m_isStreaming && !m_buffering)
1137 return;
1138
1139 if (m_seeking) {
1140 shouldUpdateAfterSeek = true;
1141 m_seeking = false;
1142 }
1143 break;
1144 case GST_STATE_CHANGE_FAILURE:
1145 LOG_VERBOSE(Media, "Failure: State: %s, pending: %s",
1146 gst_element_state_get_name(state),
1147 gst_element_state_get_name(pending));
1148 // Change failed
1149 return;
1150 case GST_STATE_CHANGE_NO_PREROLL:
1151 LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s",
1152 gst_element_state_get_name(state),
1153 gst_element_state_get_name(pending));
1154
1155 if (state == GST_STATE_READY)
1156 m_readyState = MediaPlayer::HaveNothing;
1157 else if (state == GST_STATE_PAUSED) {
1158 m_readyState = MediaPlayer::HaveEnoughData;
1159 m_paused = true;
1160 // Live pipelines go in PAUSED without prerolling.
1161 m_isStreaming = true;
1162 } else if (state == GST_STATE_PLAYING)
1163 m_paused = false;
1164
1165 if (m_seeking) {
1166 shouldUpdateAfterSeek = true;
1167 m_seeking = false;
1168 if (!m_paused)
1169 gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1170 } else if (!m_paused)
1171 gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1172
1173 m_networkState = MediaPlayer::Loading;
1174 break;
1175 default:
1176 LOG_VERBOSE(Media, "Else : %d", ret);
1177 break;
1178 }
1179
1180 if (seeking())
1181 m_readyState = MediaPlayer::HaveNothing;
1182
1183 if (shouldUpdateAfterSeek)
1184 timeChanged();
1185
1186 if (m_networkState != oldNetworkState) {
1187 LOG_VERBOSE(Media, "Network State Changed from %u to %u",
1188 oldNetworkState, m_networkState);
1189 m_player->networkStateChanged();
1190 }
1191 if (m_readyState != oldReadyState) {
1192 LOG_VERBOSE(Media, "Ready State Changed from %u to %u",
1193 oldReadyState, m_readyState);
1194 m_player->readyStateChanged();
1195 }
1196 }
1197
mediaLocationChanged(GstMessage * message)1198 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1199 {
1200 if (m_mediaLocations)
1201 gst_structure_free(m_mediaLocations);
1202
1203 if (message->structure) {
1204 // This structure can contain:
1205 // - both a new-location string and embedded locations structure
1206 // - or only a new-location string.
1207 m_mediaLocations = gst_structure_copy(message->structure);
1208 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1209
1210 if (locations)
1211 m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1212
1213 loadNextLocation();
1214 }
1215 }
1216
loadNextLocation()1217 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1218 {
1219 if (!m_mediaLocations)
1220 return false;
1221
1222 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1223 const gchar* newLocation = 0;
1224
1225 if (!locations) {
1226 // Fallback on new-location string.
1227 newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1228 if (!newLocation)
1229 return false;
1230 }
1231
1232 if (!newLocation) {
1233 if (m_mediaLocationCurrentIndex < 0) {
1234 m_mediaLocations = 0;
1235 return false;
1236 }
1237
1238 const GValue* location = gst_value_list_get_value(locations,
1239 m_mediaLocationCurrentIndex);
1240 const GstStructure* structure = gst_value_get_structure(location);
1241
1242 if (!structure) {
1243 m_mediaLocationCurrentIndex--;
1244 return false;
1245 }
1246
1247 newLocation = gst_structure_get_string(structure, "new-location");
1248 }
1249
1250 if (newLocation) {
1251 // Found a candidate. new-location is not always an absolute url
1252 // though. We need to take the base of the current url and
1253 // append the value of new-location to it.
1254
1255 gchar* currentLocation = 0;
1256 g_object_get(m_playBin, "uri", ¤tLocation, NULL);
1257
1258 KURL currentUrl(KURL(), currentLocation);
1259 g_free(currentLocation);
1260
1261 KURL newUrl;
1262
1263 if (gst_uri_is_valid(newLocation))
1264 newUrl = KURL(KURL(), newLocation);
1265 else
1266 newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation);
1267
1268 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl);
1269 if (securityOrigin->canRequest(newUrl)) {
1270 LOG_VERBOSE(Media, "New media url: %s", newUrl.string().utf8().data());
1271
1272 // Reset player states.
1273 m_networkState = MediaPlayer::Loading;
1274 m_player->networkStateChanged();
1275 m_readyState = MediaPlayer::HaveNothing;
1276 m_player->readyStateChanged();
1277
1278 // Reset pipeline state.
1279 m_resetPipeline = true;
1280 gst_element_set_state(m_playBin, GST_STATE_READY);
1281
1282 GstState state;
1283 gst_element_get_state(m_playBin, &state, 0, 0);
1284 if (state <= GST_STATE_READY) {
1285 // Set the new uri and start playing.
1286 g_object_set(m_playBin, "uri", newUrl.string().utf8().data(), NULL);
1287 gst_element_set_state(m_playBin, GST_STATE_PLAYING);
1288 return true;
1289 }
1290 }
1291 }
1292 m_mediaLocationCurrentIndex--;
1293 return false;
1294
1295 }
1296
loadStateChanged()1297 void MediaPlayerPrivateGStreamer::loadStateChanged()
1298 {
1299 updateStates();
1300 }
1301
sizeChanged()1302 void MediaPlayerPrivateGStreamer::sizeChanged()
1303 {
1304 notImplemented();
1305 }
1306
timeChanged()1307 void MediaPlayerPrivateGStreamer::timeChanged()
1308 {
1309 updateStates();
1310 m_player->timeChanged();
1311 }
1312
didEnd()1313 void MediaPlayerPrivateGStreamer::didEnd()
1314 {
1315 // EOS was reached but in case of reverse playback the position is
1316 // not always 0. So to not confuse the HTMLMediaElement we
1317 // synchronize position and duration values.
1318 float now = currentTime();
1319 if (now > 0) {
1320 m_mediaDuration = now;
1321 m_mediaDurationKnown = true;
1322 m_player->durationChanged();
1323 }
1324
1325 gst_element_set_state(m_playBin, GST_STATE_PAUSED);
1326
1327 timeChanged();
1328 }
1329
cacheDuration()1330 void MediaPlayerPrivateGStreamer::cacheDuration()
1331 {
1332 // Reset cached media duration
1333 m_mediaDuration = 0;
1334
1335 // And re-cache it if possible.
1336 GstState state;
1337 gst_element_get_state(m_playBin, &state, 0, 0);
1338 float newDuration = duration();
1339
1340 if (state <= GST_STATE_READY) {
1341 // Don't set m_mediaDurationKnown yet if the pipeline is not
1342 // paused. This allows duration() query to fail at least once
1343 // before playback starts and duration becomes known.
1344 if (!isinf(newDuration))
1345 m_mediaDuration = newDuration;
1346 } else {
1347 m_mediaDurationKnown = !isinf(newDuration);
1348 if (m_mediaDurationKnown)
1349 m_mediaDuration = newDuration;
1350 }
1351
1352 if (!isinf(newDuration))
1353 m_mediaDuration = newDuration;
1354 }
1355
durationChanged()1356 void MediaPlayerPrivateGStreamer::durationChanged()
1357 {
1358 float previousDuration = m_mediaDuration;
1359
1360 cacheDuration();
1361 // Avoid emiting durationchanged in the case where the previous
1362 // duration was 0 because that case is already handled by the
1363 // HTMLMediaElement.
1364 if (previousDuration && m_mediaDuration != previousDuration)
1365 m_player->durationChanged();
1366 }
1367
supportsMuting() const1368 bool MediaPlayerPrivateGStreamer::supportsMuting() const
1369 {
1370 return true;
1371 }
1372
setMuted(bool muted)1373 void MediaPlayerPrivateGStreamer::setMuted(bool muted)
1374 {
1375 if (!m_playBin)
1376 return;
1377
1378 g_object_set(m_playBin, "mute", muted, NULL);
1379 }
1380
notifyPlayerOfMute()1381 void MediaPlayerPrivateGStreamer::notifyPlayerOfMute()
1382 {
1383 m_muteTimerHandler = 0;
1384
1385 if (!m_player || !m_playBin)
1386 return;
1387
1388 gboolean muted;
1389 g_object_get(m_playBin, "mute", &muted, NULL);
1390 m_player->muteChanged(static_cast<bool>(muted));
1391 }
1392
muteChanged()1393 void MediaPlayerPrivateGStreamer::muteChanged()
1394 {
1395 if (m_muteTimerHandler)
1396 g_source_remove(m_muteTimerHandler);
1397 m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this);
1398 }
1399
loadingFailed(MediaPlayer::NetworkState error)1400 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1401 {
1402 m_errorOccured = true;
1403 if (m_networkState != error) {
1404 m_networkState = error;
1405 m_player->networkStateChanged();
1406 }
1407 if (m_readyState != MediaPlayer::HaveNothing) {
1408 m_readyState = MediaPlayer::HaveNothing;
1409 m_player->readyStateChanged();
1410 }
1411 }
1412
setSize(const IntSize & size)1413 void MediaPlayerPrivateGStreamer::setSize(const IntSize& size)
1414 {
1415 m_size = size;
1416 }
1417
setVisible(bool visible)1418 void MediaPlayerPrivateGStreamer::setVisible(bool visible)
1419 {
1420 }
1421
1422
triggerRepaint(GstBuffer * buffer)1423 void MediaPlayerPrivateGStreamer::triggerRepaint(GstBuffer* buffer)
1424 {
1425 g_return_if_fail(GST_IS_BUFFER(buffer));
1426 gst_buffer_replace(&m_buffer, buffer);
1427 m_player->repaint();
1428 }
1429
paint(GraphicsContext * context,const IntRect & rect)1430 void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& rect)
1431 {
1432 if (context->paintingDisabled())
1433 return;
1434
1435 if (!m_player->visible())
1436 return;
1437
1438 if (!m_buffer)
1439 return;
1440
1441 RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer);
1442 if (!gstImage)
1443 return;
1444
1445 context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
1446 rect, CompositeCopy, false);
1447 }
1448
mimeTypeCache()1449 static HashSet<String> mimeTypeCache()
1450 {
1451
1452 doGstInit();
1453
1454 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1455 static bool typeListInitialized = false;
1456
1457 if (!typeListInitialized) {
1458 // Build a whitelist of mime-types known to be supported by
1459 // GStreamer.
1460 HashSet<String> handledApplicationSubtypes;
1461 handledApplicationSubtypes.add(String("ogg"));
1462 handledApplicationSubtypes.add(String("vnd.rn-realmedia"));
1463 handledApplicationSubtypes.add(String("x-pn-realaudio"));
1464
1465 GList* factories = gst_type_find_factory_get_list();
1466 for (GList* iterator = factories; iterator; iterator = iterator->next) {
1467 GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data);
1468 GstCaps* caps = gst_type_find_factory_get_caps(factory);
1469 gchar** extensions;
1470
1471 if (!caps)
1472 continue;
1473
1474 for (guint structureIndex = 0; structureIndex < gst_caps_get_size(caps); structureIndex++) {
1475 GstStructure* structure = gst_caps_get_structure(caps, structureIndex);
1476 const gchar* name = gst_structure_get_name(structure);
1477 bool cached = false;
1478
1479 // These formats are supported by GStreamer, but not
1480 // correctly advertised.
1481 if (g_str_equal(name, "video/x-h264")) {
1482 cache.add(String("video/mp4"));
1483 cached = true;
1484 }
1485
1486 if (g_str_equal(name, "audio/x-m4a")) {
1487 cache.add(String("audio/aac"));
1488 cache.add(String("audio/mp4"));
1489 cache.add(String("audio/x-m4a"));
1490 cached = true;
1491 }
1492
1493 if (g_str_equal(name, "application/x-3gp")) {
1494 cache.add(String("audio/3gpp"));
1495 cache.add(String("video/3gpp"));
1496 cache.add(String("application/x-3gp"));
1497 cached = true;
1498 }
1499
1500 if (g_str_equal(name, "video/x-theora")) {
1501 cache.add(String("video/ogg"));
1502 cached = true;
1503 }
1504
1505 if (g_str_equal(name, "audio/x-vorbis")) {
1506 cache.add(String("audio/ogg"));
1507 cache.add(String("audio/x-vorbis+ogg"));
1508 cached = true;
1509 }
1510
1511 if (g_str_equal(name, "audio/x-wav")) {
1512 cache.add(String("audio/wav"));
1513 cache.add(String("audio/x-wav"));
1514 cached = true;
1515 }
1516
1517 if (g_str_equal(name, "audio/mpeg")) {
1518 cache.add(String(name));
1519 cache.add(String("audio/x-mpeg"));
1520 cached = true;
1521
1522 // This is what we are handling:
1523 // mpegversion=(int)1, layer=(int)[ 1, 3 ]
1524 gint mpegVersion = 0;
1525 if (gst_structure_get_int(structure, "mpegversion", &mpegVersion) && (mpegVersion == 1)) {
1526 const GValue* layer = gst_structure_get_value(structure, "layer");
1527 if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) {
1528 gint minLayer = gst_value_get_int_range_min(layer);
1529 gint maxLayer = gst_value_get_int_range_max(layer);
1530 if (minLayer <= 1 && 1 <= maxLayer)
1531 cache.add(String("audio/mp1"));
1532 if (minLayer <= 2 && 2 <= maxLayer)
1533 cache.add(String("audio/mp2"));
1534 if (minLayer <= 3 && 3 <= maxLayer) {
1535 cache.add(String("audio/x-mp3"));
1536 cache.add(String("audio/mp3"));
1537 }
1538 }
1539 }
1540 }
1541
1542 if (!cached) {
1543 // GStreamer plugins can be capable of supporting
1544 // types which WebKit supports by default. In that
1545 // case, we should not consider these types
1546 // supportable by GStreamer. Examples of what
1547 // GStreamer can support but should not be added:
1548 // text/plain, text/html, image/jpeg,
1549 // application/xml
1550 gchar** mimetype = g_strsplit(name, "/", 2);
1551 if (g_str_equal(mimetype[0], "audio")
1552 || g_str_equal(mimetype[0], "video")
1553 || (g_str_equal(mimetype[0], "application")
1554 && handledApplicationSubtypes.contains(String(mimetype[1]))))
1555 cache.add(String(name));
1556 else if (g_str_equal(name, "application/x-hls"))
1557 cache.add(String("application/vnd.apple.mpegurl"));
1558
1559
1560 g_strfreev(mimetype);
1561 }
1562
1563 // As a last resort try some special cases depending
1564 // on the file extensions registered with the typefind
1565 // factory.
1566 if (!cached && (extensions = gst_type_find_factory_get_extensions(factory))) {
1567 for (int index = 0; extensions[index]; index++) {
1568 if (g_str_equal(extensions[index], "m4v"))
1569 cache.add(String("video/x-m4v"));
1570
1571 // Workaround for
1572 // https://bugzilla.gnome.org/show_bug.cgi?id=640709.
1573 // typefindfunctions <= 0.10.32 doesn't
1574 // register the H264 typefinder correctly so
1575 // as a workaround we check the registered
1576 // file extensions for it.
1577 if (g_str_equal(extensions[index], "h264"))
1578 cache.add(String("video/mp4"));
1579 }
1580 }
1581 }
1582 }
1583
1584 gst_plugin_feature_list_free(factories);
1585 typeListInitialized = true;
1586 }
1587
1588 return cache;
1589 }
1590
getSupportedTypes(HashSet<String> & types)1591 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
1592 {
1593 types = mimeTypeCache();
1594 }
1595
supportsType(const String & type,const String & codecs)1596 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs)
1597 {
1598 if (type.isNull() || type.isEmpty())
1599 return MediaPlayer::IsNotSupported;
1600
1601 // spec says we should not return "probably" if the codecs string is empty
1602 if (mimeTypeCache().contains(type))
1603 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1604 return MediaPlayer::IsNotSupported;
1605 }
1606
hasSingleSecurityOrigin() const1607 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
1608 {
1609 return true;
1610 }
1611
supportsFullscreen() const1612 bool MediaPlayerPrivateGStreamer::supportsFullscreen() const
1613 {
1614 #if defined(BUILDING_ON_LEOPARD)
1615 // See <rdar://problem/7389945>
1616 return false;
1617 #else
1618 return true;
1619 #endif
1620 }
1621
platformMedia() const1622 PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const
1623 {
1624 PlatformMedia p;
1625 p.type = PlatformMedia::GStreamerGWorldType;
1626 p.media.gstreamerGWorld = m_gstGWorld.get();
1627 return p;
1628 }
1629
setPreload(MediaPlayer::Preload preload)1630 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
1631 {
1632 ASSERT(m_playBin);
1633
1634 m_preload = preload;
1635
1636 GstPlayFlags flags;
1637 g_object_get(m_playBin, "flags", &flags, NULL);
1638 if (preload == MediaPlayer::None)
1639 g_object_set(m_playBin, "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
1640 else
1641 g_object_set(m_playBin, "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
1642
1643 if (m_delayingLoad && m_preload != MediaPlayer::None) {
1644 m_delayingLoad = false;
1645 commitLoad();
1646 }
1647 }
1648
createGSTPlayBin()1649 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
1650 {
1651 ASSERT(!m_playBin);
1652 m_playBin = gst_element_factory_make("playbin2", "play");
1653
1654 m_gstGWorld = GStreamerGWorld::createGWorld(m_playBin);
1655
1656 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin));
1657 gst_bus_add_signal_watch(bus);
1658 g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
1659 gst_object_unref(bus);
1660
1661 g_object_set(m_playBin, "mute", m_player->muted(), NULL);
1662
1663 g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
1664 g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
1665 g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
1666 g_signal_connect(m_playBin, "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
1667 g_signal_connect(m_playBin, "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
1668
1669 m_webkitVideoSink = webkit_video_sink_new();
1670
1671 g_signal_connect(m_webkitVideoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
1672
1673 m_videoSinkBin = gst_bin_new("sink");
1674 GstElement* videoTee = gst_element_factory_make("tee", "videoTee");
1675 GstElement* queue = gst_element_factory_make("queue", 0);
1676 GstElement* identity = gst_element_factory_make("identity", "videoValve");
1677
1678 // Take ownership.
1679 gst_object_ref_sink(m_videoSinkBin);
1680
1681 // Build a new video sink consisting of a bin containing a tee
1682 // (meant to distribute data to multiple video sinks) and our
1683 // internal video sink. For fullscreen we create an autovideosink
1684 // and initially block the data flow towards it and configure it
1685
1686 gst_bin_add_many(GST_BIN(m_videoSinkBin), videoTee, queue, identity, NULL);
1687
1688 // Link a new src pad from tee to queue1.
1689 GstPad* srcPad = gst_element_get_request_pad(videoTee, "src%d");
1690 GstPad* sinkPad = gst_element_get_static_pad(queue, "sink");
1691 gst_pad_link(srcPad, sinkPad);
1692 gst_object_unref(GST_OBJECT(srcPad));
1693 gst_object_unref(GST_OBJECT(sinkPad));
1694
1695 GstElement* actualVideoSink = 0;
1696 m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
1697 if (m_fpsSink) {
1698 // The verbose property has been added in -bad 0.10.22. Making
1699 // this whole code depend on it because we don't want
1700 // fpsdiplaysink to spit data on stdout.
1701 GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory);
1702 if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) {
1703 g_object_set(m_fpsSink, "silent", TRUE , NULL);
1704
1705 // Turn off text overlay unless logging is enabled.
1706 WTFLogChannel* channel = getChannelFromName("Media");
1707 if (channel->state != WTFLogChannelOn)
1708 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
1709
1710 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) {
1711 g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink, NULL);
1712 gst_bin_add(GST_BIN(m_videoSinkBin), m_fpsSink);
1713 actualVideoSink = m_fpsSink;
1714 } else
1715 m_fpsSink = 0;
1716 } else
1717 m_fpsSink = 0;
1718 }
1719
1720 if (!m_fpsSink) {
1721 gst_bin_add(GST_BIN(m_videoSinkBin), m_webkitVideoSink);
1722 actualVideoSink = m_webkitVideoSink;
1723 }
1724
1725 ASSERT(actualVideoSink);
1726 #if GST_CHECK_VERSION(0, 10, 30)
1727 // Faster elements linking, if possible.
1728 gst_element_link_pads_full(queue, "src", identity, "sink", GST_PAD_LINK_CHECK_NOTHING);
1729 gst_element_link_pads_full(identity, "src", actualVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING);
1730 #else
1731 gst_element_link_many(queue, identity, actualVideoSink, NULL);
1732 #endif
1733
1734 // Add a ghostpad to the bin so it can proxy to tee.
1735 GstPad* pad = gst_element_get_static_pad(videoTee, "sink");
1736 gst_element_add_pad(m_videoSinkBin, gst_ghost_pad_new("sink", pad));
1737 gst_object_unref(GST_OBJECT(pad));
1738
1739 // Set the bin as video sink of playbin.
1740 g_object_set(m_playBin, "video-sink", m_videoSinkBin, NULL);
1741
1742
1743 pad = gst_element_get_static_pad(m_webkitVideoSink, "sink");
1744 if (pad) {
1745 g_signal_connect(pad, "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
1746 gst_object_unref(GST_OBJECT(pad));
1747 }
1748
1749 }
1750
1751 }
1752
1753 #endif // USE(GSTREAMER)
1754