1/* 2 * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> 3 * Copyright (C) 2013 Collabora Ltd. 4 * Copyright (C) 2013 Orange 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include "config.h" 22#include "WebKitMediaSourceGStreamer.h" 23 24#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER) 25 26#include "GRefPtrGStreamer.h" 27#include "GStreamerUtilities.h" 28#include "NotImplemented.h" 29#include "TimeRanges.h" 30#include <gst/app/gstappsrc.h> 31#include <gst/gst.h> 32#include <gst/pbutils/missing-plugins.h> 33#include <wtf/gobject/GMainLoopSource.h> 34#include <wtf/gobject/GUniquePtr.h> 35#include <wtf/text/CString.h> 36 37typedef struct _Source { 38 GstElement* appsrc; 39 guint sourceid; /* To control the GSource */ 40 GstPad* srcpad; 41 gboolean padAdded; 42 43 guint64 offset; 44 guint64 size; 45 gboolean paused; 46 47 GMainLoopSource start; 48 GMainLoopSource stop; 49 GMainLoopSource needData; 50 GMainLoopSource enoughData; 51 GMainLoopSource seek; 52 53 guint64 requestedOffset; 54} Source; 55 56 57#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate)) 58 59struct _WebKitMediaSrcPrivate { 60 gchar* uri; 61 Source sourceVideo; 62 Source sourceAudio; 63 WebCore::MediaPlayer* player; 64 GstElement* playbin; 65 gint64 duration; 66 gboolean seekable; 67 gboolean noMorePad; 68 // TRUE if appsrc's version is >= 0.10.27, see 69 // https://bugzilla.gnome.org/show_bug.cgi?id=609423 70 gboolean haveAppSrc27; 71 guint nbSource; 72}; 73 74enum { 75 PropLocation = 1, 76 ProLast 77}; 78 79static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY); 80 81GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug); 82#define GST_CAT_DEFAULT webkit_media_src_debug 83 84static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData); 85static void webKitMediaSrcFinalize(GObject*); 86static void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*); 87static void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*); 88static GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange); 89static gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); 90 91static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint, gpointer); 92static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer); 93static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64, gpointer); 94static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint, gpointer); 95static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer); 96static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64, gpointer); 97static GstAppSrcCallbacks appsrcCallbacksVideo = { 98 webKitMediaVideoSrcNeedDataCb, 99 webKitMediaVideoSrcEnoughDataCb, 100 webKitMediaVideoSrcSeekDataCb, 101 { 0 } 102}; 103static GstAppSrcCallbacks appsrcCallbacksAudio = { 104 webKitMediaAudioSrcNeedDataCb, 105 webKitMediaAudioSrcEnoughDataCb, 106 webKitMediaAudioSrcSeekDataCb, 107 { 0 } 108}; 109#define webkit_media_src_parent_class parent_class 110// We split this out into another macro to avoid a check-webkit-style error. 111#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element"); 112G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN, 113 G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit); 114 WEBKIT_MEDIA_SRC_CATEGORY_INIT); 115 116static void webkit_media_src_class_init(WebKitMediaSrcClass* klass) 117{ 118 GObjectClass* oklass = G_OBJECT_CLASS(klass); 119 GstElementClass* eklass = GST_ELEMENT_CLASS(klass); 120 121 oklass->finalize = webKitMediaSrcFinalize; 122 oklass->set_property = webKitMediaSrcSetProperty; 123 oklass->get_property = webKitMediaSrcGetProperty; 124 125 gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate)); 126 127 gst_element_class_set_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>"); 128 129 /* Allows setting the uri using the 'location' property, which is used 130 * for example by gst_element_make_from_uri() */ 131 g_object_class_install_property(oklass, 132 PropLocation, 133 g_param_spec_string("location", "location", "Location to read from", 0, 134 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); 135 136 eklass->change_state = webKitMediaSrcChangeState; 137 138 g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate)); 139} 140 141static void webKitMediaSrcAddSrc(WebKitMediaSrc* src, GstElement* element) 142{ 143 GstPad* ghostPad; 144 WebKitMediaSrcPrivate* priv = src->priv; 145 146 if (!gst_bin_add(GST_BIN(src), element)) { 147 GST_DEBUG_OBJECT(src, "Src element not added"); 148 return; 149 } 150 GRefPtr<GstPad> targetsrc = adoptGRef(gst_element_get_static_pad(element, "src")); 151 if (!targetsrc) { 152 GST_DEBUG_OBJECT(src, "Pad not found"); 153 return; 154 } 155 156 gst_element_sync_state_with_parent(element); 157 GUniquePtr<gchar> name(g_strdup_printf("src_%u", priv->nbSource)); 158 ghostPad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, name.get(), targetsrc.get()); 159 gst_pad_set_active(ghostPad, TRUE); 160 161 priv->nbSource++; 162 163 if (priv->sourceVideo.appsrc == element) 164 priv->sourceVideo.srcpad = ghostPad; 165 else if (priv->sourceAudio.appsrc == element) 166 priv->sourceAudio.srcpad = ghostPad; 167 168 GST_OBJECT_FLAG_SET(ghostPad, GST_PAD_FLAG_NEED_PARENT); 169 gst_pad_set_query_function(ghostPad, webKitMediaSrcQueryWithParent); 170} 171 172static void webkit_media_src_init(WebKitMediaSrc* src) 173{ 174 WebKitMediaSrcPrivate* priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(src); 175 src->priv = priv; 176 new (priv) WebKitMediaSrcPrivate(); 177 178 priv->sourceVideo.appsrc = gst_element_factory_make("appsrc", "videoappsrc"); 179 gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceVideo.appsrc), &appsrcCallbacksVideo, src, 0); 180 webKitMediaSrcAddSrc(src, priv->sourceVideo.appsrc); 181 182 priv->sourceAudio.appsrc = gst_element_factory_make("appsrc", "audioappsrc"); 183 gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceAudio.appsrc), &appsrcCallbacksAudio, src, 0); 184 webKitMediaSrcAddSrc(src, priv->sourceAudio.appsrc); 185} 186 187static void webKitMediaSrcFinalize(GObject* object) 188{ 189 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object); 190 WebKitMediaSrcPrivate* priv = src->priv; 191 192 g_free(priv->uri); 193 priv->~WebKitMediaSrcPrivate(); 194 195 GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); 196} 197 198static void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec) 199{ 200 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object); 201 switch (propId) { 202 case PropLocation: 203 gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0); 204 break; 205 default: 206 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); 207 break; 208 } 209} 210 211static void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec) 212{ 213 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object); 214 WebKitMediaSrcPrivate* priv = src->priv; 215 216 GST_OBJECT_LOCK(src); 217 switch (propId) { 218 case PropLocation: 219 g_value_set_string(value, priv->uri); 220 break; 221 default: 222 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec); 223 break; 224 } 225 GST_OBJECT_UNLOCK(src); 226} 227 228// must be called on main thread and with object unlocked 229static void webKitMediaVideoSrcStop(WebKitMediaSrc* src) 230{ 231 WebKitMediaSrcPrivate* priv = src->priv; 232 gboolean seeking; 233 234 GST_OBJECT_LOCK(src); 235 236 seeking = priv->sourceVideo.seek.isActive(); 237 238 priv->sourceVideo.start.cancel(); 239 240 priv->player = 0; 241 priv->playbin = 0; 242 243 priv->sourceVideo.needData.cancel(); 244 priv->sourceVideo.enoughData.cancel(); 245 priv->sourceVideo.seek.cancel(); 246 247 priv->sourceVideo.paused = FALSE; 248 priv->sourceVideo.offset = 0; 249 priv->seekable = FALSE; 250 251 priv->duration = 0; 252 priv->nbSource = 0; 253 254 GST_OBJECT_UNLOCK(src); 255 256 if (priv->sourceVideo.appsrc) { 257 gst_app_src_set_caps(GST_APP_SRC(priv->sourceVideo.appsrc), 0); 258 if (!seeking) 259 gst_app_src_set_size(GST_APP_SRC(priv->sourceVideo.appsrc), -1); 260 } 261 262 GST_DEBUG_OBJECT(src, "Stopped request"); 263} 264 265static void webKitMediaAudioSrcStop(WebKitMediaSrc* src) 266{ 267 WebKitMediaSrcPrivate* priv = src->priv; 268 gboolean seeking; 269 270 GST_OBJECT_LOCK(src); 271 272 seeking = priv->sourceAudio.seek.isActive(); 273 274 priv->sourceAudio.start.cancel(); 275 276 priv->player = 0; 277 278 priv->playbin = 0; 279 280 priv->sourceAudio.needData.cancel(); 281 priv->sourceAudio.enoughData.cancel(); 282 priv->sourceAudio.seek.cancel(); 283 284 priv->sourceAudio.paused = FALSE; 285 286 priv->sourceAudio.offset = 0; 287 288 priv->seekable = FALSE; 289 290 priv->duration = 0; 291 priv->nbSource = 0; 292 293 GST_OBJECT_UNLOCK(src); 294 295 if (priv->sourceAudio.appsrc) { 296 gst_app_src_set_caps(GST_APP_SRC(priv->sourceAudio.appsrc), 0); 297 if (!seeking) 298 gst_app_src_set_size(GST_APP_SRC(priv->sourceAudio.appsrc), -1); 299 } 300 301 GST_DEBUG_OBJECT(src, "Stopped request"); 302} 303 304// must be called on main thread and with object unlocked 305static void webKitMediaVideoSrcStart(WebKitMediaSrc* src) 306{ 307 WebKitMediaSrcPrivate* priv = src->priv; 308 309 GST_OBJECT_LOCK(src); 310 if (!priv->uri) { 311 GST_ERROR_OBJECT(src, "No URI provided"); 312 GST_OBJECT_UNLOCK(src); 313 webKitMediaVideoSrcStop(src); 314 return; 315 } 316 317 GST_OBJECT_UNLOCK(src); 318 GST_DEBUG_OBJECT(src, "Started request"); 319} 320 321// must be called on main thread and with object unlocked 322static void webKitMediaAudioSrcStart(WebKitMediaSrc* src) 323{ 324 WebKitMediaSrcPrivate* priv = src->priv; 325 326 GST_OBJECT_LOCK(src); 327 if (!priv->uri) { 328 GST_ERROR_OBJECT(src, "No URI provided"); 329 GST_OBJECT_UNLOCK(src); 330 webKitMediaAudioSrcStop(src); 331 return; 332 } 333 334 GST_OBJECT_UNLOCK(src); 335 GST_DEBUG_OBJECT(src, "Started request"); 336} 337 338static GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition) 339{ 340 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; 341 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(element); 342 WebKitMediaSrcPrivate* priv = src->priv; 343 344 switch (transition) { 345 case GST_STATE_CHANGE_NULL_TO_READY: 346 if (!priv->sourceVideo.appsrc && !priv->sourceAudio.appsrc) { 347 gst_element_post_message(element, 348 gst_missing_element_message_new(element, "appsrc")); 349 GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc")); 350 return GST_STATE_CHANGE_FAILURE; 351 } 352 break; 353 default: 354 break; 355 } 356 357 ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); 358 if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) { 359 GST_DEBUG_OBJECT(src, "State change failed"); 360 return ret; 361 } 362 363 switch (transition) { 364 case GST_STATE_CHANGE_READY_TO_PAUSED: 365 GST_DEBUG_OBJECT(src, "READY->PAUSED"); 366 GST_OBJECT_LOCK(src); 367 368 gst_object_ref(src); 369 priv->sourceVideo.start.schedule("[WebKit] webKitMediaVideoSrcStart", std::function<void()>(std::bind(webKitMediaVideoSrcStart, src)), G_PRIORITY_DEFAULT, 370 [src] { gst_object_unref(src); }); 371 372 gst_object_ref(src); 373 priv->sourceAudio.start.schedule("[WebKit] webKitMediaAudioSrcStart", std::function<void()>(std::bind(webKitMediaAudioSrcStart, src)), G_PRIORITY_DEFAULT, 374 [src] { gst_object_unref(src); }); 375 376 GST_OBJECT_UNLOCK(src); 377 break; 378 case GST_STATE_CHANGE_PAUSED_TO_READY: 379 GST_DEBUG_OBJECT(src, "PAUSED->READY"); 380 GST_OBJECT_LOCK(src); 381 382 gst_object_ref(src); 383 priv->sourceVideo.stop.schedule("[WebKit] webKitMediaVideoSrcStop", std::function<void()>(std::bind(webKitMediaVideoSrcStop, src)), G_PRIORITY_DEFAULT, 384 [src] { gst_object_unref(src); }); 385 386 gst_object_ref(src); 387 priv->sourceAudio.stop.schedule("[WebKit] webKitMediaAudioSrcStop", std::function<void()>(std::bind(webKitMediaAudioSrcStop, src)), G_PRIORITY_DEFAULT, 388 [src] { gst_object_unref(src); }); 389 390 GST_OBJECT_UNLOCK(src); 391 break; 392 default: 393 break; 394 } 395 396 return ret; 397} 398 399static gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query) 400{ 401 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent)); 402 gboolean result = FALSE; 403 404 switch (GST_QUERY_TYPE(query)) { 405 case GST_QUERY_DURATION: { 406 GstFormat format; 407 gst_query_parse_duration(query, &format, NULL); 408 409 GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format)); 410 GST_OBJECT_LOCK(src); 411 if ((format == GST_FORMAT_TIME) && (src->priv->duration > 0)) { 412 gst_query_set_duration(query, format, src->priv->duration); 413 result = TRUE; 414 } 415 GST_OBJECT_UNLOCK(src); 416 break; 417 } 418 case GST_QUERY_URI: { 419 GST_OBJECT_LOCK(src); 420 gst_query_set_uri(query, src->priv->uri); 421 GST_OBJECT_UNLOCK(src); 422 result = TRUE; 423 break; 424 } 425 default: { 426 GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad))); 427 // Forward the query to the proxy target pad. 428 if (target) 429 result = gst_pad_query(target.get(), query); 430 break; 431 } 432 } 433 434 return result; 435} 436 437// uri handler interface 438static GstURIType webKitMediaSrcUriGetType(GType) 439{ 440 return GST_URI_SRC; 441} 442 443const gchar* const* webKitMediaSrcGetProtocols(GType) 444{ 445 static const char* protocols[] = {"mediasourceblob", 0 }; 446 return protocols; 447} 448 449static gchar* webKitMediaSrcGetUri(GstURIHandler* handler) 450{ 451 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler); 452 gchar* ret; 453 454 GST_OBJECT_LOCK(src); 455 ret = g_strdup(src->priv->uri); 456 GST_OBJECT_UNLOCK(src); 457 return ret; 458} 459 460static gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**) 461{ 462 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler); 463 WebKitMediaSrcPrivate* priv = src->priv; 464 if (GST_STATE(src) >= GST_STATE_PAUSED) { 465 GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED"); 466 return FALSE; 467 } 468 469 GST_OBJECT_LOCK(src); 470 g_free(priv->uri); 471 priv->uri = 0; 472 if (!uri) { 473 GST_OBJECT_UNLOCK(src); 474 return TRUE; 475 } 476 477 WebCore::URL url(WebCore::URL(), uri); 478 479 priv->uri = g_strdup(url.string().utf8().data()); 480 GST_OBJECT_UNLOCK(src); 481 return TRUE; 482} 483 484static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer) 485{ 486 GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface; 487 488 iface->get_type = webKitMediaSrcUriGetType; 489 iface->get_protocols = webKitMediaSrcGetProtocols; 490 iface->get_uri = webKitMediaSrcGetUri; 491 iface->set_uri = webKitMediaSrcSetUri; 492} 493 494// appsrc callbacks 495static void webKitMediaVideoSrcNeedDataMainCb(WebKitMediaSrc* src) 496{ 497 WebKitMediaSrcPrivate* priv = src->priv; 498 499 GST_OBJECT_LOCK(src); 500 priv->sourceVideo.paused = FALSE; 501 GST_OBJECT_UNLOCK(src); 502} 503 504static void webKitMediaAudioSrcNeedDataMainCb(WebKitMediaSrc* src) 505{ 506 WebKitMediaSrcPrivate* priv = src->priv; 507 508 GST_OBJECT_LOCK(src); 509 priv->sourceAudio.paused = FALSE; 510 GST_OBJECT_UNLOCK(src); 511} 512 513static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) 514{ 515 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); 516 WebKitMediaSrcPrivate* priv = src->priv; 517 518 GST_DEBUG_OBJECT(src, "Need more data: %u", length); 519 520 GST_OBJECT_LOCK(src); 521 if (priv->sourceVideo.needData.isScheduled() || !priv->sourceVideo.paused) { 522 GST_OBJECT_UNLOCK(src); 523 return; 524 } 525 526 gst_object_ref(src); 527 priv->sourceVideo.needData.schedule("[WebKit] webKitMediaVideoSrcNeedDataMainCb", std::function<void()>(std::bind(webKitMediaVideoSrcNeedDataMainCb, src)), G_PRIORITY_DEFAULT, 528 [src] { gst_object_unref(src); }); 529 GST_OBJECT_UNLOCK(src); 530} 531 532static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) 533{ 534 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); 535 WebKitMediaSrcPrivate* priv = src->priv; 536 537 GST_DEBUG_OBJECT(src, "Need more data: %u", length); 538 539 GST_OBJECT_LOCK(src); 540 if (priv->sourceAudio.needData.isScheduled() || !priv->sourceAudio.paused) { 541 GST_OBJECT_UNLOCK(src); 542 return; 543 } 544 545 gst_object_ref(src); 546 priv->sourceAudio.needData.schedule("[WebKit] webKitMediaAudioSrcNeedDataMainCb", std::function<void()>(std::bind(webKitMediaAudioSrcNeedDataMainCb, src)), G_PRIORITY_DEFAULT, 547 [src] { gst_object_unref(src); }); 548 GST_OBJECT_UNLOCK(src); 549} 550 551static void webKitMediaVideoSrcEnoughDataMainCb(WebKitMediaSrc* src) 552{ 553 WebKitMediaSrcPrivate* priv = src->priv; 554 555 GST_OBJECT_LOCK(src); 556 priv->sourceVideo.paused = TRUE; 557 GST_OBJECT_UNLOCK(src); 558} 559 560static void webKitMediaAudioSrcEnoughDataMainCb(WebKitMediaSrc* src) 561{ 562 WebKitMediaSrcPrivate* priv = src->priv; 563 564 GST_OBJECT_LOCK(src); 565 priv->sourceAudio.paused = TRUE; 566 GST_OBJECT_UNLOCK(src); 567} 568 569static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer userData) 570{ 571 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); 572 WebKitMediaSrcPrivate* priv = src->priv; 573 574 GST_DEBUG_OBJECT(src, "Have enough data"); 575 576 GST_OBJECT_LOCK(src); 577 if (priv->sourceVideo.enoughData.isScheduled() || priv->sourceVideo.paused) { 578 GST_OBJECT_UNLOCK(src); 579 return; 580 } 581 582 gst_object_ref(src); 583 priv->sourceVideo.enoughData.schedule("[WebKit] webKitMediaVideoSrcEnoughDataMainCb", std::function<void()>(std::bind(webKitMediaVideoSrcEnoughDataMainCb, src)), G_PRIORITY_DEFAULT, 584 [src] { gst_object_unref(src); }); 585 586 GST_OBJECT_UNLOCK(src); 587} 588 589static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer userData) 590{ 591 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); 592 WebKitMediaSrcPrivate* priv = src->priv; 593 594 GST_DEBUG_OBJECT(src, "Have enough data"); 595 596 GST_OBJECT_LOCK(src); 597 if (priv->sourceAudio.enoughData.isScheduled() || priv->sourceAudio.paused) { 598 GST_OBJECT_UNLOCK(src); 599 return; 600 } 601 602 gst_object_ref(src); 603 priv->sourceAudio.enoughData.schedule("[WebKit] webKitMediaAudioSrcEnoughDataMainCb", std::function<void()>(std::bind(webKitMediaAudioSrcEnoughDataMainCb, src)), G_PRIORITY_DEFAULT, 604 [src] { gst_object_unref(src); }); 605 606 GST_OBJECT_UNLOCK(src); 607} 608 609static void webKitMediaVideoSrcSeekMainCb(WebKitMediaSrc*) 610{ 611 notImplemented(); 612} 613 614 615static void webKitMediaAudioSrcSeekMainCb(WebKitMediaSrc*) 616{ 617 notImplemented(); 618} 619 620static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData) 621{ 622 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); 623 WebKitMediaSrcPrivate* priv = src->priv; 624 625 GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); 626 GST_OBJECT_LOCK(src); 627 if (offset == priv->sourceVideo.offset && priv->sourceVideo.requestedOffset == priv->sourceVideo.offset) { 628 GST_OBJECT_UNLOCK(src); 629 return TRUE; 630 } 631 632 if (!priv->seekable) { 633 GST_OBJECT_UNLOCK(src); 634 return FALSE; 635 } 636 if (offset > priv->sourceVideo.size) { 637 GST_OBJECT_UNLOCK(src); 638 return FALSE; 639 } 640 641 GST_DEBUG_OBJECT(src, "Doing range-request seek"); 642 priv->sourceVideo.requestedOffset = offset; 643 644 gst_object_ref(src); 645 priv->sourceVideo.seek.schedule("[WebKit] webKitMediaVideoSrcSeekMainCb", std::function<void()>(std::bind(webKitMediaVideoSrcSeekMainCb, src)), G_PRIORITY_DEFAULT, 646 [src] { gst_object_unref(src); }); 647 648 GST_OBJECT_UNLOCK(src); 649 650 return TRUE; 651} 652 653static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData) 654{ 655 WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData); 656 WebKitMediaSrcPrivate* priv = src->priv; 657 658 GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); 659 GST_OBJECT_LOCK(src); 660 if (offset == priv->sourceAudio.offset && priv->sourceAudio.requestedOffset == priv->sourceAudio.offset) { 661 GST_OBJECT_UNLOCK(src); 662 return TRUE; 663 } 664 665 if (!priv->seekable) { 666 GST_OBJECT_UNLOCK(src); 667 return FALSE; 668 } 669 if (offset > priv->sourceAudio.size) { 670 GST_OBJECT_UNLOCK(src); 671 return FALSE; 672 } 673 674 GST_DEBUG_OBJECT(src, "Doing range-request seek"); 675 priv->sourceAudio.requestedOffset = offset; 676 677 gst_object_ref(src); 678 priv->sourceAudio.seek.schedule("[WebKit] webKitMediaAudioSrcSeekMainCb", std::function<void()>(std::bind(webKitMediaAudioSrcSeekMainCb, src)), G_PRIORITY_DEFAULT, 679 [src] { gst_object_unref(src); }); 680 681 GST_OBJECT_UNLOCK(src); 682 683 return TRUE; 684} 685 686void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc* src, WebCore::MediaPlayer* player) 687{ 688 WebKitMediaSrcPrivate* priv = src->priv; 689 priv->player = player; 690} 691 692void webKitMediaSrcSetPlayBin(WebKitMediaSrc* src, GstElement* playBin) 693{ 694 WebKitMediaSrcPrivate* priv = src->priv; 695 priv->playbin = playBin; 696} 697 698MediaSourceClientGstreamer::MediaSourceClientGstreamer(WebKitMediaSrc* src) 699 : m_src(static_cast<WebKitMediaSrc*>(gst_object_ref(src))) 700{ 701} 702 703MediaSourceClientGstreamer::~MediaSourceClientGstreamer() 704{ 705 gst_object_unref(m_src); 706} 707 708void MediaSourceClientGstreamer::didReceiveDuration(double duration) 709{ 710 WebKitMediaSrcPrivate* priv = m_src->priv; 711 GST_DEBUG_OBJECT(m_src, "Received duration: %lf", duration); 712 713 GST_OBJECT_LOCK(m_src); 714 priv->duration = duration >= 0.0 ? static_cast<gint64>(duration*GST_SECOND) : 0; 715 GST_OBJECT_UNLOCK(m_src); 716} 717 718void MediaSourceClientGstreamer::didReceiveData(const char* data, int length, String type) 719{ 720 WebKitMediaSrcPrivate* priv = m_src->priv; 721 GstFlowReturn ret = GST_FLOW_OK; 722 GstBuffer * buffer; 723 724 if (type.startsWith("video")) { 725 if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == TRUE) { 726 gst_element_no_more_pads(GST_ELEMENT(m_src)); 727 priv->noMorePad = TRUE; 728 } 729 if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == FALSE) { 730 gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceVideo.srcpad); 731 priv->sourceVideo.padAdded = TRUE; 732 } 733 GST_OBJECT_LOCK(m_src); 734 buffer = WebCore::createGstBufferForData(data, length); 735 GST_OBJECT_UNLOCK(m_src); 736 737 ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceVideo.appsrc), buffer); 738 } else if (type.startsWith("audio")) { 739 if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == TRUE) { 740 gst_element_no_more_pads(GST_ELEMENT(m_src)); 741 priv->noMorePad = TRUE; 742 } 743 if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == FALSE) { 744 gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceAudio.srcpad); 745 priv->sourceAudio.padAdded = TRUE; 746 } 747 GST_OBJECT_LOCK(m_src); 748 buffer = WebCore::createGstBufferForData(data, length); 749 GST_OBJECT_UNLOCK(m_src); 750 751 ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceAudio.appsrc), buffer); 752 } 753 754 if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) 755 GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0)); 756} 757 758void MediaSourceClientGstreamer::didFinishLoading(double) 759{ 760 WebKitMediaSrcPrivate* priv = m_src->priv; 761 762 GST_DEBUG_OBJECT(m_src, "Have EOS"); 763 764 GST_OBJECT_LOCK(m_src); 765 if (!priv->sourceVideo.seek.isActive()) { 766 GST_OBJECT_UNLOCK(m_src); 767 gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceVideo.appsrc)); 768 } else 769 GST_OBJECT_UNLOCK(m_src); 770 771 GST_OBJECT_LOCK(m_src); 772 if (!priv->sourceAudio.seek.isActive()) { 773 GST_OBJECT_UNLOCK(m_src); 774 gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceAudio.appsrc)); 775 } else 776 GST_OBJECT_UNLOCK(m_src); 777} 778 779void MediaSourceClientGstreamer::didFail() 780{ 781 gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceVideo.appsrc)); 782 gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceAudio.appsrc)); 783} 784 785#endif // USE(GSTREAMER) 786 787