Title: [239531] trunk/Source/WebCore
Revision
239531
Author
[email protected]
Date
2018-12-21 17:37:00 -0800 (Fri, 21 Dec 2018)

Log Message

'ended' Event doesn't fire on MediaStreamTrack when a USB camera is unplugged
https://bugs.webkit.org/show_bug.cgi?id=187896
<rdar://problem/42681445>

Reviewed by Jer Noble.

Source/WebCore:

No new tests, tested manually.

* platform/mediastream/mac/AVVideoCaptureSource.h:
* platform/mediastream/mac/AVVideoCaptureSource.mm:
(WebCore::AVVideoCaptureSource::deviceDisconnected):
(-[WebCoreAVVideoCaptureSourceObserver addNotificationObservers]):
(-[WebCoreAVVideoCaptureSourceObserver removeNotificationObservers]):
(-[WebCoreAVVideoCaptureSourceObserver deviceConnectedDidChange:]):
* platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp:
(WebCore::deviceHasInputStreams):
(WebCore::isValidCaptureDevice):
(WebCore::CoreAudioCaptureDeviceManager::coreAudioCaptureDevices):
(WebCore::CoreAudioCaptureDeviceManager::refreshAudioCaptureDevices):
(WebCore::CoreAudioCaptureDeviceManager::devicesChanged): Deleted.
* platform/mediastream/mac/CoreAudioCaptureDeviceManager.h:
* platform/mediastream/mac/CoreAudioCaptureSource.cpp:
(WebCore::CoreAudioSharedUnit::setCaptureDevice):
(WebCore::CoreAudioSharedUnit::devicesChanged):
(WebCore::CoreAudioSharedUnit::startProducingData):
(WebCore::CoreAudioSharedUnit::startInternal):
(WebCore::CoreAudioSharedUnit::verifyIsCapturing):
(WebCore::CoreAudioSharedUnit::captureFailed):
(WebCore::CoreAudioCaptureSourceFactory::devicesChanged):
(WebCore::CoreAudioCaptureSource::CoreAudioCaptureSource):
(WebCore::CoreAudioSharedUnit::setCaptureDeviceID): Deleted.
* platform/mediastream/mac/CoreAudioCaptureSource.h:

Source/WebCore/PAL:

* pal/spi/cf/CoreAudioSPI.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (239530 => 239531)


--- trunk/Source/WebCore/ChangeLog	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/ChangeLog	2018-12-22 01:37:00 UTC (rev 239531)
@@ -1,3 +1,38 @@
+2018-12-21  Eric Carlson  <[email protected]>
+
+        'ended' Event doesn't fire on MediaStreamTrack when a USB camera is unplugged
+        https://bugs.webkit.org/show_bug.cgi?id=187896
+        <rdar://problem/42681445>
+
+        Reviewed by Jer Noble.
+
+        No new tests, tested manually.
+
+        * platform/mediastream/mac/AVVideoCaptureSource.h:
+        * platform/mediastream/mac/AVVideoCaptureSource.mm:
+        (WebCore::AVVideoCaptureSource::deviceDisconnected):
+        (-[WebCoreAVVideoCaptureSourceObserver addNotificationObservers]):
+        (-[WebCoreAVVideoCaptureSourceObserver removeNotificationObservers]):
+        (-[WebCoreAVVideoCaptureSourceObserver deviceConnectedDidChange:]):
+        * platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp:
+        (WebCore::deviceHasInputStreams):
+        (WebCore::isValidCaptureDevice):
+        (WebCore::CoreAudioCaptureDeviceManager::coreAudioCaptureDevices):
+        (WebCore::CoreAudioCaptureDeviceManager::refreshAudioCaptureDevices):
+        (WebCore::CoreAudioCaptureDeviceManager::devicesChanged): Deleted.
+        * platform/mediastream/mac/CoreAudioCaptureDeviceManager.h:
+        * platform/mediastream/mac/CoreAudioCaptureSource.cpp:
+        (WebCore::CoreAudioSharedUnit::setCaptureDevice):
+        (WebCore::CoreAudioSharedUnit::devicesChanged):
+        (WebCore::CoreAudioSharedUnit::startProducingData):
+        (WebCore::CoreAudioSharedUnit::startInternal):
+        (WebCore::CoreAudioSharedUnit::verifyIsCapturing):
+        (WebCore::CoreAudioSharedUnit::captureFailed):
+        (WebCore::CoreAudioCaptureSourceFactory::devicesChanged):
+        (WebCore::CoreAudioCaptureSource::CoreAudioCaptureSource):
+        (WebCore::CoreAudioSharedUnit::setCaptureDeviceID): Deleted.
+        * platform/mediastream/mac/CoreAudioCaptureSource.h:
+
 2018-12-20  Ryosuke Niwa  <[email protected]>
 
         REGRESSION(r239353): iOS WK1 Assertion failure in notifyChildNodeRemoved while running

