summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp')
-rw-r--r--Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp222
1 files changed, 108 insertions, 114 deletions
diff --git a/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp b/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp
index ff672f371..445c9793c 100644
--- a/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp
+++ b/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011, 2012 Igalia S.L
+ * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -26,9 +27,10 @@
#include "AudioIOCallback.h"
#include "GRefPtrGStreamer.h"
#include "GStreamerUtilities.h"
-#include <gst/audio/audio.h>
-#include <gst/pbutils/pbutils.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/audio/audio-info.h>
+#include <gst/pbutils/missing-plugins.h>
+#include <wtf/glib/GUniquePtr.h>
using namespace WebCore;
@@ -51,18 +53,22 @@ struct _WebKitWebAudioSourcePrivate {
AudioBus* bus;
AudioIOCallback* provider;
guint framesToPull;
+ guint bufferSize;
GRefPtr<GstElement> interleave;
- GRefPtr<GstElement> wavEncoder;
GRefPtr<GstTask> task;
GRecMutex mutex;
- GSList* pads; // List of queue sink pads. One queue for each planar audio channel.
- GstPad* sourcePad; // src pad of the element, interleaved wav data is pushed to it.
+ // List of appsrc. One appsrc for each planar audio channel.
+ Vector<GRefPtr<GstElement>> sources;
- bool newStreamEventPending;
- GstSegment segment;
+ // src pad of the element, interleaved wav data is pushed to it.
+ GstPad* sourcePad;
+
+ guint64 numberOfSamples;
+
+ GRefPtr<GstBufferPool> pool;
};
enum {
@@ -73,9 +79,9 @@ enum {
};
static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS("audio/x-wav"));
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS(GST_AUDIO_CAPS_MAKE(GST_AUDIO_NE(F32))));
GST_DEBUG_CATEGORY_STATIC(webkit_web_audio_src_debug);
#define GST_CAT_DEFAULT webkit_web_audio_src_debug
@@ -91,8 +97,8 @@ static GstCaps* getGStreamerMonoAudioCaps(float sampleRate)
{
return gst_caps_new_simple("audio/x-raw", "rate", G_TYPE_INT, static_cast<int>(sampleRate),
"channels", G_TYPE_INT, 1,
- "format", G_TYPE_STRING, gst_audio_format_to_string(GST_AUDIO_FORMAT_F32),
- "layout", G_TYPE_STRING, "non-interleaved", NULL);
+ "format", G_TYPE_STRING, GST_AUDIO_NE(F32),
+ "layout", G_TYPE_STRING, "interleaved", nullptr);
}
static GstAudioChannelPosition webKitWebAudioGStreamerChannelPosition(int channelIndex)
@@ -178,17 +184,14 @@ static void webkit_web_audio_src_init(WebKitWebAudioSrc* src)
src->priv = priv;
new (priv) WebKitWebAudioSourcePrivate();
- priv->sourcePad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", 0);
+ priv->sourcePad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", nullptr);
gst_element_add_pad(GST_ELEMENT(src), priv->sourcePad);
- priv->provider = 0;
- priv->bus = 0;
-
- priv->newStreamEventPending = true;
- gst_segment_init(&priv->segment, GST_FORMAT_TIME);
+ priv->provider = nullptr;
+ priv->bus = nullptr;
g_rec_mutex_init(&priv->mutex);
- priv->task = gst_task_new(reinterpret_cast<GstTaskFunction>(webKitWebAudioSrcLoop), src, 0);
+ priv->task = adoptGRef(gst_task_new(reinterpret_cast<GstTaskFunction>(webKitWebAudioSrcLoop), src, nullptr));
gst_task_set_lock(priv->task.get(), &priv->mutex);
}
@@ -202,54 +205,40 @@ static void webKitWebAudioSrcConstructed(GObject* object)
ASSERT(priv->provider);
ASSERT(priv->sampleRate);
- priv->interleave = gst_element_factory_make("interleave", 0);
- priv->wavEncoder = gst_element_factory_make("wavenc", 0);
+ priv->interleave = gst_element_factory_make("interleave", nullptr);
if (!priv->interleave) {
GST_ERROR_OBJECT(src, "Failed to create interleave");
return;
}
- if (!priv->wavEncoder) {
- GST_ERROR_OBJECT(src, "Failed to create wavenc");
- return;
- }
-
- gst_bin_add_many(GST_BIN(src), priv->interleave.get(), priv->wavEncoder.get(), NULL);
- gst_element_link_pads_full(priv->interleave.get(), "src", priv->wavEncoder.get(), "sink", GST_PAD_LINK_CHECK_NOTHING);
+ gst_bin_add(GST_BIN(src), priv->interleave.get());
// For each channel of the bus create a new upstream branch for interleave, like:
- // queue ! capsfilter ! audioconvert. which is plugged to a new interleave request sinkpad.
+ // appsrc ! . which is plugged to a new interleave request sinkpad.
for (unsigned channelIndex = 0; channelIndex < priv->bus->numberOfChannels(); channelIndex++) {
- GUniquePtr<gchar> queueName(g_strdup_printf("webaudioQueue%u", channelIndex));
- GstElement* queue = gst_element_factory_make("queue", queueName.get());
- GstElement* capsfilter = gst_element_factory_make("capsfilter", 0);
- GstElement* audioconvert = gst_element_factory_make("audioconvert", 0);
-
+ GUniquePtr<gchar> appsrcName(g_strdup_printf("webaudioSrc%u", channelIndex));
+ GRefPtr<GstElement> appsrc = gst_element_factory_make("appsrc", appsrcName.get());
GRefPtr<GstCaps> monoCaps = adoptGRef(getGStreamerMonoAudioCaps(priv->sampleRate));
GstAudioInfo info;
gst_audio_info_from_caps(&info, monoCaps.get());
GST_AUDIO_INFO_POSITION(&info, 0) = webKitWebAudioGStreamerChannelPosition(channelIndex);
GRefPtr<GstCaps> caps = adoptGRef(gst_audio_info_to_caps(&info));
- g_object_set(capsfilter, "caps", caps.get(), NULL);
-
- // Configure the queue for minimal latency.
- g_object_set(queue, "max-size-buffers", static_cast<guint>(1), NULL);
- GstPad* pad = gst_element_get_static_pad(queue, "sink");
- priv->pads = g_slist_prepend(priv->pads, pad);
+ // Configure the appsrc for minimal latency.
+ g_object_set(appsrc.get(), "max-bytes", static_cast<guint64>(2 * priv->bufferSize), "block", TRUE,
+ "blocksize", priv->bufferSize,
+ "format", GST_FORMAT_TIME, "caps", caps.get(), nullptr);
- gst_bin_add_many(GST_BIN(src), queue, capsfilter, audioconvert, NULL);
- gst_element_link_pads_full(queue, "src", capsfilter, "sink", GST_PAD_LINK_CHECK_NOTHING);
- gst_element_link_pads_full(capsfilter, "src", audioconvert, "sink", GST_PAD_LINK_CHECK_NOTHING);
- gst_element_link_pads_full(audioconvert, "src", priv->interleave.get(), 0, GST_PAD_LINK_CHECK_NOTHING);
+ priv->sources.append(appsrc);
+ gst_bin_add(GST_BIN(src), appsrc.get());
+ gst_element_link_pads_full(appsrc.get(), "src", priv->interleave.get(), "sink_%u", GST_PAD_LINK_CHECK_NOTHING);
}
- priv->pads = g_slist_reverse(priv->pads);
- // wavenc's src pad is the only visible pad of our element.
- GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(priv->wavEncoder.get(), "src"));
+ // interleave's src pad is the only visible pad of our element.
+ GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(priv->interleave.get(), "src"));
gst_ghost_pad_set_target(GST_GHOST_PAD(priv->sourcePad), targetPad.get());
}
@@ -260,8 +249,6 @@ static void webKitWebAudioSrcFinalize(GObject* object)
g_rec_mutex_clear(&priv->mutex);
- g_slist_free_full(priv->pads, reinterpret_cast<GDestroyNotify>(gst_object_unref));
-
priv->~WebKitWebAudioSourcePrivate();
GST_CALL_PARENT(G_OBJECT_CLASS, finalize, ((GObject* )(src)));
}
@@ -283,6 +270,7 @@ static void webKitWebAudioSrcSetProperty(GObject* object, guint propertyId, cons
break;
case PROP_FRAMES:
priv->framesToPull = g_value_get_uint(value);
+ priv->bufferSize = sizeof(float) * priv->framesToPull;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec);
@@ -320,68 +308,65 @@ static void webKitWebAudioSrcLoop(WebKitWebAudioSrc* src)
ASSERT(priv->bus);
ASSERT(priv->provider);
- if (!priv->provider || !priv->bus)
+ if (!priv->provider || !priv->bus) {
+ GST_ELEMENT_ERROR(src, CORE, FAILED, ("Internal WebAudioSrc error"), ("Can't start without provider or bus"));
+ gst_task_stop(src->priv->task.get());
return;
-
- GSList* channelBufferList = 0;
- register int i;
- unsigned bufferSize = priv->framesToPull * sizeof(float);
- for (i = g_slist_length(priv->pads) - 1; i >= 0; i--) {
- GstBuffer* channelBuffer = gst_buffer_new_and_alloc(bufferSize);
- ASSERT(channelBuffer);
- channelBufferList = g_slist_prepend(channelBufferList, channelBuffer);
- GstMapInfo info;
- gst_buffer_map(channelBuffer, &info, GST_MAP_READ);
- priv->bus->setChannelMemory(i, reinterpret_cast<float*>(info.data), priv->framesToPull);
- gst_buffer_unmap(channelBuffer, &info);
}
- // FIXME: Add support for local/live audio input.
- priv->provider->render(0, priv->bus, priv->framesToPull);
-
- GSList* padsIt = priv->pads;
- GSList* buffersIt = channelBufferList;
-
-#if GST_CHECK_VERSION(1, 2, 0)
- guint groupId = 0;
- if (priv->newStreamEventPending)
- groupId = gst_util_group_id_next();
-#endif
-
- for (i = 0; padsIt && buffersIt; padsIt = g_slist_next(padsIt), buffersIt = g_slist_next(buffersIt), ++i) {
- GstPad* pad = static_cast<GstPad*>(padsIt->data);
- GstBuffer* channelBuffer = static_cast<GstBuffer*>(buffersIt->data);
-
- // Send stream-start, segment and caps events downstream, along with the first buffer.
- if (priv->newStreamEventPending) {
- GRefPtr<GstElement> queue = adoptGRef(gst_pad_get_parent_element(pad));
- GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(queue.get(), "sink"));
- GUniquePtr<gchar> queueName(gst_element_get_name(queue.get()));
- GUniquePtr<gchar> streamId(g_strdup_printf("webaudio/%s", queueName.get()));
- GstEvent* streamStartEvent = gst_event_new_stream_start(streamId.get());
-#if GST_CHECK_VERSION(1, 2, 0)
- gst_event_set_group_id(streamStartEvent, groupId);
-#endif
- gst_pad_send_event(sinkPad.get(), streamStartEvent);
-
- GRefPtr<GstCaps> monoCaps = adoptGRef(getGStreamerMonoAudioCaps(priv->sampleRate));
- GstAudioInfo info;
- gst_audio_info_from_caps(&info, monoCaps.get());
- GST_AUDIO_INFO_POSITION(&info, 0) = webKitWebAudioGStreamerChannelPosition(i);
- GRefPtr<GstCaps> capsWithChannelPosition = adoptGRef(gst_audio_info_to_caps(&info));
- gst_pad_send_event(sinkPad.get(), gst_event_new_caps(capsWithChannelPosition.get()));
-
- gst_pad_send_event(sinkPad.get(), gst_event_new_segment(&priv->segment));
+ ASSERT(priv->pool);
+ GstClockTime timestamp = gst_util_uint64_scale(priv->numberOfSamples, GST_SECOND, priv->sampleRate);
+ priv->numberOfSamples += priv->framesToPull;
+ GstClockTime duration = gst_util_uint64_scale(priv->numberOfSamples, GST_SECOND, priv->sampleRate) - timestamp;
+
+ Vector<GRefPtr<GstBuffer>> channelBufferList;
+ channelBufferList.reserveInitialCapacity(priv->sources.size());
+ for (unsigned i = 0; i < priv->sources.size(); ++i) {
+ GRefPtr<GstBuffer> buffer;
+ GstFlowReturn ret = gst_buffer_pool_acquire_buffer(priv->pool.get(), &buffer.outPtr(), nullptr);
+ if (ret != GST_FLOW_OK) {
+ for (auto& buffer : channelBufferList)
+ unmapGstBuffer(buffer.get());
+
+ // FLUSHING and EOS are not errors.
+ if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED)
+ GST_ELEMENT_ERROR(src, CORE, PAD, ("Internal WebAudioSrc error"), ("Failed to allocate buffer for flow: %s", gst_flow_get_name(ret)));
+ gst_task_stop(src->priv->task.get());
+ return;
}
- GstFlowReturn ret = gst_pad_chain(pad, channelBuffer);
- if (ret != GST_FLOW_OK)
- GST_ELEMENT_ERROR(src, CORE, PAD, ("Internal WebAudioSrc error"), ("Failed to push buffer on %s:%s flow: %s", GST_DEBUG_PAD_NAME(pad), gst_flow_get_name(ret)));
+ ASSERT(buffer);
+ GST_BUFFER_TIMESTAMP(buffer.get()) = timestamp;
+ GST_BUFFER_DURATION(buffer.get()) = duration;
+ mapGstBuffer(buffer.get(), GST_MAP_READWRITE);
+ priv->bus->setChannelMemory(i, reinterpret_cast<float*>(getGstBufferDataPointer(buffer.get())), priv->framesToPull);
+ channelBufferList.uncheckedAppend(WTFMove(buffer));
}
- priv->newStreamEventPending = false;
-
- g_slist_free(channelBufferList);
+ // FIXME: Add support for local/live audio input.
+ priv->provider->render(nullptr, priv->bus, priv->framesToPull);
+
+ ASSERT(channelBufferList.size() == priv->sources.size());
+ bool failed = false;
+ for (unsigned i = 0; i < priv->sources.size(); ++i) {
+ // Unmap before passing on the buffer.
+ auto& buffer = channelBufferList[i];
+ unmapGstBuffer(buffer.get());
+
+ if (failed)
+ continue;
+
+ auto& appsrc = priv->sources[i];
+ // Leak the buffer ref, because gst_app_src_push_buffer steals it.
+ GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(appsrc.get()), buffer.leakRef());
+ if (ret != GST_FLOW_OK) {
+ // FLUSHING and EOS are not errors.
+ if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED)
+ GST_ELEMENT_ERROR(src, CORE, PAD, ("Internal WebAudioSrc error"), ("Failed to push buffer on %s flow: %s", GST_OBJECT_NAME(appsrc.get()), gst_flow_get_name(ret)));
+ gst_task_stop(src->priv->task.get());
+ failed = true;
+ }
+ }
}
static GstStateChangeReturn webKitWebAudioSrcChangeState(GstElement* element, GstStateChange transition)
@@ -393,14 +378,10 @@ static GstStateChangeReturn webKitWebAudioSrcChangeState(GstElement* element, Gs
case GST_STATE_CHANGE_NULL_TO_READY:
if (!src->priv->interleave) {
gst_element_post_message(element, gst_missing_element_message_new(element, "interleave"));
- GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no interleave"));
- return GST_STATE_CHANGE_FAILURE;
- }
- if (!src->priv->wavEncoder) {
- gst_element_post_message(element, gst_missing_element_message_new(element, "wavenc"));
- GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no wavenc"));
+ GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (nullptr), ("no interleave"));
return GST_STATE_CHANGE_FAILURE;
}
+ src->priv->numberOfSamples = 0;
break;
default:
break;
@@ -413,16 +394,29 @@ static GstStateChangeReturn webKitWebAudioSrcChangeState(GstElement* element, Gs
}
switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
+ case GST_STATE_CHANGE_READY_TO_PAUSED: {
GST_DEBUG_OBJECT(src, "READY->PAUSED");
- if (!gst_task_start(src->priv->task.get()))
+
+ src->priv->pool = gst_buffer_pool_new();
+ GstStructure* config = gst_buffer_pool_get_config(src->priv->pool.get());
+ gst_buffer_pool_config_set_params(config, nullptr, src->priv->bufferSize, 0, 0);
+ gst_buffer_pool_set_config(src->priv->pool.get(), config);
+ if (!gst_buffer_pool_set_active(src->priv->pool.get(), TRUE))
+ returnValue = GST_STATE_CHANGE_FAILURE;
+ else if (!gst_task_start(src->priv->task.get()))
returnValue = GST_STATE_CHANGE_FAILURE;
break;
+ }
case GST_STATE_CHANGE_PAUSED_TO_READY:
- src->priv->newStreamEventPending = true;
GST_DEBUG_OBJECT(src, "PAUSED->READY");
+
+#if GST_CHECK_VERSION(1, 4, 0)
+ gst_buffer_pool_set_flushing(src->priv->pool.get(), TRUE);
+#endif
if (!gst_task_join(src->priv->task.get()))
returnValue = GST_STATE_CHANGE_FAILURE;
+ gst_buffer_pool_set_active(src->priv->pool.get(), FALSE);
+ src->priv->pool = nullptr;
break;
default:
break;