Changeset 249321 in webkit


Ignore:
Timestamp:
Aug 30, 2019 4:02:18 AM (5 years ago)
Author:
cturner@igalia.com
Message:

[GStreamer] Do not ref the player count from background threads.
https://bugs.webkit.org/show_bug.cgi?id=201222

Reviewed by Xabier Rodriguez-Calvar.

Test: imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-retrieve-persistent-license.https.html

In the sync-message handler, a ref() was being taken waiting for a
CDM instance to be attached. This hits asserts since you are not
allowed to ref() an object created on the main thread
(BasePlayer) on a background thread.

The protection condition was overly scoped, tidied up the locking
and made it more granular. To avoid needing to hold a ref() in the
background thread, use instead a semaphore to signal when a CDM
instance is attached, or the player has been destroyed.

Also remove an erroneous safe-guard, the operator= in
isCDMInstanceAvailable will ref() the CDMInstance for us. This use
of holding a reference to CDMInstance in the decryptors is not
thread-safe, and now we have a problem since there's no clean way
to communicate with CDMInstance from background threads without
being thread unsafe. For ClearKey and Widevine, a thread safe
ProxyCDM needs to be designed and passed to background
threads (upcoming patch).

  • platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp:

(WebCore::MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase):
(WebCore::MediaPlayerPrivateGStreamerBase::handleSyncMessage):
(WebCore::MediaPlayerPrivateGStreamerBase::cdmInstanceAttached):
(WebCore::MediaPlayerPrivateGStreamerBase::cdmInstanceDetached):
(WebCore::MediaPlayerPrivateGStreamerBase::handleProtectionEvent):

  • platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h:
  • platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:

