Title: [213069] trunk/Source/WebCore
Revision
213069
Author
[email protected]
Date
2017-02-27 08:59:53 -0800 (Mon, 27 Feb 2017)

Log Message

AudioTrackPrivateMediaStreamCocoa should not exclusively lock its read and write threads.
https://bugs.webkit.org/show_bug.cgi?id=168643

Reviewed by Eric Carlson.

Locking the write thread causes the read thread to drop audio samples and generates audible
glitches, and the realtime audio thread backing the read thread should never block. There's
no real reason to lock these threads against one another here; they both rely on the
AudioSampleDataSource and it's CARingBuffer to safely and simultaneously read and write
data.

The one piece which locks previously protected against unsafe access was during creation of
the audio unit.  Without a lock, the audio unit could begin playback after the unit was
created and assigned to m_remoteIOUnit but before the ring buffer was created. To protect
against this possibility, create the unit, set the input and output descriptions, but only
assign the new audio unit to m_remoteIOUnit after the ring buffer has been created and
initialized.

* platform/audio/mac/CAAudioStreamDescription.h:
* platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.cpp:
(WebCore::AudioTrackPrivateMediaStreamCocoa::~AudioTrackPrivateMediaStreamCocoa):
(WebCore::AudioTrackPrivateMediaStreamCocoa::playInternal):
(WebCore::AudioTrackPrivateMediaStreamCocoa::play):
(WebCore::AudioTrackPrivateMediaStreamCocoa::pause):
(WebCore::AudioTrackPrivateMediaStreamCocoa::setVolume):
(WebCore::AudioTrackPrivateMediaStreamCocoa::createAudioUnit):
(WebCore::AudioTrackPrivateMediaStreamCocoa::setupAudioUnit): Renamed to createAudioUnit()
(WebCore::AudioTrackPrivateMediaStreamCocoa::audioSamplesAvailable):
* platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (213068 => 213069)


--- trunk/Source/WebCore/ChangeLog	2017-02-27 16:53:05 UTC (rev 213068)
+++ trunk/Source/WebCore/ChangeLog	2017-02-27 16:59:53 UTC (rev 213069)
@@ -1,3 +1,35 @@
+2017-02-21  Jer Noble  <[email protected]>
+
+        AudioTrackPrivateMediaStreamCocoa should not exclusively lock its read and write threads.
+        https://bugs.webkit.org/show_bug.cgi?id=168643
+
+        Reviewed by Eric Carlson.
+
+        Locking the write thread causes the read thread to drop audio samples and generates audible
+        glitches, and the realtime audio thread backing the read thread should never block. There's
+        no real reason to lock these threads against one another here; they both rely on the
+        AudioSampleDataSource and it's CARingBuffer to safely and simultaneously read and write
+        data.
+
+        The one piece which locks previously protected against unsafe access was during creation of
+        the audio unit.  Without a lock, the audio unit could begin playback after the unit was
+        created and assigned to m_remoteIOUnit but before the ring buffer was created. To protect
+        against this possibility, create the unit, set the input and output descriptions, but only
+        assign the new audio unit to m_remoteIOUnit after the ring buffer has been created and
+        initialized.
+
+        * platform/audio/mac/CAAudioStreamDescription.h:
+        * platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.cpp:
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::~AudioTrackPrivateMediaStreamCocoa):
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::playInternal):
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::play):
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::pause):
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::setVolume):
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::createAudioUnit):
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::setupAudioUnit): Renamed to createAudioUnit()
+        (WebCore::AudioTrackPrivateMediaStreamCocoa::audioSamplesAvailable):
+        * platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.h:
+
 2017-02-27  Youenn Fablet  <[email protected]>
 
         [WebRTC] RealtimOutgoingVideoSource should not need to do image conversion

Modified: trunk/Source/WebCore/platform/audio/mac/CAAudioStreamDescription.h (213068 => 213069)


--- trunk/Source/WebCore/platform/audio/mac/CAAudioStreamDescription.h	2017-02-27 16:53:05 UTC (rev 213068)
+++ trunk/Source/WebCore/platform/audio/mac/CAAudioStreamDescription.h	2017-02-27 16:59:53 UTC (rev 213069)
@@ -73,7 +73,8 @@
     }
     bool operator!=(const AudioStreamDescription& other) { return !operator == (other); }
 
-    const AudioStreamBasicDescription& streamDescription() { return m_streamDescription; }
+    const AudioStreamBasicDescription& streamDescription() const { return m_streamDescription; }
+    AudioStreamBasicDescription& streamDescription() { return m_streamDescription; }
 
 private:
     void calculateFormat();

Modified: trunk/Source/WebCore/platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.cpp (213068 => 213069)


