⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 271197 in webkit


Ignore:
Timestamp:
Jan 6, 2021, 1:56:37 AM (6 years ago)
Author:
Philippe Normand
Message:

REGRESSION[r270947][GStreamer]: Deadlocks audio rendering
https://bugs.webkit.org/show_bug.cgi?id=220112

Reviewed by Chris Dumez.

This patch fixes the mentioned regression by making the webaudiosrc element wait on the
dispatch condition only if it wasn't done synchronously. Additionally several race
conditions are now fixed in the AudioDestination, by ensuring the start and stop completion
handlers are invoked only after the corresponding pipeline state has been reached. Also the
AudioDestination now correctly notifies its parent node of is-playing changes.

  • platform/audio/gstreamer/AudioDestinationGStreamer.cpp:

(WebCore::AudioDestinationGStreamer::AudioDestinationGStreamer):
(WebCore::AudioDestinationGStreamer::~AudioDestinationGStreamer):
(WebCore::AudioDestinationGStreamer::handleMessage):
(WebCore::AudioDestinationGStreamer::start):
(WebCore::AudioDestinationGStreamer::startRendering):
(WebCore::AudioDestinationGStreamer::stop):
(WebCore::AudioDestinationGStreamer::stopRendering):
(WebCore::AudioDestinationGStreamer::notifyStartupResult):
(WebCore::AudioDestinationGStreamer::notifyStopResult):
(WebCore::AudioDestinationGStreamer::notifyIsPlaying):

  • platform/audio/gstreamer/AudioDestinationGStreamer.h:
  • platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp:

(webkit_web_audio_src_class_init):
(webKitWebAudioSrcConstructed):
(webKitWebAudioSrcSetProperty):
(webKitWebAudioSrcGetProperty):
(webKitWebAudioSrcAllocateBuffers):
(webKitWebAudioSrcRenderAndPushFrames):
(webKitWebAudioSrcRenderIteration):
(webKitWebAudioSrcChangeState):
(webkitWebAudioSourceSetDispatchToRenderThreadFunction):

  • platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.h:
  • platform/graphics/gstreamer/GStreamerCommon.cpp:

(WebCore::webkitGstSetElementStateSynchronously):

  • platform/graphics/gstreamer/GStreamerCommon.h:

(WebCore::webkitGstSetElementStateSynchronously):

