blob: 8f8e725104e40ff8d6efb8affa8b1a581f071f31 [file] [log] [blame]
/*
* Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) 2013 Collabora Ltd.
* Copyright (C) 2013 Orange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include "WebKitMediaSourceGStreamer.h"
#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
#include "GRefPtrGStreamer.h"
#include "GStreamerUtilities.h"
#include "NotImplemented.h"
#include "TimeRanges.h"
#include <gst/app/gstappsrc.h>
#include <gst/gst.h>
#include <gst/pbutils/missing-plugins.h>
#include <wtf/gobject/GUniquePtr.h>
#include <wtf/text/CString.h>
typedef struct _Source {
GstElement* appsrc;
guint sourceid; /* To control the GSource */
GstPad* srcpad;
gboolean padAdded;
guint64 offset;
guint64 size;
gboolean paused;
guint startId;
guint stopId;
guint needDataId;
guint enoughDataId;
guint seekId;
guint64 requestedOffset;
} Source;
#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate))
struct _WebKitMediaSrcPrivate {
gchar* uri;
Source sourceVideo;
Source sourceAudio;
WebCore::MediaPlayer* player;
GstElement* playbin;
gint64 duration;
gboolean seekable;
gboolean noMorePad;
// TRUE if appsrc's version is >= 0.10.27, see
// https://bugzilla.gnome.org/show_bug.cgi?id=609423
gboolean haveAppSrc27;
guint nbSource;
};
enum {
PropLocation = 1,
ProLast
};
static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
#define GST_CAT_DEFAULT webkit_media_src_debug
static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
static void webKitMediaSrcFinalize(GObject*);
static void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*);
static void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*);
static GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange);
static gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint, gpointer);
static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer);
static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64, gpointer);
static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint, gpointer);
static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer);
static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64, gpointer);
static GstAppSrcCallbacks appsrcCallbacksVideo = {
webKitMediaVideoSrcNeedDataCb,
webKitMediaVideoSrcEnoughDataCb,
webKitMediaVideoSrcSeekDataCb,
{ 0 }
};
static GstAppSrcCallbacks appsrcCallbacksAudio = {
webKitMediaAudioSrcNeedDataCb,
webKitMediaAudioSrcEnoughDataCb,
webKitMediaAudioSrcSeekDataCb,
{ 0 }
};
#define webkit_media_src_parent_class parent_class
// We split this out into another macro to avoid a check-webkit-style error.
#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
WEBKIT_MEDIA_SRC_CATEGORY_INIT);
static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
{
GObjectClass* oklass = G_OBJECT_CLASS(klass);
GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
oklass->finalize = webKitMediaSrcFinalize;
oklass->set_property = webKitMediaSrcSetProperty;
oklass->get_property = webKitMediaSrcGetProperty;
gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
gst_element_class_set_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>");
/* Allows setting the uri using the 'location' property, which is used
* for example by gst_element_make_from_uri() */
g_object_class_install_property(oklass,
PropLocation,
g_param_spec_string("location", "location", "Location to read from", 0,
(GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
eklass->change_state = webKitMediaSrcChangeState;
g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
}
static void webKitMediaSrcAddSrc(WebKitMediaSrc* src, GstElement* element)
{
GstPad* ghostPad;
WebKitMediaSrcPrivate* priv = src->priv;
if (!gst_bin_add(GST_BIN(src), element)) {
GST_DEBUG_OBJECT(src, "Src element not added");
return;
}
GRefPtr<GstPad> targetsrc = adoptGRef(gst_element_get_static_pad(element, "src"));
if (!targetsrc) {
GST_DEBUG_OBJECT(src, "Pad not found");
return;
}
gst_element_sync_state_with_parent(element);
GUniquePtr<gchar> name(g_strdup_printf("src_%u", priv->nbSource));
ghostPad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, name.get(), targetsrc.get());
gst_pad_set_active(ghostPad, TRUE);
priv->nbSource++;
if (priv->sourceVideo.appsrc == element)
priv->sourceVideo.srcpad = ghostPad;
else if (priv->sourceAudio.appsrc == element)
priv->sourceAudio.srcpad = ghostPad;
GST_OBJECT_FLAG_SET(ghostPad, GST_PAD_FLAG_NEED_PARENT);
gst_pad_set_query_function(ghostPad, webKitMediaSrcQueryWithParent);
}
static void webkit_media_src_init(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(src);
src->priv = priv;
priv->sourceVideo.appsrc = gst_element_factory_make("appsrc", "videoappsrc");
gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceVideo.appsrc), &appsrcCallbacksVideo, src, 0);
webKitMediaSrcAddSrc(src, priv->sourceVideo.appsrc);
priv->sourceAudio.appsrc = gst_element_factory_make("appsrc", "audioappsrc");
gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceAudio.appsrc), &appsrcCallbacksAudio, src, 0);
webKitMediaSrcAddSrc(src, priv->sourceAudio.appsrc);
}
static void webKitMediaSrcFinalize(GObject* object)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
WebKitMediaSrcPrivate* priv = src->priv;
g_free(priv->uri);
GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
}
static void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
switch (propId) {
case PropLocation:
gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
break;
}
}
static void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
switch (propId) {
case PropLocation:
g_value_set_string(value, priv->uri);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
break;
}
GST_OBJECT_UNLOCK(src);
}
// must be called on main thread and with object unlocked
static gboolean webKitMediaVideoSrcStop(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
gboolean seeking;
GST_OBJECT_LOCK(src);
seeking = priv->sourceVideo.seekId;
if (priv->sourceVideo.startId) {
g_source_remove(priv->sourceVideo.startId);
priv->sourceVideo.startId = 0;
}
priv->player = 0;
priv->playbin = 0;
if (priv->sourceVideo.needDataId)
g_source_remove(priv->sourceVideo.needDataId);
priv->sourceVideo.needDataId = 0;
if (priv->sourceVideo.enoughDataId)
g_source_remove(priv->sourceVideo.enoughDataId);
priv->sourceVideo.enoughDataId = 0;
if (priv->sourceVideo.seekId)
g_source_remove(priv->sourceVideo.seekId);
priv->sourceVideo.seekId = 0;
priv->sourceVideo.paused = FALSE;
priv->sourceVideo.offset = 0;
priv->seekable = FALSE;
priv->duration = 0;
priv->nbSource = 0;
priv->sourceVideo.stopId = 0;
GST_OBJECT_UNLOCK(src);
if (priv->sourceVideo.appsrc) {
gst_app_src_set_caps(GST_APP_SRC(priv->sourceVideo.appsrc), 0);
if (!seeking)
gst_app_src_set_size(GST_APP_SRC(priv->sourceVideo.appsrc), -1);
}
GST_DEBUG_OBJECT(src, "Stopped request");
return FALSE;
}
static gboolean webKitMediaAudioSrcStop(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
gboolean seeking;
GST_OBJECT_LOCK(src);
seeking = priv->sourceAudio.seekId;
if (priv->sourceAudio.startId) {
g_source_remove(priv->sourceAudio.startId);
priv->sourceAudio.startId = 0;
}
priv->player = 0;
priv->playbin = 0;
if (priv->sourceAudio.needDataId)
g_source_remove(priv->sourceAudio.needDataId);
priv->sourceAudio.needDataId = 0;
if (priv->sourceAudio.enoughDataId)
g_source_remove(priv->sourceAudio.enoughDataId);
priv->sourceAudio.enoughDataId = 0;
if (priv->sourceAudio.seekId)
g_source_remove(priv->sourceAudio.seekId);
priv->sourceAudio.seekId = 0;
priv->sourceAudio.paused = FALSE;
priv->sourceAudio.offset = 0;
priv->seekable = FALSE;
priv->duration = 0;
priv->nbSource = 0;
priv->sourceAudio.stopId = 0;
GST_OBJECT_UNLOCK(src);
if (priv->sourceAudio.appsrc) {
gst_app_src_set_caps(GST_APP_SRC(priv->sourceAudio.appsrc), 0);
if (!seeking)
gst_app_src_set_size(GST_APP_SRC(priv->sourceAudio.appsrc), -1);
}
GST_DEBUG_OBJECT(src, "Stopped request");
return FALSE;
}
// must be called on main thread and with object unlocked
static gboolean webKitMediaVideoSrcStart(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
if (!priv->uri) {
GST_ERROR_OBJECT(src, "No URI provided");
GST_OBJECT_UNLOCK(src);
webKitMediaVideoSrcStop(src);
return FALSE;
}
GST_OBJECT_UNLOCK(src);
GST_DEBUG_OBJECT(src, "Started request");
return FALSE;
}
// must be called on main thread and with object unlocked
static gboolean webKitMediaAudioSrcStart(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
if (!priv->uri) {
GST_ERROR_OBJECT(src, "No URI provided");
GST_OBJECT_UNLOCK(src);
webKitMediaAudioSrcStop(src);
return FALSE;
}
GST_OBJECT_UNLOCK(src);
GST_DEBUG_OBJECT(src, "Started request");
return FALSE;
}
static GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(element);
WebKitMediaSrcPrivate* priv = src->priv;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
if (!priv->sourceVideo.appsrc && !priv->sourceAudio.appsrc) {
gst_element_post_message(element,
gst_missing_element_message_new(element, "appsrc"));
GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
return GST_STATE_CHANGE_FAILURE;
}
break;
default:
break;
}
ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
GST_DEBUG_OBJECT(src, "State change failed");
return ret;
}
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_DEBUG_OBJECT(src, "READY->PAUSED");
GST_OBJECT_LOCK(src);
priv->sourceVideo.startId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceVideo.startId, "[WebKit] webKitMediaVideoSrcStart");
priv->sourceAudio.startId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceAudio.startId, "[WebKit] webKitMediaAudioSrcStart");
GST_OBJECT_UNLOCK(src);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG_OBJECT(src, "PAUSED->READY");
GST_OBJECT_LOCK(src);
priv->sourceVideo.stopId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceVideo.stopId, "[WebKit] webKitMediaVideoSrcStop");
priv->sourceAudio.stopId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceAudio.stopId, "[WebKit] webKitMediaAudioSrcStop");
GST_OBJECT_UNLOCK(src);
break;
default:
break;
}
return ret;
}
static gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
gboolean result = FALSE;
switch (GST_QUERY_TYPE(query)) {
case GST_QUERY_DURATION: {
GstFormat format;
gst_query_parse_duration(query, &format, NULL);
GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
GST_OBJECT_LOCK(src);
if ((format == GST_FORMAT_TIME) && (src->priv->duration > 0)) {
gst_query_set_duration(query, format, src->priv->duration);
result = TRUE;
}
GST_OBJECT_UNLOCK(src);
break;
}
case GST_QUERY_URI: {
GST_OBJECT_LOCK(src);
gst_query_set_uri(query, src->priv->uri);
GST_OBJECT_UNLOCK(src);
result = TRUE;
break;
}
default: {
GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
// Forward the query to the proxy target pad.
if (target)
result = gst_pad_query(target.get(), query);
break;
}
}
return result;
}
// uri handler interface
static GstURIType webKitMediaSrcUriGetType(GType)
{
return GST_URI_SRC;
}
const gchar* const* webKitMediaSrcGetProtocols(GType)
{
static const char* protocols[] = {"mediasourceblob", 0 };
return protocols;
}
static gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
gchar* ret;
GST_OBJECT_LOCK(src);
ret = g_strdup(src->priv->uri);
GST_OBJECT_UNLOCK(src);
return ret;
}
static gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
WebKitMediaSrcPrivate* priv = src->priv;
if (GST_STATE(src) >= GST_STATE_PAUSED) {
GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
return FALSE;
}
GST_OBJECT_LOCK(src);
g_free(priv->uri);
priv->uri = 0;
if (!uri) {
GST_OBJECT_UNLOCK(src);
return TRUE;
}
WebCore::URL url(WebCore::URL(), uri);
priv->uri = g_strdup(url.string().utf8().data());
GST_OBJECT_UNLOCK(src);
return TRUE;
}
static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
{
GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
iface->get_type = webKitMediaSrcUriGetType;
iface->get_protocols = webKitMediaSrcGetProtocols;
iface->get_uri = webKitMediaSrcGetUri;
iface->set_uri = webKitMediaSrcSetUri;
}
// appsrc callbacks
static gboolean webKitMediaVideoSrcNeedDataMainCb(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
// already stopped
if (!priv->sourceVideo.needDataId) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
priv->sourceVideo.paused = FALSE;
priv->sourceVideo.needDataId = 0;
GST_OBJECT_UNLOCK(src);
return FALSE;
}
static gboolean webKitMediaAudioSrcNeedDataMainCb(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
// already stopped
if (!priv->sourceAudio.needDataId) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
priv->sourceAudio.paused = FALSE;
priv->sourceAudio.needDataId = 0;
GST_OBJECT_UNLOCK(src);
return FALSE;
}
static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
WebKitMediaSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Need more data: %u", length);
GST_OBJECT_LOCK(src);
if (priv->sourceVideo.needDataId || !priv->sourceVideo.paused) {
GST_OBJECT_UNLOCK(src);
return;
}
priv->sourceVideo.needDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceVideo.needDataId, "[WebKit] webKitMediaVideoSrcNeedDataMainCb");
GST_OBJECT_UNLOCK(src);
}
static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
WebKitMediaSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Need more data: %u", length);
GST_OBJECT_LOCK(src);
if (priv->sourceAudio.needDataId || !priv->sourceAudio.paused) {
GST_OBJECT_UNLOCK(src);
return;
}
priv->sourceAudio.needDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceAudio.needDataId, "[WebKit] webKitMediaAudioSrcNeedDataMainCb");
GST_OBJECT_UNLOCK(src);
}
static gboolean webKitMediaVideoSrcEnoughDataMainCb(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
// already stopped
if (!priv->sourceVideo.enoughDataId) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
priv->sourceVideo.paused = TRUE;
priv->sourceVideo.enoughDataId = 0;
GST_OBJECT_UNLOCK(src);
return FALSE;
}
static gboolean webKitMediaAudioSrcEnoughDataMainCb(WebKitMediaSrc* src)
{
WebKitMediaSrcPrivate* priv = src->priv;
GST_OBJECT_LOCK(src);
// already stopped
if (!priv->sourceAudio.enoughDataId) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
priv->sourceAudio.paused = TRUE;
priv->sourceAudio.enoughDataId = 0;
GST_OBJECT_UNLOCK(src);
return FALSE;
}
static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer userData)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
WebKitMediaSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Have enough data");
GST_OBJECT_LOCK(src);
if (priv->sourceVideo.enoughDataId || priv->sourceVideo.paused) {
GST_OBJECT_UNLOCK(src);
return;
}
priv->sourceVideo.enoughDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceVideo.enoughDataId, "[WebKit] webKitMediaVideoSrcEnoughDataMainCb");
GST_OBJECT_UNLOCK(src);
}
static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer userData)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
WebKitMediaSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Have enough data");
GST_OBJECT_LOCK(src);
if (priv->sourceAudio.enoughDataId || priv->sourceAudio.paused) {
GST_OBJECT_UNLOCK(src);
return;
}
priv->sourceAudio.enoughDataId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceAudio.enoughDataId, "[WebKit] webKitMediaAudioSrcEnoughDataMainCb");
GST_OBJECT_UNLOCK(src);
}
static gboolean webKitMediaVideoSrcSeekMainCb(WebKitMediaSrc*)
{
notImplemented();
return FALSE;
}
static gboolean webKitMediaAudioSrcSeekMainCb(WebKitMediaSrc*)
{
notImplemented();
return FALSE;
}
static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
WebKitMediaSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
GST_OBJECT_LOCK(src);
if (offset == priv->sourceVideo.offset && priv->sourceVideo.requestedOffset == priv->sourceVideo.offset) {
GST_OBJECT_UNLOCK(src);
return TRUE;
}
if (!priv->seekable) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
if (offset > priv->sourceVideo.size) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
GST_DEBUG_OBJECT(src, "Doing range-request seek");
priv->sourceVideo.requestedOffset = offset;
if (priv->sourceVideo.seekId)
g_source_remove(priv->sourceVideo.seekId);
priv->sourceVideo.seekId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaVideoSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceVideo.seekId, "[WebKit] webKitMediaVideoSrcSeekMainCb");
GST_OBJECT_UNLOCK(src);
return TRUE;
}
static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
{
WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
WebKitMediaSrcPrivate* priv = src->priv;
GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
GST_OBJECT_LOCK(src);
if (offset == priv->sourceAudio.offset && priv->sourceAudio.requestedOffset == priv->sourceAudio.offset) {
GST_OBJECT_UNLOCK(src);
return TRUE;
}
if (!priv->seekable) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
if (offset > priv->sourceAudio.size) {
GST_OBJECT_UNLOCK(src);
return FALSE;
}
GST_DEBUG_OBJECT(src, "Doing range-request seek");
priv->sourceAudio.requestedOffset = offset;
if (priv->sourceAudio.seekId)
g_source_remove(priv->sourceAudio.seekId);
priv->sourceAudio.seekId = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitMediaAudioSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
g_source_set_name_by_id(priv->sourceAudio.seekId, "[WebKit] webKitMediaAudioSrcSeekMainCb");
GST_OBJECT_UNLOCK(src);
return TRUE;
}
void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc* src, WebCore::MediaPlayer* player)
{
WebKitMediaSrcPrivate* priv = src->priv;
priv->player = player;
}
void webKitMediaSrcSetPlayBin(WebKitMediaSrc* src, GstElement* playBin)
{
WebKitMediaSrcPrivate* priv = src->priv;
priv->playbin = playBin;
}
MediaSourceClientGstreamer::MediaSourceClientGstreamer(WebKitMediaSrc* src)
: m_src(static_cast<WebKitMediaSrc*>(gst_object_ref(src)))
{
}
MediaSourceClientGstreamer::~MediaSourceClientGstreamer()
{
gst_object_unref(m_src);
}
void MediaSourceClientGstreamer::didReceiveDuration(double duration)
{
WebKitMediaSrcPrivate* priv = m_src->priv;
GST_DEBUG_OBJECT(m_src, "Received duration: %lf", duration);
GST_OBJECT_LOCK(m_src);
priv->duration = duration >= 0.0 ? static_cast<gint64>(duration*GST_SECOND) : 0;
GST_OBJECT_UNLOCK(m_src);
}
void MediaSourceClientGstreamer::didReceiveData(const char* data, int length, String type)
{
WebKitMediaSrcPrivate* priv = m_src->priv;
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer * buffer;
if (type.startsWith("video")) {
if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == TRUE) {
gst_element_no_more_pads(GST_ELEMENT(m_src));
priv->noMorePad = TRUE;
}
if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == FALSE) {
gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceVideo.srcpad);
priv->sourceVideo.padAdded = TRUE;
}
GST_OBJECT_LOCK(m_src);
buffer = WebCore::createGstBufferForData(data, length);
GST_OBJECT_UNLOCK(m_src);
ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceVideo.appsrc), buffer);
} else if (type.startsWith("audio")) {
if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == TRUE) {
gst_element_no_more_pads(GST_ELEMENT(m_src));
priv->noMorePad = TRUE;
}
if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == FALSE) {
gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceAudio.srcpad);
priv->sourceAudio.padAdded = TRUE;
}
GST_OBJECT_LOCK(m_src);
buffer = WebCore::createGstBufferForData(data, length);
GST_OBJECT_UNLOCK(m_src);
ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceAudio.appsrc), buffer);
}
if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0));
}
void MediaSourceClientGstreamer::didFinishLoading(double)
{
WebKitMediaSrcPrivate* priv = m_src->priv;
GST_DEBUG_OBJECT(m_src, "Have EOS");
GST_OBJECT_LOCK(m_src);
if (!priv->sourceVideo.seekId) {
GST_OBJECT_UNLOCK(m_src);
gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceVideo.appsrc));
} else
GST_OBJECT_UNLOCK(m_src);
GST_OBJECT_LOCK(m_src);
if (!priv->sourceAudio.seekId) {
GST_OBJECT_UNLOCK(m_src);
gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceAudio.appsrc));
} else
GST_OBJECT_UNLOCK(m_src);
}
void MediaSourceClientGstreamer::didFail()
{
gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceVideo.appsrc));
gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceAudio.appsrc));
}
#endif // USE(GSTREAMER)