--- trunk/Source/WebCore/platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.cpp	2017-02-27 16:53:05 UTC (rev 213068)
+++ trunk/Source/WebCore/platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.cpp	2017-02-27 16:59:53 UTC (rev 213069)
@@ -48,8 +48,6 @@
 
 AudioTrackPrivateMediaStreamCocoa::~AudioTrackPrivateMediaStreamCocoa()
 {
-    std::lock_guard<Lock> lock(m_internalStateLock);
-
     streamTrack().source().removeObserver(*this);
 
     if (m_dataSource)
@@ -68,8 +66,6 @@
 
 void AudioTrackPrivateMediaStreamCocoa::playInternal()
 {
-    ASSERT(m_internalStateLock.isHeld());
-
     if (m_isPlaying)
         return;
 
@@ -85,14 +81,11 @@
 
 void AudioTrackPrivateMediaStreamCocoa::play()
 {
-    std::lock_guard<Lock> lock(m_internalStateLock);
     playInternal();
 }
 
 void AudioTrackPrivateMediaStreamCocoa::pause()
 {
-    std::lock_guard<Lock> lock(m_internalStateLock);
-
     m_isPlaying = false;
     m_autoPlay = false;
 
@@ -109,9 +102,9 @@
         m_dataSource->setVolume(m_volume);
 }
 
-OSStatus AudioTrackPrivateMediaStreamCocoa::setupAudioUnit()
+AudioComponentInstance AudioTrackPrivateMediaStreamCocoa::createAudioUnit(const CAAudioStreamDescription& inputDescription, CAAudioStreamDescription& outputDescription)
 {
-    ASSERT(m_internalStateLock.isHeld());
+    AudioComponentInstance remoteIOUnit { nullptr };
 
     AudioComponentDescription ioUnitDescription { kAudioUnitType_Output, 0, kAudioUnitManufacturer_Apple, 0, 0 };
 #if PLATFORM(IOS)
@@ -123,69 +116,66 @@
     AudioComponent ioComponent = AudioComponentFindNext(nullptr, &ioUnitDescription);
     ASSERT(ioComponent);
     if (!ioComponent) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to find remote IO unit component", this);
-        return -1;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnit(%p) unable to find remote IO unit component", this);
+        return nullptr;
     }
 
-    OSStatus err = AudioComponentInstanceNew(ioComponent, &m_remoteIOUnit);
+    OSStatus err = AudioComponentInstanceNew(ioComponent, &remoteIOUnit);
     if (err) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to open vpio unit, error %d (%.4s)", this, (int)err, (char*)&err);
-        return -1;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnit(%p) unable to open vpio unit, error %d (%.4s)", this, (int)err, (char*)&err);
+        return nullptr;
     }
 
 #if PLATFORM(IOS)
     UInt32 param = 1;
-    err = AudioUnitSetProperty(m_remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(param));
+    err = AudioUnitSetProperty(remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(param));
     if (err) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to enable vpio unit output, error %d (%.4s)", this, (int)err, (char*)&err);
-        return err;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnit(%p) unable to enable vpio unit output, error %d (%.4s)", this, (int)err, (char*)&err);
+        return nullptr;
     }
 #endif
 
     AURenderCallbackStruct callback = { inputProc, this };
-    err = AudioUnitSetProperty(m_remoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
+    err = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
     if (err) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to set vpio unit speaker proc, error %d (%.4s)", this, (int)err, (char*)&err);
-        return err;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnit(%p) unable to set vpio unit speaker proc, error %d (%.4s)", this, (int)err, (char*)&err);
+        return nullptr;
     }
 
-    AudioStreamBasicDescription outputDescription = { };
     UInt32 size = sizeof(outputDescription);
-    err  = AudioUnitGetProperty(m_remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription, &size);
+    err  = AudioUnitGetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription, &size);
     if (err) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnits(%p) unable to get input stream format, error %d (%.4s)", this, (int)err, (char*)&err);
-        return err;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnits(%p) unable to get input stream format, error %d (%.4s)", this, (int)err, (char*)&err);
+        return nullptr;
     }
 
-    outputDescription = m_inputDescription->streamDescription();
-    outputDescription.mSampleRate = AudioSession::sharedSession().sampleRate();
+    outputDescription = inputDescription;
+    outputDescription.streamDescription().mSampleRate = AudioSession::sharedSession().sampleRate();
 
-    err = AudioUnitSetProperty(m_remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription, sizeof(outputDescription));
+    err = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription.streamDescription(), sizeof(outputDescription.streamDescription()));
     if (err) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnits(%p) unable to set input stream format, error %d (%.4s)", this, (int)err, (char*)&err);
-        return err;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnits(%p) unable to set input stream format, error %d (%.4s)", this, (int)err, (char*)&err);
+        return nullptr;
     }
-    m_outputDescription = std::make_unique<CAAudioStreamDescription>(outputDescription);
 
-    err = AudioUnitInitialize(m_remoteIOUnit);
+    err = AudioUnitInitialize(remoteIOUnit);
     if (err) {
-        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnits(%p) AudioUnitInitialize() failed, error %d (%.4s)", this, (int)err, (char*)&err);
-        return err;
+        LOG(Media, "AudioTrackPrivateMediaStreamCocoa::createAudioUnits(%p) AudioUnitInitialize() failed, error %d (%.4s)", this, (int)err, (char*)&err);
+        return nullptr;
     }
 
     AudioSession::sharedSession().setPreferredBufferSize(renderBufferSize);
 