Location:
trunk/Source/WebCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r271194 r271197  
     12021-01-06  Philippe Normand  <pnormand@igalia.com>
     2
     3        REGRESSION[r270947][GStreamer]: Deadlocks audio rendering
     4        https://bugs.webkit.org/show_bug.cgi?id=220112
     5
     6        Reviewed by Chris Dumez.
     7
     8        This patch fixes the mentioned regression by making the webaudiosrc element wait on the
     9        dispatch condition only if it wasn't done synchronously. Additionally several race
     10        conditions are now fixed in the AudioDestination, by ensuring the start and stop completion
     11        handlers are invoked only after the corresponding pipeline state has been reached. Also the
     12        AudioDestination now correctly notifies its parent node of is-playing changes.
     13
     14        * platform/audio/gstreamer/AudioDestinationGStreamer.cpp:
     15        (WebCore::AudioDestinationGStreamer::AudioDestinationGStreamer):
     16        (WebCore::AudioDestinationGStreamer::~AudioDestinationGStreamer):
     17        (WebCore::AudioDestinationGStreamer::handleMessage):
     18        (WebCore::AudioDestinationGStreamer::start):
     19        (WebCore::AudioDestinationGStreamer::startRendering):
     20        (WebCore::AudioDestinationGStreamer::stop):
     21        (WebCore::AudioDestinationGStreamer::stopRendering):
     22        (WebCore::AudioDestinationGStreamer::notifyStartupResult):
     23        (WebCore::AudioDestinationGStreamer::notifyStopResult):
     24        (WebCore::AudioDestinationGStreamer::notifyIsPlaying):
     25        * platform/audio/gstreamer/AudioDestinationGStreamer.h:
     26        * platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp:
     27        (webkit_web_audio_src_class_init):
     28        (webKitWebAudioSrcConstructed):
     29        (webKitWebAudioSrcSetProperty):
     30        (webKitWebAudioSrcGetProperty):
     31        (webKitWebAudioSrcAllocateBuffers):
     32        (webKitWebAudioSrcRenderAndPushFrames):
     33        (webKitWebAudioSrcRenderIteration):
     34        (webKitWebAudioSrcChangeState):
     35        (webkitWebAudioSourceSetDispatchToRenderThreadFunction):
     36        * platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.h:
     37        * platform/graphics/gstreamer/GStreamerCommon.cpp:
     38        (WebCore::webkitGstSetElementStateSynchronously):
     39        * platform/graphics/gstreamer/GStreamerCommon.h:
     40        (WebCore::webkitGstSetElementStateSynchronously):
     41
    1422021-01-05  Eric Carlson  <eric.carlson@apple.com>
    243
  • trunk/Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.cpp

    r270947 r271197  
    3333#include <gst/audio/gstaudiobasesink.h>
    3434#include <gst/gst.h>
     35#include <wtf/PrintStream.h>
    3536#include <wtf/glib/GUniquePtr.h>
    3637#include <wtf/glib/RunLoopSourcePriority.h>
     38#include <wtf/text/StringConcatenateNumbers.h>
    3739
    3840namespace WebCore {
     
    121123    , m_sampleRate(sampleRate)
    122124{
    123     m_pipeline = gst_pipeline_new("audio-destination");
    124     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
     125    static Atomic<uint32_t> pipelineId;
     126    m_pipeline = gst_pipeline_new(makeString("audio-destination-", pipelineId.exchangeAdd(1)).ascii().data());
     127    auto bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
    125128    ASSERT(bus);
    126129    gst_bus_add_signal_watch_full(bus.get(), RunLoopSourcePriority::RunLoopDispatcher);
    127130    g_signal_connect(bus.get(), "message", G_CALLBACK(messageCallback), this);
    128131
    129     m_src = reinterpret_cast<GstElement*>(g_object_new(WEBKIT_TYPE_WEB_AUDIO_SRC, "rate", sampleRate,
    130         "bus", m_renderBus.get(), "provider", this, "frames", AudioUtilities::renderQuantumSize, nullptr));
     132    m_src = GST_ELEMENT_CAST(g_object_new(WEBKIT_TYPE_WEB_AUDIO_SRC, "rate", sampleRate,
     133        "bus", m_renderBus.get(), "destination", this, "frames", AudioUtilities::renderQuantumSize, nullptr));
    131134
    132135    GRefPtr<GstElement> audioSink = createPlatformAudioSink();
     
    166169AudioDestinationGStreamer::~AudioDestinationGStreamer()
    167170{
    168     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
     171    GST_DEBUG_OBJECT(m_pipeline.get(), "Disposing");
     172    auto bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
    169173    ASSERT(bus);
    170174    g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(messageCallback), this);
     
    172176
    173177    gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
     178    notifyStopResult(true);
    174179}
    175180
     
    193198        g_warning("Error: %d, %s. Debug output: %s", error->code,  error->message, debug.get());
    194199        gst_element_set_state(m_pipeline.get(), GST_STATE_NULL);
    195         m_isPlaying = false;
     200        notifyIsPlaying(false);
    196201        break;
    197202    case GST_MESSAGE_STATE_CHANGED:
     
    201206
    202207            GST_INFO_OBJECT(m_pipeline.get(), "State changed (old: %s, new: %s, pending: %s)",
    203                 gst_element_state_get_name(oldState),
    204                 gst_element_state_get_name(newState),
    205                 gst_element_state_get_name(pending));
     208                gst_element_state_get_name(oldState), gst_element_state_get_name(newState), gst_element_state_get_name(pending));
    206209
    207210            WTF::String dotFileName = makeString(GST_OBJECT_NAME(m_pipeline.get()), '_',
    208                 gst_element_state_get_name(oldState), '_',
    209                 gst_element_state_get_name(newState));
     211                gst_element_state_get_name(oldState), '_', gst_element_state_get_name(newState));
    210212
    211213            GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN_CAST(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.utf8().data());
     
    213215        break;
    214216    default:
     217        GST_DEBUG_OBJECT(m_pipeline.get(), "Unhandled message: %s", GST_MESSAGE_TYPE_NAME(message));
    215218        break;
    216219    }
     
    220223void AudioDestinationGStreamer::start(Function<void(Function<void()>&&)>&& dispatchToRenderThread, CompletionHandler<void(bool)>&& completionHandler)
    221224{
    222     webkitWebAudioSourceSetDispatchToRenderThreadCallback(WEBKIT_WEB_AUDIO_SRC(m_src.get()), WTFMove(dispatchToRenderThread));
     225    webkitWebAudioSourceSetDispatchToRenderThreadFunction(WEBKIT_WEB_AUDIO_SRC(m_src.get()), WTFMove(dispatchToRenderThread));
    223226    startRendering(WTFMove(completionHandler));
    224227}
     
    227230{
    228231    ASSERT(m_audioSinkAvailable);
    229     bool success = false;
    230     if (m_audioSinkAvailable) {
    231         GST_DEBUG("Starting");
    232         if (gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
    233             g_warning("Error: Failed to set pipeline to playing");
    234             m_isPlaying = false;
    235         } else {
    236             m_isPlaying = true;
    237             success = true;
    238         }
    239     }
    240 
    241     callOnMainThread([completionHandler = WTFMove(completionHandler), success]() mutable {
    242         completionHandler(success);
     232    m_startupCompletionHandler = WTFMove(completionHandler);
     233    GST_DEBUG_OBJECT(m_pipeline.get(), "Starting audio rendering, sink %s", m_audioSinkAvailable ? "available" : "not available");
     234
     235    if (m_isPlaying) {
     236        notifyStartupResult(true);
     237        return;
     238    }
     239
     240    if (!m_audioSinkAvailable) {
     241        notifyStartupResult(false);
     242        return;
     243    }
     244
     245    notifyStartupResult(webkitGstSetElementStateSynchronously(m_pipeline.get(), GST_STATE_PLAYING, [this](GstMessage* message) -> bool {
     246        return handleMessage(message);
     247    }));
     248}
     249
     250void AudioDestinationGStreamer::stop(CompletionHandler<void(bool)>&& completionHandler)
     251{
     252    stopRendering(WTFMove(completionHandler));
     253    webkitWebAudioSourceSetDispatchToRenderThreadFunction(WEBKIT_WEB_AUDIO_SRC(m_src.get()), nullptr);
     254}
     255
     256void AudioDestinationGStreamer::stopRendering(CompletionHandler<void(bool)>&& completionHandler)
     257{
     258    ASSERT(m_audioSinkAvailable);
     259    m_stopCompletionHandler = WTFMove(completionHandler);
     260    GST_DEBUG_OBJECT(m_pipeline.get(), "Stopping audio rendering, sink %s", m_audioSinkAvailable ? "available" : "not available");
     261
     262    if (!m_isPlaying) {
     263        GST_DEBUG_OBJECT(m_pipeline.get(), "Already stopped");
     264        notifyStopResult(true);
     265        return;
     266    }
     267
     268    if (!m_audioSinkAvailable) {
     269        notifyStopResult(false);
     270        return;
     271    }
     272
     273    notifyStopResult(webkitGstSetElementStateSynchronously(m_pipeline.get(), GST_STATE_READY, [this](GstMessage* message) -> bool {
     274        return handleMessage(message);
     275    }));
     276}
     277
     278void AudioDestinationGStreamer::notifyStartupResult(bool success)
     279{
     280    callOnMainThreadAndWait([this, completionHandler = WTFMove(m_startupCompletionHandler), success]() mutable {
     281        GST_DEBUG_OBJECT(m_pipeline.get(), "Has start completion handler: %s", boolForPrinting(!!completionHandler));
     282        if (completionHandler)
     283            completionHandler(success);
    243284    });
    244285}
    245286
    246 void AudioDestinationGStreamer::stop(CompletionHandler<void(bool)>&& completionHandler)
    247 {
    248     stopRendering(WTFMove(completionHandler));
    249 }
    250 
    251 void AudioDestinationGStreamer::stopRendering(CompletionHandler<void(bool)>&& completionHandler)
    252 {
    253     ASSERT(m_audioSinkAvailable);
    254     bool success = false;
    255     if (m_audioSinkAvailable) {
    256         GST_DEBUG("Stopping");
    257         gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED);
    258         m_isPlaying = false;
    259         success = true;
    260     }
    261     callOnMainThread([completionHandler = WTFMove(completionHandler), success]() mutable {
    262         completionHandler(success);
     287void AudioDestinationGStreamer::notifyStopResult(bool success)
     288{
     289    if (success)
     290        notifyIsPlaying(false);
     291
     292    callOnMainThreadAndWait([this, completionHandler = WTFMove(m_stopCompletionHandler), success]() mutable {
     293        GST_DEBUG_OBJECT(m_pipeline.get(), "Has stop completion handler: %s", boolForPrinting(!!completionHandler));
     294        if (completionHandler)
     295            completionHandler(success);
    263296    });
    264297}
    265298
     299void AudioDestinationGStreamer::notifyIsPlaying(bool isPlaying)
     300{
     301    if (m_isPlaying == isPlaying)
     302        return;
     303
     304    GST_DEBUG("Is playing: %s", boolForPrinting(isPlaying));
     305    m_isPlaying = isPlaying;
     306    if (m_callback)
     307        m_callback->isPlayingDidChange();
     308}
     309
    266310} // namespace WebCore
    267311
  • trunk/Source/WebCore/platform/audio/gstreamer/AudioDestinationGStreamer.h

    r270947 r271197  
    2222#include "AudioDestination.h"
    2323#include "GRefPtrGStreamer.h"
     24#include <wtf/Condition.h>
    2425#include <wtf/Forward.h>
    25 
    26 typedef struct _GstElement GstElement;
    27 typedef struct _GstPad GstPad;
    28 typedef struct _GstMessage GstMessage;
    2926
    3027namespace WebCore {
     
    4340
    4441    gboolean handleMessage(GstMessage*);
     42    void notifyIsPlaying(bool);
    4543
    4644protected:
     
    4947
    5048private:
     49    void notifyStartupResult(bool);
     50    void notifyStopResult(bool);
     51
    5152    RefPtr<AudioBus> m_renderBus;
    5253
     
    5657    GRefPtr<GstElement> m_pipeline;
    5758    GRefPtr<GstElement> m_src;
     59    CompletionHandler<void(bool)> m_startupCompletionHandler;
     60    CompletionHandler<void(bool)> m_stopCompletionHandler;
     61    Lock m_setStateLock;
     62    Condition m_setStateCondition;
    5863};
    5964
  • trunk/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.cpp

    r270840 r271197  
    2525
    2626#include "AudioBus.h"
    27 #include "AudioDestination.h"
     27#include "AudioDestinationGStreamer.h"
    2828#include "AudioIOCallback.h"
    2929#include "AudioUtilities.h"
     
    6060    gfloat sampleRate;
    6161    AudioBus* bus;
    62     AudioDestination* provider;
     62    AudioDestinationGStreamer* destination;
    6363    guint framesToPull;
    6464    guint bufferSize;
     
    8080
    8181    bool enableGapBufferSupport;
    82 
    83     Optional<Function<void(Function<void()>&&)>> dispatchToRenderThreadCallback;
    84     Lock dispatchMutex;
     82    bool hasRenderedAudibleFrame { false };
     83
     84    Lock dispatchToRenderThreadLock;
     85    Function<void(Function<void()>&&)> dispatchToRenderThreadFunction;
     86
     87    bool dispatchDone;
     88    Lock dispatchLock;
    8589    Condition dispatchCondition;
    8690
     
    106110    PROP_RATE = 1,
    107111    PROP_BUS,
    108     PROP_PROVIDER,
     112    PROP_DESTINATION,
    109113    PROP_FRAMES
    110114};
     
    117121static void webKitWebAudioSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*);
    118122static GstStateChangeReturn webKitWebAudioSrcChangeState(GstElement*, GstStateChange);
    119 static void webKitWebAudioSrcLoop(WebKitWebAudioSrc*);
     123static void webKitWebAudioSrcRenderIteration(WebKitWebAudioSrc*);
    120124
    121125static GstCaps* getGStreamerMonoAudioCaps(float sampleRate)
     
    186190                                                         "Bus", flags));
    187191
    188     g_object_class_install_property(objectClass,
    189                                     PROP_PROVIDER,
    190                                     g_param_spec_pointer("provider", "provider",
    191                                                          "Provider", flags));
     192    g_object_class_install_property(objectClass, PROP_DESTINATION, g_param_spec_pointer("destination", "destination",
     193        "Destination", flags));
    192194
    193195    g_object_class_install_property(objectClass,
     
    204206
    205207    ASSERT(priv->bus);
    206     ASSERT(priv->provider);
     208    ASSERT(priv->destination);
    207209    ASSERT(priv->sampleRate);
    208210
    209211    gst_element_add_pad(GST_ELEMENT(src), priv->sourcePad);
    210212
    211     priv->task = adoptGRef(gst_task_new(reinterpret_cast<GstTaskFunction>(webKitWebAudioSrcLoop), src, nullptr));
     213    priv->task = adoptGRef(gst_task_new(reinterpret_cast<GstTaskFunction>(webKitWebAudioSrcRenderIteration), src, nullptr));
    212214    gst_task_set_lock(priv->task.get(), &priv->mutex);
    213215
     
    261263        priv->bus = static_cast<AudioBus*>(g_value_get_pointer(value));
    262264        break;
    263     case PROP_PROVIDER:
    264         priv->provider = static_cast<AudioDestination*>(g_value_get_pointer(value));
     265    case PROP_DESTINATION:
     266        priv->destination = static_cast<AudioDestinationGStreamer*>(g_value_get_pointer(value));
    265267        break;
    266268    case PROP_FRAMES:
     
    286288        g_value_set_pointer(value, priv->bus);
    287289        break;
    288     case PROP_PROVIDER:
    289         g_value_set_pointer(value, priv->provider);
     290    case PROP_DESTINATION:
     291        g_value_set_pointer(value, priv->destination);
    290292        break;
    291293    case PROP_FRAMES:
     
    298300}
    299301
    300 static Optional<Vector<GRefPtr<GstBuffer>>> webKitWebAudioSrcAllocateBuffersAndRenderAudio(WebKitWebAudioSrc* src)
     302static Optional<Vector<GRefPtr<GstBuffer>>> webKitWebAudioSrcAllocateBuffers(WebKitWebAudioSrc* src)
    301303{
    302304    WebKitWebAudioSrcPrivate* priv = src->priv;
    303305
    304306    ASSERT(priv->bus);
    305     ASSERT(priv->provider);
    306     if (!priv->provider || !priv->bus) {
    307         GST_ELEMENT_ERROR(src, CORE, FAILED, ("Internal WebAudioSrc error"), ("Can't start without provider or bus"));
     307    ASSERT(priv->destination);
     308    if (!priv->destination || !priv->bus) {
     309        GST_ELEMENT_ERROR(src, CORE, FAILED, ("Internal WebAudioSrc error"), ("Can't start without destination or bus"));
    308310        gst_task_stop(src->priv->task.get());
    309311        return WTF::nullopt;
     
    311313
    312314    ASSERT(priv->pool);
    313     GstClockTime timestamp = gst_util_uint64_scale(priv->numberOfSamples, GST_SECOND, priv->sampleRate);
    314     priv->numberOfSamples += priv->framesToPull;
    315     GstClockTime duration = gst_util_uint64_scale(priv->numberOfSamples, GST_SECOND, priv->sampleRate) - timestamp;
    316315
    317316    Vector<GRefPtr<GstBuffer>> channelBufferList;
     
    330329
    331330        ASSERT(buffer);
    332         GST_BUFFER_TIMESTAMP(buffer.get()) = timestamp;
    333         GST_BUFFER_DURATION(buffer.get()) = duration;
    334331        GstMappedBuffer mappedBuffer(buffer.get(), GST_MAP_READWRITE);
    335332        ASSERT(mappedBuffer);
     
    339336    }
    340337
     338    return makeOptional(channelBufferList);
     339}
     340
     341static void webKitWebAudioSrcRenderAndPushFrames(GRefPtr<GstElement>&& element, Vector<GRefPtr<GstBuffer>>&& channelBufferList)
     342{
     343    auto* src = WEBKIT_WEB_AUDIO_SRC(element.get());
     344    auto* priv = src->priv;
     345
     346    ASSERT(channelBufferList.size() == priv->sources.size());
     347
     348    GstClockTime timestamp = gst_util_uint64_scale(priv->numberOfSamples, GST_SECOND, priv->sampleRate);
     349    priv->numberOfSamples += priv->framesToPull;
     350    GstClockTime duration = gst_util_uint64_scale(priv->numberOfSamples, GST_SECOND, priv->sampleRate) - timestamp;
     351
    341352    AudioIOPosition outputTimestamp;
    342     auto clock = adoptGRef(gst_element_get_clock(GST_ELEMENT_CAST(src)));
    343     if (clock) {
     353    if (auto clock = adoptGRef(gst_element_get_clock(element.get()))) {
    344354        auto clockTime = gst_clock_get_time(clock.get());
    345355        outputTimestamp.position = Seconds::fromNanoseconds(timestamp);
     
    348358
    349359    // FIXME: Add support for local/live audio input.
    350 
    351     if (src->priv->dispatchToRenderThreadCallback.hasValue()) {
    352         LockHolder holder(priv->dispatchMutex);
    353         (*priv->dispatchToRenderThreadCallback)([src, outputTimestamp]() mutable {
    354             auto* priv = src->priv;
    355             priv->provider->callRenderCallback(nullptr, priv->bus, priv->framesToPull, outputTimestamp);
    356             priv->dispatchCondition.notifyOne();
    357         });
    358         priv->dispatchCondition.wait(priv->dispatchMutex);
    359     } else
    360         priv->provider->callRenderCallback(nullptr, priv->bus, priv->framesToPull, outputTimestamp);
    361 
    362     return makeOptional(channelBufferList);
    363 }
    364 
    365 static void webKitWebAudioSrcLoop(WebKitWebAudioSrc* src)
    366 {
    367     WebKitWebAudioSrcPrivate* priv = src->priv;
    368 
    369     Optional<Vector<GRefPtr<GstBuffer>>> channelBufferList = webKitWebAudioSrcAllocateBuffersAndRenderAudio(src);
    370     if (!channelBufferList) {
    371         gst_task_stop(src->priv->task.get());
    372         return;
    373     }
    374 
    375     ASSERT(channelBufferList->size() == priv->sources.size());
     360    priv->destination->callRenderCallback(nullptr, priv->bus, priv->framesToPull, outputTimestamp);
     361
     362    if (!priv->hasRenderedAudibleFrame && !priv->bus->isSilent()) {
     363        priv->destination->notifyIsPlaying(true);
     364        priv->hasRenderedAudibleFrame = true;
     365    }
    376366
    377367    bool failed = false;
    378368    for (unsigned i = 0; i < priv->sources.size(); ++i) {
    379         auto& buffer = channelBufferList.value()[i];
     369        auto& buffer = channelBufferList[i];
     370        GST_BUFFER_TIMESTAMP(buffer.get()) = outputTimestamp.position.nanoseconds();
     371        GST_BUFFER_DURATION(buffer.get()) = duration;
    380372
    381373        if (priv->enableGapBufferSupport && priv->bus->channel(i)->isSilent())
     
    392384            if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED)
    393385                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)));
    394             gst_task_stop(src->priv->task.get());
     386            gst_task_stop(priv->task.get());
    395387            failed = true;
    396388        }
    397389    }
     390
     391    {
     392        LockHolder lock(priv->dispatchLock);
     393        priv->dispatchDone = true;
     394        priv->dispatchCondition.notifyOne();
     395    }
     396}
     397
     398static void webKitWebAudioSrcRenderIteration(WebKitWebAudioSrc* src)
     399{
     400    auto* priv = src->priv;
     401    auto channelBufferList = webKitWebAudioSrcAllocateBuffers(src);
     402    if (!channelBufferList) {
     403        gst_task_stop(priv->task.get());
     404        return;
     405    }
     406
     407    {
     408        LockHolder lock(priv->dispatchLock);
     409        priv->dispatchDone = false;
     410    }
     411
     412    auto locker = tryHoldLock(priv->dispatchToRenderThreadLock);
     413    if (!locker || !priv->dispatchToRenderThreadFunction)
     414        return;
     415
     416    priv->dispatchToRenderThreadFunction([channels = WTFMove(*channelBufferList), protectedThis = GRefPtr<GstElement>(GST_ELEMENT_CAST(src))]() mutable {
     417        webKitWebAudioSrcRenderAndPushFrames(WTFMove(protectedThis), WTFMove(channels));
     418    });
     419
     420    {
     421        LockHolder lock(priv->dispatchLock);
     422        if (!priv->dispatchDone)
     423            priv->dispatchCondition.wait(priv->dispatchLock);
     424    }
    398425}
    399426
     
    401428{
    402429    GstStateChangeReturn returnValue = GST_STATE_CHANGE_SUCCESS;
    403     WebKitWebAudioSrc* src = WEBKIT_WEB_AUDIO_SRC(element);
     430    auto* src = WEBKIT_WEB_AUDIO_SRC(element);
     431    auto* priv = src->priv;
     432
     433#if GST_CHECK_VERSION(1, 14, 0)
     434    GST_DEBUG_OBJECT(element, "%s", gst_state_change_get_name(transition));
     435#endif
    404436
    405437    switch (transition) {
    406438    case GST_STATE_CHANGE_NULL_TO_READY:
    407         if (!src->priv->interleave) {
     439        if (!priv->interleave) {
    408440            gst_element_post_message(element, gst_missing_element_message_new(element, "audiointerleave"));
    409441            GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (nullptr), ("no audiointerleave"));
    410442            return GST_STATE_CHANGE_FAILURE;
    411443        }
    412         src->priv->numberOfSamples = 0;
     444        priv->numberOfSamples = 0;
    413445        break;
    414446    default:
     
    424456    switch (transition) {
    425457    case GST_STATE_CHANGE_READY_TO_PAUSED: {
    426         GST_DEBUG_OBJECT(src, "READY->PAUSED");
    427 
    428         src->priv->pool = gst_buffer_pool_new();
    429         GstStructure* config = gst_buffer_pool_get_config(src->priv->pool.get());
    430         gst_buffer_pool_config_set_params(config, nullptr, src->priv->bufferSize, 0, 0);
    431         gst_buffer_pool_set_config(src->priv->pool.get(), config);
    432         if (!gst_buffer_pool_set_active(src->priv->pool.get(), TRUE))
     458        priv->pool = gst_buffer_pool_new();
     459        GstStructure* config = gst_buffer_pool_get_config(priv->pool.get());
     460        gst_buffer_pool_config_set_params(config, nullptr, priv->bufferSize, 0, 0);
     461        gst_buffer_pool_set_config(priv->pool.get(), config);
     462        if (!gst_buffer_pool_set_active(priv->pool.get(), TRUE))
    433463            returnValue = GST_STATE_CHANGE_FAILURE;
    434         else if (!gst_task_start(src->priv->task.get()))
     464        else if (!gst_task_start(priv->task.get()))
    435465            returnValue = GST_STATE_CHANGE_FAILURE;
    436466        break;
    437467    }
    438468    case GST_STATE_CHANGE_PAUSED_TO_READY:
    439         GST_DEBUG_OBJECT(src, "PAUSED->READY");
    440 
    441         gst_buffer_pool_set_flushing(src->priv->pool.get(), TRUE);
    442         if (!gst_task_join(src->priv->task.get()))
     469        {
     470            LockHolder lock(priv->dispatchLock);
     471            priv->dispatchDone = false;
     472            priv->dispatchCondition.notifyAll();
     473        }
     474        gst_buffer_pool_set_flushing(priv->pool.get(), TRUE);
     475        if (!gst_task_join(priv->task.get()))
    443476            returnValue = GST_STATE_CHANGE_FAILURE;
    444         gst_buffer_pool_set_active(src->priv->pool.get(), FALSE);
    445         src->priv->pool = nullptr;
     477
     478        gst_buffer_pool_set_active(priv->pool.get(), FALSE);
     479        priv->pool = nullptr;
    446480        break;
    447481    default:
     
    452486}
    453487
    454 void webkitWebAudioSourceSetDispatchToRenderThreadCallback(WebKitWebAudioSrc* src, Function<void(Function<void()>&&)>&& function)
    455 {
    456     ASSERT(function);
    457     src->priv->dispatchToRenderThreadCallback = WTFMove(function);
     488void webkitWebAudioSourceSetDispatchToRenderThreadFunction(WebKitWebAudioSrc* src, Function<void(Function<void()>&&)>&& function)
     489{
     490    auto locker = holdLock(src->priv->dispatchToRenderThreadLock);
     491    src->priv->dispatchToRenderThreadFunction = WTFMove(function);
    458492}
    459493
  • trunk/Source/WebCore/platform/audio/gstreamer/WebKitWebAudioSourceGStreamer.h

    r268579 r271197  
    3131GType webkit_web_audio_src_get_type();
    3232
    33 void webkitWebAudioSourceSetDispatchToRenderThreadCallback(WebKitWebAudioSrc*, Function<void(Function<void()>&&)>&&);
     33void webkitWebAudioSourceSetDispatchToRenderThreadFunction(WebKitWebAudioSrc*, Function<void(Function<void()>&&)>&&);
    3434
    3535#endif // USE(GSTREAMER)
  • trunk/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.cpp

    r271128 r271197  
    3333#include <gst/gst.h>
    3434#include <mutex>
     35#include <wtf/Scope.h>
    3536#include <wtf/glib/GLibUtilities.h>
    3637#include <wtf/glib/GUniquePtr.h>
     
    455456}
    456457
     458bool webkitGstSetElementStateSynchronously(GstElement* pipeline, GstState targetState, Function<bool(GstMessage*)>&& messageHandler)
     459{
     460    GST_DEBUG_OBJECT(pipeline, "Setting state to %s", gst_element_state_get_name(targetState));
     461
     462    GstState currentState;
     463    auto result = gst_element_get_state(pipeline, &currentState, nullptr, 10);
     464    if (result == GST_STATE_CHANGE_SUCCESS && currentState >= targetState) {
     465        GST_DEBUG_OBJECT(pipeline, "Target state already reached");
     466        return true;
     467    }
     468
     469    auto bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(pipeline)));
     470    gst_bus_enable_sync_message_emission(bus.get());
     471
     472    auto cleanup = makeScopeExit([bus = GRefPtr<GstBus>(bus), pipeline, targetState] {
     473        gst_bus_disable_sync_message_emission(bus.get());
     474        GstState currentState;
     475        auto result = gst_element_get_state(pipeline, &currentState, nullptr, 0);
     476        GST_DEBUG_OBJECT(pipeline, "Task finished, result: %s, target state reached: %s", gst_element_state_change_return_get_name(result), boolForPrinting(currentState == targetState));
     477    });
     478
     479    result = gst_element_set_state(pipeline, targetState);
     480    if (result == GST_STATE_CHANGE_FAILURE)
     481        return false;
     482
     483    if (result == GST_STATE_CHANGE_ASYNC) {
     484        while (auto message = adoptGRef(gst_bus_timed_pop_filtered(bus.get(), GST_CLOCK_TIME_NONE, GST_MESSAGE_STATE_CHANGED))) {
     485            if (!messageHandler(message.get()))
     486                return false;
     487
     488            result = gst_element_get_state(pipeline, &currentState, nullptr, 10);
     489            if (result == GST_STATE_CHANGE_FAILURE)
     490                return false;
     491
     492            if (currentState == targetState)
     493                return true;
     494        }
     495    }
     496    return true;
     497}
     498
    457499}
    458500
  • trunk/Source/WebCore/platform/graphics/gstreamer/GStreamerCommon.h

    r271128 r271197  
    295295GstElement* createPlatformAudioSink();
    296296
     297bool webkitGstSetElementStateSynchronously(GstElement*, GstState, Function<bool(GstMessage*)>&& = [](GstMessage*) -> bool {
     298    return true;
     299});
     300
    297301}
    298302
Note: See TracChangeset for help on using the changeset viewer.