Modified: trunk/Source/WebCore/PAL/ChangeLog (239530 => 239531)


--- trunk/Source/WebCore/PAL/ChangeLog	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/PAL/ChangeLog	2018-12-22 01:37:00 UTC (rev 239531)
@@ -1,3 +1,13 @@
+2018-12-21  Eric Carlson  <[email protected]>
+
+        'ended' Event doesn't fire on MediaStreamTrack when a USB camera is unplugged
+        https://bugs.webkit.org/show_bug.cgi?id=187896
+        <rdar://problem/42681445>
+
+        Reviewed by Jer Noble.
+
+        * pal/spi/cf/CoreAudioSPI.h:
+
 2018-12-19  Chris Dumez  <[email protected]>
 
         wtf/Optional.h: move-constructor and move-assignment operator should disengage the value being moved from

Modified: trunk/Source/WebCore/PAL/pal/spi/cf/CoreAudioSPI.h (239530 => 239531)


--- trunk/Source/WebCore/PAL/pal/spi/cf/CoreAudioSPI.h	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/PAL/pal/spi/cf/CoreAudioSPI.h	2018-12-22 01:37:00 UTC (rev 239531)
@@ -33,6 +33,12 @@
 
 #if PLATFORM(MAC)
 #include <CoreAudio/AudioHardware.h>
+
+CF_ENUM(AudioObjectPropertySelector)
+{
+    kAudioDevicePropertyTapEnabled = 'tapd',
+};
+
 #else
 
 WTF_EXTERN_C_BEGIN
@@ -55,7 +61,8 @@
 
 CF_ENUM(AudioObjectPropertySelector)
 {
-    kAudioHardwarePropertyDefaultInputDevice = 'dIn '
+    kAudioHardwarePropertyDefaultInputDevice = 'dIn ',
+    kAudioDevicePropertyTapEnabled = 'tapd',
 };
 
 CF_ENUM(int)

Modified: trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h (239530 => 239531)


--- trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h	2018-12-22 01:37:00 UTC (rev 239531)
@@ -60,6 +60,7 @@
     enum class InterruptionReason { None, VideoNotAllowedInBackground, AudioInUse, VideoInUse, VideoNotAllowedInSideBySide };
     void captureSessionBeginInterruption(RetainPtr<NSNotification>);
     void captureSessionEndInterruption(RetainPtr<NSNotification>);
+    void deviceDisconnected(RetainPtr<NSNotification>);
 
     AVCaptureSession* session() const { return m_session.get(); }
 

Modified: trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm (239530 => 239531)


--- trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm	2018-12-22 01:37:00 UTC (rev 239531)
@@ -78,6 +78,9 @@
 
 SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeVideo, NSString *)
 
+SOFT_LINK_CONSTANT(AVFoundation, AVCaptureDeviceWasDisconnectedNotification, NSString *)
+#define AVCaptureDeviceWasDisconnectedNotification getAVCaptureDeviceWasDisconnectedNotification()
+
 #if PLATFORM(IOS_FAMILY)
 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionRuntimeErrorNotification, NSString *)
 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVCaptureSessionWasInterruptedNotification, NSString *)
@@ -109,6 +112,7 @@
 -(void)sessionRuntimeError:(NSNotification*)notification;
 -(void)beginSessionInterrupted:(NSNotification*)notification;
 -(void)endSessionInterrupted:(NSNotification*)notification;
+-(void)deviceConnectedDidChange:(NSNotification*)notification;
 #endif
 @end
 
@@ -626,6 +630,13 @@
 }
 #endif
 