(isCDMInstanceAvailable):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r249316 r249321  
     12019-08-30  Charlie Turner  <cturner@igalia.com>
     2
     3        [GStreamer] Do not ref the player count from background threads.
     4        https://bugs.webkit.org/show_bug.cgi?id=201222
     5
     6        Reviewed by Xabier Rodriguez-Calvar.
     7
     8        Test: imported/w3c/web-platform-tests/encrypted-media/clearkey-mp4-playback-retrieve-persistent-license.https.html
     9
     10        In the sync-message handler, a ref() was being taken waiting for a
     11        CDM instance to be attached. This hits asserts since you are not
     12        allowed to ref() an object created on the main thread
     13        (BasePlayer) on a background thread.
     14
     15        The protection condition was overly scoped, tidied up the locking
     16        and made it more granular. To avoid needing to hold a ref() in the
     17        background thread, use instead a semaphore to signal when a CDM
     18        instance is attached, or the player has been destroyed.
     19
     20        Also remove an erroneous safe-guard, the operator= in
     21        isCDMInstanceAvailable will ref() the CDMInstance for us. This use
     22        of holding a reference to CDMInstance in the decryptors is not
     23        thread-safe, and now we have a problem since there's no clean way
     24        to communicate with CDMInstance from background threads without
     25        being thread unsafe. For ClearKey and Widevine, a thread safe
     26        ProxyCDM needs to be designed and passed to background
     27        threads (upcoming patch).
     28
     29        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp:
     30        (WebCore::MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase):
     31        (WebCore::MediaPlayerPrivateGStreamerBase::handleSyncMessage):
     32        (WebCore::MediaPlayerPrivateGStreamerBase::cdmInstanceAttached):
     33        (WebCore::MediaPlayerPrivateGStreamerBase::cdmInstanceDetached):
     34        (WebCore::MediaPlayerPrivateGStreamerBase::handleProtectionEvent):
     35        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h:
     36        * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
     37        (isCDMInstanceAvailable):
     38
    1392019-08-30  Sihui Liu  <sihui_liu@apple.com>
    240
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp

    r249217 r249321  
    245245#endif
    246246
    247 #if ENABLE(ENCRYPTED_MEDIA)
    248     m_protectionCondition.notifyAll();
    249 #endif
    250247    m_notifier->invalidate();
    251248
     
    266263    // waiting there, and ensure that any triggerRepaint call reaching the lock won't wait on m_drawCondition.
    267264    cancelRepaint(true);
     265
     266#if ENABLE(ENCRYPTED_MEDIA)
     267    m_cdmAttachmentSemaphore.signal();
     268#endif
    268269
    269270    // The change to GST_STATE_NULL state is always synchronous. So after this gets executed we don't need to worry
     
    328329        }
    329330        GST_DEBUG_OBJECT(pipeline(), "handling drm-preferred-decryption-system-id need context message");
    330         LockHolder lock(m_protectionMutex);
    331         ProtectionSystemEvents protectionSystemEvents(message);
    332         GST_TRACE("found %zu protection events, %zu decryptors available", protectionSystemEvents.events().size(), protectionSystemEvents.availableSystems().size());
     331
    333332        InitData initData;
    334 
    335         for (auto& event : protectionSystemEvents.events()) {
    336             const char* eventKeySystemId = nullptr;
    337             GstBuffer* data = nullptr;
    338             gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr);
    339 
    340             initData.append({eventKeySystemId, data});
    341             m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
     333        {
     334            LockHolder lock(m_protectionMutex);
     335            ProtectionSystemEvents protectionSystemEvents(message);
     336            GST_TRACE("found %zu protection events, %zu decryptors available", protectionSystemEvents.events().size(), protectionSystemEvents.availableSystems().size());
     337
     338            for (auto& event : protectionSystemEvents.events()) {
     339                const char* eventKeySystemId = nullptr;
     340                GstBuffer* data = nullptr;
     341                gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr);
     342
     343                initData.append({eventKeySystemId, data});
     344                m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
     345            }
    342346        }
    343 
    344347        initializationDataEncountered(WTFMove(initData));
    345348
    346349        GST_INFO_OBJECT(pipeline(), "waiting for a CDM instance");
    347         m_protectionCondition.waitFor(m_protectionMutex, Seconds(4), [this] {
    348             return this->m_cdmInstance;
    349         });
    350 
    351         if (m_cdmInstance && !m_cdmInstance->keySystem().isEmpty()) {
     350        if (m_cdmAttachmentSemaphore.waitFor(4_s)
     351            && m_notifier->isValid() // Check the player is not being destroyed.
     352            && !m_cdmInstance->keySystem().isEmpty()) {
    352353            const char* preferredKeySystemUuid = GStreamerEMEUtilities::keySystemToUuid(m_cdmInstance->keySystem());
    353354            GST_INFO_OBJECT(pipeline(), "working with key system %s, continuing with key system %s on %s", m_cdmInstance->keySystem().utf8().data(), preferredKeySystemUuid, GST_MESSAGE_SRC_NAME(message));
     
    13221323    GST_DEBUG_OBJECT(m_pipeline.get(), "CDM instance %p dispatched as context", m_cdmInstance.get());
    13231324
    1324     m_protectionCondition.notifyAll();
     1325    m_cdmAttachmentSemaphore.signal();
    13251326}
    13261327
     
    13421343    GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-cdm-instance", FALSE));
    13431344    gst_element_set_context(GST_ELEMENT(m_pipeline.get()), context.get());
    1344 
    1345     m_protectionCondition.notifyAll();
    13461345}
    13471346
     
    13611360void MediaPlayerPrivateGStreamerBase::handleProtectionEvent(GstEvent* event)
    13621361{
    1363     if (m_handledProtectionEvents.contains(GST_EVENT_SEQNUM(event))) {
    1364         GST_DEBUG_OBJECT(pipeline(), "event %u already handled", GST_EVENT_SEQNUM(event));
    1365         return;
     1362    {
     1363        LockHolder lock(m_protectionMutex);
     1364        if (m_handledProtectionEvents.contains(GST_EVENT_SEQNUM(event))) {
     1365            GST_DEBUG_OBJECT(pipeline(), "event %u already handled", GST_EVENT_SEQNUM(event));
     1366            return;
     1367        }
    13661368    }
    13671369    GST_DEBUG_OBJECT(pipeline(), "handling event %u from MSE", GST_EVENT_SEQNUM(event));
  • trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h

    r249276 r249321  
    3737#include <wtf/RunLoop.h>
    3838#include <wtf/WeakPtr.h>
     39#include <wtf/threads/BinarySemaphore.h>
    3940
    4041#if USE(GSTREAMER_GL)
     
    301302
    302303#if ENABLE(ENCRYPTED_MEDIA)
    303     Lock m_protectionMutex;
    304     Condition m_protectionCondition;
     304    BinarySemaphore m_cdmAttachmentSemaphore;
    305305    RefPtr<const CDMInstance> m_cdmInstance;
     306
     307    Lock m_protectionMutex; // Guards access to m_handledProtectionEvents.
    306308    HashSet<uint32_t> m_handledProtectionEvents;
     309
    307310    bool m_waitingForKey { false };
    308311#endif
  • trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp

    r242035 r249321  
    294294        if (context) {
    295295            const GValue* value = gst_structure_get_value(gst_context_get_structure(context.get()), "cdm-instance");
    296             // Capture the CDMInstance into a separate variable to avoid missing a refcount.
    297             CDMInstance* instance = value ? reinterpret_cast<CDMInstance*>(g_value_get_pointer(value)) : nullptr;
    298             // ... And force a refcount bump using operator=.
    299             priv->cdmInstance = instance;
     296            priv->cdmInstance = value ? reinterpret_cast<CDMInstance*>(g_value_get_pointer(value)) : nullptr;
    300297            if (priv->cdmInstance)
    301298                GST_DEBUG_OBJECT(self, "received a new CDM instance %p, refcount %u", priv->cdmInstance.get(), priv->cdmInstance->refCount());
Note: See TracChangeset for help on using the changeset viewer.