diff --git a/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.h b/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.h index d5fb2c8899f8c..3241bb1112eb7 100644 --- a/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.h +++ b/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.h @@ -26,6 +26,7 @@ #if USE(AVFOUNDATION) +#include "BitrateMode.h" #include #include #include @@ -41,12 +42,28 @@ class WebAudioBufferList; class AudioSampleBufferConverter : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr { public: + +#if ENABLE(WEB_CODECS) + using BitrateMode = BitrateMode; +#else + enum class BitrateMode { + Constant, + Variable + }; +#endif + struct Options { AudioFormatID format { kAudioFormatMPEG4AAC }; std::optional description { }; std::optional outputBitRate { }; bool generateTimestamp { true }; - std::optional preSkip { }; // If not set, let AudioConverter use the default. + std::optional preSkip { }; + std::optional bitrateMode { }; + std::optional packetSize { }; + std::optional complexity { }; + std::optional packetlossperc { }; + std::optional useinbandfec { }; + std::optional usedtx { }; }; static RefPtr create(CMBufferQueueTriggerCallback, void* callbackObject, const Options&); ~AudioSampleBufferConverter(); @@ -60,6 +77,7 @@ class AudioSampleBufferConverter : public ThreadSafeRefCountedAndCanMakeThreadSa RetainPtr takeOutputSampleBuffer(); unsigned bitRate() const; + unsigned preSkip() const { return m_preSkip; } private: AudioSampleBufferConverter(const Options&); @@ -109,6 +127,7 @@ class AudioSampleBufferConverter : public ThreadSafeRefCountedAndCanMakeThreadSa const AudioFormatID m_outputCodecType; const Options m_options; std::atomic m_defaultBitRate { 0 }; + std::atomic m_preSkip { 0 }; }; } diff --git a/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.mm b/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.mm index 850a6f8c24771..ed4d20336fa35 100644 --- a/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.mm +++ b/Source/WebCore/platform/audio/cocoa/AudioSampleBufferConverter.mm @@ -172,11 +172,21 @@ m_destinationFormat.mChannelsPerFrame = m_sourceFormat.mChannelsPerFrame; m_destinationFormat.mSampleRate = m_sourceFormat.mSampleRate; } - if (outputFormatID == kAudioFormatOpus) - m_destinationFormat.mSampleRate = 48000; - UInt32 size = sizeof(m_destinationFormat); - if (auto error = PAL::AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &m_destinationFormat)) { + if (auto error = [&](auto& destinationFormat) { + auto originalDestinationFormat = destinationFormat; + UInt32 size = sizeof(destinationFormat); + auto result = PAL::AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat); + if (result == kAudioCodecUnsupportedFormatError) { + destinationFormat.mSampleRate = 48000; + result = PAL::AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat); + if (originalDestinationFormat.mFramesPerPacket) { + // Adjust mFramesPerPacket to match new sampling rate + destinationFormat.mFramesPerPacket = originalDestinationFormat.mFramesPerPacket / originalDestinationFormat.mSampleRate * destinationFormat.mSampleRate; + } + } + return result; + }(m_destinationFormat)) { RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter AudioFormatGetProperty failed with %d", static_cast(error)); return error; } @@ -209,7 +219,7 @@ } } - size = sizeof(m_sourceFormat); + UInt32 size = sizeof(m_sourceFormat); if (auto error = PAL::AudioConverterGetProperty(m_converter, kAudioConverterCurrentInputStreamDescription, &size, &m_sourceFormat)) { RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter getting kAudioConverterCurrentInputStreamDescription failed with %d", static_cast(error)); return error; @@ -231,8 +241,7 @@ } if (shouldSetDefaultOutputBitRate) { auto outputBitRate = defaultOutputBitRate(m_destinationFormat); - size = sizeof(outputBitRate); - if (auto error = PAL::AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, size, &outputBitRate)) + if (auto error = PAL::AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, sizeof(outputBitRate), &outputBitRate)) RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter setting default kAudioConverterEncodeBitRate failed with %d", static_cast(error)); else m_defaultBitRate = outputBitRate; @@ -250,7 +259,29 @@ UInt32 size = sizeof(primeInfo); if (auto error = PAL::AudioConverterGetProperty(m_converter, kAudioConverterPrimeInfo, &size, &primeInfo)) RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter getting kAudioConverterPrimeInfo failed with %d", static_cast(error)); + else + m_preSkip = primeInfo.leadingFrames; m_remainingPrimeDuration = PAL::CMTimeMake(primeInfo.leadingFrames, m_destinationFormat.mSampleRate); + + if (m_options.bitrateMode) { + UInt32 bitrateMode = *m_options.bitrateMode == BitrateMode::Variable ? kAudioCodecBitRateControlMode_Variable : kAudioCodecBitRateControlMode_Constant; + if (auto error = PAL::AudioConverterSetProperty(m_converter, kAudioCodecPropertyBitRateControlMode, sizeof(bitrateMode), &bitrateMode)) + RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter setting kAudioCodecPropertyBitRateControlMode failed with %d", static_cast(error)); + } + if (m_options.complexity) { + if (auto error = PAL::AudioConverterSetProperty(m_converter, kAudioCodecPropertyQualitySetting, sizeof(*m_options.complexity), &m_options.complexity.value())) + RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter setting kAudioCodecPropertyQualitySetting failed with %d", static_cast(error)); + } + + // Only operational with Opus encoder. + if (m_options.packetlossperc) { + if (auto error = PAL::AudioConverterSetProperty(m_converter, 'plsp', sizeof(*m_options.packetlossperc), &m_options.packetlossperc.value())) + RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter setting packetlossperc failed with %d", static_cast(error)); + } + if (m_options.useinbandfec) { + if (auto error = PAL::AudioConverterSetProperty(m_converter, 'pfec', sizeof(*m_options.useinbandfec), &m_options.useinbandfec.value())) + RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferConverter setting useinbandfec failed with %d", static_cast(error)); + } } if (!m_destinationFormat.mBytesPerPacket) {