+void AVVideoCaptureSource::deviceDisconnected(RetainPtr<NSNotification> notification)
+{
+    if (this->device() == [notification object])
+        captureFailed();
+}
+
+
 } // namespace WebCore
 
 @implementation WebCoreAVVideoCaptureSourceObserver
@@ -650,12 +661,14 @@
 
 - (void)addNotificationObservers
 {
-#if PLATFORM(IOS_FAMILY)
     ASSERT(m_callback);
 
     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+
+    [center addObserver:self selector:@selector(deviceConnectedDidChange:) name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+
+#if PLATFORM(IOS_FAMILY)
     AVCaptureSessionType* session = m_callback->session();
-
     [center addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:session];
     [center addObserver:self selector:@selector(beginSessionInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:session];
     [center addObserver:self selector:@selector(endSessionInterrupted:) name:AVCaptureSessionInterruptionEndedNotification object:session];
@@ -664,9 +677,7 @@
 
 - (void)removeNotificationObservers
 {
-#if PLATFORM(IOS_FAMILY)
     [[NSNotificationCenter defaultCenter] removeObserver:self];
-#endif
 }
 
 - (void)captureOutput:(AVCaptureOutputType*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnectionType*)connection
@@ -704,6 +715,14 @@
         m_callback->captureDeviceSuspendedDidChange();
 }
 
+- (void)deviceConnectedDidChange:(NSNotification*)notification
+{
+    LOG(Media, "WebCoreAVVideoCaptureSourceObserver::deviceConnectedDidChange(%p)", self);
+
+    if (m_callback)
+        m_callback->deviceDisconnected(notification);
+}
+
 #if PLATFORM(IOS_FAMILY)
 - (void)sessionRuntimeError:(NSNotification*)notification
 {

Modified: trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp (239530 => 239531)


--- trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp	2018-12-22 01:37:00 UTC (rev 239531)
@@ -29,10 +29,12 @@
 #if ENABLE(MEDIA_STREAM) && PLATFORM(MAC)
 
 #include "CoreAudioCaptureDevice.h"
+#include "CoreAudioCaptureSource.h"
 #include "Logging.h"
 #include "RealtimeMediaSourceCenter.h"
 #include <AudioUnit/AudioUnit.h>
 #include <CoreMedia/CMSync.h>
+#include <pal/spi/cf/CoreAudioSPI.h>
 #include <wtf/Assertions.h>
 #include <wtf/NeverDestroyed.h>
 
@@ -67,7 +69,6 @@
     UInt32 dataSize = 0;
     AudioObjectPropertyAddress address = { kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyScopeInput, kAudioObjectPropertyElementMaster };
     auto err = AudioObjectGetPropertyDataSize(deviceID, &address, 0, nullptr, &dataSize);
-
     if (err || !dataSize)
         return false;
 
@@ -80,6 +81,23 @@
 
 static bool isValidCaptureDevice(const CoreAudioCaptureDevice& device)
 {
+    // Ignore output devices that have input only for echo cancellation.
+    AudioObjectPropertyAddress address = { kAudioDevicePropertyTapEnabled, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
+    if (AudioObjectHasProperty(device.deviceID(), &address))
+        return false;
+
+    // Ignore non-aggregable devices.
+    UInt32 dataSize = 0;
+    address = { kAudioObjectPropertyCreator, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+    CFStringRef name = nullptr;
+    dataSize = sizeof(name);
+    AudioObjectGetPropertyData(device.deviceID(), &address, 0, nullptr, &dataSize, &name);
+    bool isNonAggregable = !name || !String(name).startsWith("com.apple.audio.CoreAudio");
+    if (name)
+        CFRelease(name);
+    if (isNonAggregable)
+        return false;
+
     // Ignore unnamed devices and aggregate devices created by VPIO.
     return !device.label().isEmpty() && !device.label().startsWith("VPAUAggregateAudioDevice");
 }
@@ -91,8 +109,24 @@
         initialized = true;
         refreshAudioCaptureDevices(DoNotNotify);
 
+        auto weakThis = makeWeakPtr(*this);
+        m_listenerBlock = Block_copy(^(UInt32 count, const AudioObjectPropertyAddress properties[]) {
+            if (!weakThis)
+                return;
+
+            for (UInt32 i = 0; i < count; ++i) {
+                const AudioObjectPropertyAddress& property = properties[i];
+
+                if (property.mSelector != kAudioHardwarePropertyDevices)
+                    continue;
+
+                weakThis->refreshAudioCaptureDevices(Notify);
+                return;
+            }
+        });
+
         AudioObjectPropertyAddress address = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
-        auto err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &address, devicesChanged, this);
+        auto err = AudioObjectAddPropertyListenerBlock(kAudioObjectSystemObject, &address, dispatch_get_main_queue(), m_listenerBlock);
         if (err)
             LOG_ERROR("CoreAudioCaptureDeviceManager::devices(%p) AudioObjectAddPropertyListener returned error %d (%.4s)", this, (int)err, (char*)&err);
     }
@@ -164,16 +198,12 @@
         m_devices.append(captureDevice);
     }
 
-    if (notify == Notify)
+    if (notify == Notify) {
         deviceChanged();
+        CoreAudioCaptureSourceFactory::singleton().devicesChanged(m_devices);
+    }
 }
 
-OSStatus CoreAudioCaptureDeviceManager::devicesChanged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void* userData)
-{
-    static_cast<CoreAudioCaptureDeviceManager*>(userData)->refreshAudioCaptureDevices(Notify);
-    return 0;
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM) && PLATFORM(MAC)

