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, ¤tState, &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", ¤tVideo, 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", ¤tAudio, 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", ¤tLocation, 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