-    return err;
+    return remoteIOUnit;
 }
 
 void AudioTrackPrivateMediaStreamCocoa::audioSamplesAvailable(const MediaTime& sampleTime, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t sampleCount)
 {
+    // This function is called on a background thread. The following protectedThis object ensures the object is not
+    // destroyed on the main thread before this function exits.
     Ref<AudioTrackPrivateMediaStreamCocoa> protectedThis { *this };
     ASSERT(description.platformDescription().type == PlatformDescription::CAAudioStreamBasicType);
 
-    std::lock_guard<Lock> lock(m_internalStateLock);
-
-    CAAudioStreamDescription streamDescription = toCAAudioStreamDescription(description);
     if (!m_inputDescription || *m_inputDescription != description) {
 
         m_inputDescription = nullptr;
@@ -197,23 +187,26 @@
             m_remoteIOUnit = nullptr;
         }
 
-        m_inputDescription = std::make_unique<CAAudioStreamDescription>(streamDescription);
-        if (setupAudioUnit()) {
-            m_inputDescription = nullptr;
+        CAAudioStreamDescription inputDescription = toCAAudioStreamDescription(description);
+        CAAudioStreamDescription outputDescription;
+
+        auto remoteIOUnit = createAudioUnit(inputDescription, outputDescription);
+        if (!remoteIOUnit)
             return;
-        }
 
+        m_inputDescription = std::make_unique<CAAudioStreamDescription>(inputDescription);
+        m_outputDescription = std::make_unique<CAAudioStreamDescription>(outputDescription);
+
         if (!m_dataSource)
             m_dataSource = AudioSampleDataSource::create(description.sampleRate() * 2);
-        if (!m_dataSource)
-            return;
 
-        if (m_dataSource->setInputFormat(streamDescription))
+        if (m_dataSource->setInputFormat(inputDescription) || m_dataSource->setOutputFormat(outputDescription)) {
+            AudioComponentInstanceDispose(remoteIOUnit);
             return;
-        if (m_dataSource->setOutputFormat(*m_outputDescription.get()))
-            return;
+        }
 
         m_dataSource->setVolume(m_volume);
+        m_remoteIOUnit = remoteIOUnit;
     }
 
     m_dataSource->pushSamples(sampleTime, audioData, sampleCount);
@@ -229,12 +222,10 @@
 
 OSStatus AudioTrackPrivateMediaStreamCocoa::render(UInt32 sampleCount, AudioBufferList& ioData, UInt32 /*inBusNumber*/, const AudioTimeStamp& timeStamp, AudioUnitRenderActionFlags& actionFlags)
 {
+    // This function is called on a high-priority background thread. The following protectedThis object ensures the object is not
+    // destroyed on the main thread before this function exits.
     Ref<AudioTrackPrivateMediaStreamCocoa> protectedThis { *this };
 
-    std::unique_lock<Lock> lock(m_internalStateLock, std::try_to_lock);
-    if (!lock.owns_lock())
-        return kAudioConverterErr_UnspecifiedError;
-
     if (!m_isPlaying || m_muted || !m_dataSource || streamTrack().muted() || streamTrack().ended() || !streamTrack().enabled()) {
         AudioSampleBufferList::zeroABL(ioData, static_cast<size_t>(sampleCount));
         actionFlags = kAudioUnitRenderAction_OutputIsSilence;

Modified: trunk/Source/WebCore/platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.h (213068 => 213069)


--- trunk/Source/WebCore/platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.h	2017-02-27 16:53:05 UTC (rev 213068)
+++ trunk/Source/WebCore/platform/mediastream/mac/AudioTrackPrivateMediaStreamCocoa.h	2017-02-27 16:59:53 UTC (rev 213069)
@@ -30,7 +30,6 @@
 #include "AudioTrackPrivateMediaStream.h"
 #include <AudioToolbox/AudioToolbox.h>
 #include <CoreAudio/CoreAudioTypes.h>
-#include <wtf/Lock.h>
 
 namespace WebCore {
 
@@ -67,7 +66,7 @@
     static OSStatus inputProc(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32 inBusNumber, UInt32 numberOfFrames, AudioBufferList*);
     OSStatus render(UInt32 sampleCount, AudioBufferList&, UInt32 inBusNumber, const AudioTimeStamp&, AudioUnitRenderActionFlags&);
 
-    OSStatus setupAudioUnit();
+    AudioComponentInstance createAudioUnit(const CAAudioStreamDescription& inputDescription, CAAudioStreamDescription& outputDescription);
     void cleanup();
     void zeroBufferList(AudioBufferList&, size_t);
     void playInternal();
@@ -78,7 +77,6 @@
 
     RefPtr<AudioSampleDataSource> m_dataSource;
 
-    Lock m_internalStateLock;
     float m_volume { 1 };
     bool m_isPlaying { false };
     bool m_autoPlay { false };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to