Modified: trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.h (239530 => 239531)


--- trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.h	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.h	2018-12-22 01:37:00 UTC (rev 239531)
@@ -52,7 +52,6 @@
     CoreAudioCaptureDeviceManager() = default;
     ~CoreAudioCaptureDeviceManager() = default;
     
-    static OSStatus devicesChanged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void*);
     Vector<CoreAudioCaptureDevice>& coreAudioCaptureDevices();
 
     enum NotifyIfDevicesHaveChanged { Notify, DoNotNotify };
@@ -60,6 +59,8 @@
 
     Vector<CaptureDevice> m_devices;
     Vector<CoreAudioCaptureDevice> m_coreAudioCaptureDevices;
+
+    AudioObjectPropertyListenerBlock m_listenerBlock;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp (239530 => 239531)


--- trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp	2018-12-22 01:37:00 UTC (rev 239531)
@@ -46,6 +46,7 @@
 #include <pal/avfoundation/MediaTimeAVFoundation.h>
 #include <pal/spi/cf/CoreAudioSPI.h>
 #include <sys/time.h>
+#include <wtf/Algorithms.h>
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
 #include <pal/cf/CoreMediaSoftLink.h>
@@ -102,8 +103,10 @@
 
     bool hasAudioUnit() const { return m_ioUnit; }
 
-    void setCaptureDeviceID(uint32_t);
+    void setCaptureDevice(String&&, uint32_t);
 
+    void devicesChanged(const Vector<CaptureDevice>&);
+
 private:
     OSStatus configureSpeakerProc();
     OSStatus configureMicrophoneProc();
@@ -116,10 +119,12 @@
     static OSStatus speakerCallback(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*);
     OSStatus provideSpeakerData(AudioUnitRenderActionFlags&, const AudioTimeStamp&, UInt32, UInt32, AudioBufferList*);
 
-    void startInternal();
+    OSStatus startInternal();
     void stopInternal();
 
     void verifyIsCapturing();
+    void devicesChanged();
+    void captureFailed();
 
     Vector<std::reference_wrapper<CoreAudioCaptureSource>> m_clients;
 
@@ -162,6 +167,8 @@
     uint64_t m_speakerProcsCalled { 0 };
 #endif
 
+    String m_persistentID;
+
     uint64_t m_microphoneProcsCalled { 0 };
     uint64_t m_microphoneProcsCalledLastTime { 0 };
     Timer m_verifyCapturingTimer;
@@ -197,8 +204,10 @@
     });
 }
 
-void CoreAudioSharedUnit::setCaptureDeviceID(uint32_t captureDeviceID)
+void CoreAudioSharedUnit::setCaptureDevice(String&& persistentID, uint32_t captureDeviceID)
 {
+    m_persistentID = WTFMove(persistentID);
+
 #if PLATFORM(MAC)
     if (m_captureDeviceID == captureDeviceID)
         return;
@@ -210,6 +219,17 @@
 #endif
 }
 
