diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp')
-rw-r--r-- | Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp | 222 |
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; |