+void CoreAudioSharedUnit::devicesChanged(const Vector<CaptureDevice>& devices)
+{
+    if (!m_ioUnit)
+        return;
+
+    if (WTF::anyOf(devices, [this] (auto& device) { return m_persistentID == device.persistentId(); }))
+        return;
+
+    captureFailed();
+}
+
 void CoreAudioSharedUnit::addEchoCancellationSource(AudioSampleDataSource& source)
 {
     if (!source.setOutputFormat(m_speakerProcFormat)) {
@@ -559,7 +579,8 @@
         ASSERT(!m_ioUnit);
     }
 
-    startInternal();
+    if (startInternal())
+        captureFailed();
 }
 
 OSStatus CoreAudioSharedUnit::resume()
@@ -578,7 +599,7 @@
     return 0;
 }
 
-void CoreAudioSharedUnit::startInternal()
+OSStatus CoreAudioSharedUnit::startInternal()
 {
     OSStatus err;
     if (!m_ioUnit) {
@@ -586,7 +607,7 @@
         if (err) {
             cleanupAudioUnit();
             ASSERT(!m_ioUnit);
-            return;
+            return err;
         }
         ASSERT(m_ioUnit);
     }
@@ -598,7 +619,7 @@
     err = AudioOutputUnitStart(m_ioUnit);
     if (err) {
         RELEASE_LOG_ERROR(Media, "CoreAudioSharedUnit::start(%p) AudioOutputUnitStart failed with error %d (%.4s)", this, (int)err, (char*)&err);
-        return;
+        return err;
     }
 
     m_ioUnitStarted = true;
@@ -606,6 +627,8 @@
     m_verifyCapturingTimer.startRepeating(10_s);
     m_microphoneProcsCalled = 0;
     m_microphoneProcsCalledLastTime = 0;
+
+    return 0;
 }
 
 void CoreAudioSharedUnit::verifyIsCapturing()
@@ -617,8 +640,14 @@
         return;
     }
 
+    captureFailed();
+}
+
+
+void CoreAudioSharedUnit::captureFailed()
+{
 #if !RELEASE_LOG_DISABLED
-    RELEASE_LOG_ERROR(Media, "CoreAudioSharedUnit::verifyIsCapturing - capture failed\n");
+    RELEASE_LOG_ERROR(Media, "CoreAudioSharedUnit::captureFailed - capture failed\n");
 #endif
     for (CoreAudioCaptureSource& client : m_clients)
         client.captureFailed();
@@ -783,12 +812,17 @@
 #endif
 }
 
-CoreAudioCaptureSource::CoreAudioCaptureSource(String&& deviceID, String&& label, String&& hashSalt, uint32_t persistentID)
+void CoreAudioCaptureSourceFactory::devicesChanged(const Vector<CaptureDevice>& devices)
+{
+    CoreAudioSharedUnit::singleton().devicesChanged(devices);
+}
+
+CoreAudioCaptureSource::CoreAudioCaptureSource(String&& deviceID, String&& label, String&& hashSalt, uint32_t captureDeviceID)
     : RealtimeMediaSource(RealtimeMediaSource::Type::Audio, WTFMove(label), WTFMove(deviceID), WTFMove(hashSalt))
-    , m_captureDeviceID(persistentID)
+    , m_captureDeviceID(captureDeviceID)
 {
     auto& unit = CoreAudioSharedUnit::singleton();
-    unit.setCaptureDeviceID(m_captureDeviceID);
+    unit.setCaptureDevice(String { persistentID() }, m_captureDeviceID);
 
     initializeEchoCancellation(unit.enableEchoCancellation());
     initializeSampleRate(unit.sampleRate());

Modified: trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h (239530 => 239531)


--- trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h	2018-12-22 00:39:04 UTC (rev 239530)
+++ trunk/Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.h	2018-12-22 01:37:00 UTC (rev 239531)
@@ -117,6 +117,8 @@
     void endInterruption();
     void scheduleReconfiguration();
 
+    void devicesChanged(const Vector<CaptureDevice>&);
+
 #if PLATFORM(IOS_FAMILY)
     void setCoreAudioActiveSource(CoreAudioCaptureSource& source) { setActiveSource(source); }
     void unsetCoreAudioActiveSource(CoreAudioCaptureSource& source) { unsetActiveSource(source); }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to