|
1642 | 1642 | RTC_DCHECK_RUN_ON(thread_); |
1643 | 1643 | RTC_DCHECK(engine_manual_input_ == nullptr); |
1644 | 1644 |
|
| 1645 | + std::vector<std::function<void()>> rollback_actions; |
| 1646 | + |
| 1647 | + auto rollback = [&](int32_t result) { |
| 1648 | + for (auto& action : rollback_actions) { |
| 1649 | + action(); |
| 1650 | + } |
| 1651 | + |
| 1652 | + return result; |
| 1653 | + }; |
| 1654 | + |
1645 | 1655 | auto inputNode = [this, state]() { |
1646 | 1656 | RTC_DCHECK_RUN_ON(thread_); |
1647 | 1657 | RTC_DCHECK(engine_device_ != nil); |
|
1656 | 1666 | return engine_device_.outputNode; |
1657 | 1667 | }; |
1658 | 1668 |
|
| 1669 | + // -------------------------------------------------------------------------------------------- |
| 1670 | + // Step: Stop AVAudioEngine |
| 1671 | + // |
1659 | 1672 | if (state.prev.IsAnyRunning() && |
1660 | 1673 | (!state.next.IsAnyRunning() || state.IsEngineRestartRequired() || |
1661 | 1674 | state.DidBeginInterruption() || state.IsEngineRecreateRequired())) { |
|
1677 | 1690 | state.next.IsInputEnabled()); |
1678 | 1691 | if (result != 0) { |
1679 | 1692 | LOGE() << "Call to OnEngineDidStop returned error: " << result; |
1680 | | - return result; |
| 1693 | + return rollback(result); |
1681 | 1694 | } |
1682 | 1695 | } |
1683 | 1696 | } |
1684 | 1697 |
|
| 1698 | + // -------------------------------------------------------------------------------------------- |
| 1699 | + // Step: Recreate AVAudioEngine |
| 1700 | + // |
1685 | 1701 | if (state.IsEngineRecreateRequired()) { |
1686 | 1702 | LOGI() << "Recreate required, releasing AVAudioEngine..."; |
1687 | 1703 | if (observer_ != nullptr) { |
1688 | 1704 | int32_t result = observer_->OnEngineWillRelease(engine_device_); |
1689 | 1705 | if (result != 0) { |
1690 | 1706 | LOGE() << "Call to OnEngineWillRelease returned error: " << result; |
1691 | | - return result; |
| 1707 | + return rollback(result); |
1692 | 1708 | } |
1693 | 1709 | } |
1694 | 1710 | engine_device_ = nil; |
1695 | 1711 | } |
1696 | 1712 |
|
| 1713 | + // -------------------------------------------------------------------------------------------- |
| 1714 | + // Step: Create AVAudioEngine |
| 1715 | + // |
1697 | 1716 | if (state.next.IsAnyEnabled() && |
1698 | 1717 | (!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired())) { |
1699 | 1718 | LOGI() << "Creating AVAudioEngine (device)..."; |
1700 | 1719 | RTC_DCHECK(engine_device_ == nil); |
1701 | 1720 |
|
1702 | 1721 | engine_device_ = [[AVAudioEngine alloc] init]; |
1703 | 1722 |
|
| 1723 | + rollback_actions.push_back([=]() { |
| 1724 | + RTC_DCHECK_RUN_ON(thread_); |
| 1725 | + LOGI() << "Rolling back create AVAudioEngine (device)..."; |
| 1726 | + engine_device_ = nil; |
| 1727 | + }); |
| 1728 | + |
1704 | 1729 | if (observer_ != nullptr) { |
1705 | 1730 | int32_t result = observer_->OnEngineDidCreate(engine_device_); |
1706 | 1731 | if (result != 0) { |
1707 | 1732 | LOGE() << "Call to OnEngineDidCreate returned error: " << result; |
1708 | | - return result; |
| 1733 | + return rollback(result); |
1709 | 1734 | } |
1710 | 1735 | } |
1711 | 1736 | } |
1712 | 1737 |
|
| 1738 | + // -------------------------------------------------------------------------------------------- |
| 1739 | + // Step: Stop playout buffer |
| 1740 | + // |
1713 | 1741 | if (!state.next.IsOutputEnabled() && audio_device_buffer_->IsPlaying()) { |
1714 | 1742 | LOGI() << "Stopping Playout buffer..."; |
1715 | 1743 | if (engine_device_ != nullptr) { |
|
1719 | 1747 | audio_device_buffer_->StopPlayout(); |
1720 | 1748 | } |
1721 | 1749 |
|
| 1750 | + // -------------------------------------------------------------------------------------------- |
| 1751 | + // Step: Stop recording buffer |
| 1752 | + // |
1722 | 1753 | if (!state.next.IsInputEnabled() && audio_device_buffer_->IsRecording()) { |
1723 | 1754 | LOGI() << "Stopping Record buffer..."; |
1724 | 1755 | if (engine_device_ != nullptr) { |
|
1728 | 1759 | audio_device_buffer_->StopRecording(); |
1729 | 1760 | } |
1730 | 1761 |
|
| 1762 | + // -------------------------------------------------------------------------------------------- |
| 1763 | + // Step: Trigger "engine will enable" event |
| 1764 | + // |
1731 | 1765 | if (state.DidAnyEnable() && observer_ != nullptr) { |
1732 | 1766 | // Invoke here before configuring nodes. In iOS, session configuration is required before |
1733 | 1767 | // enabling AGC, muted talker etc. |
1734 | 1768 | int32_t result = observer_->OnEngineWillEnable(engine_device_, state.next.IsOutputEnabled(), |
1735 | 1769 | state.next.IsInputEnabled()); |
1736 | 1770 | if (result != 0) { |
1737 | 1771 | LOGE() << "Call to OnEngineWillEnable returned error: " << result; |
1738 | | - return result; |
| 1772 | + return rollback(result); |
1739 | 1773 | } |
1740 | 1774 | } |
1741 | 1775 |
|
| 1776 | + // -------------------------------------------------------------------------------------------- |
| 1777 | + // Step: Configure device (macOS only) |
| 1778 | + // |
1742 | 1779 | #if TARGET_OS_OSX |
1743 | 1780 | if (state.next.IsAnyEnabled() && |
1744 | 1781 | (!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired() || |
|
1781 | 1818 | } |
1782 | 1819 | #endif |
1783 | 1820 |
|
1784 | | - // Configure Voice-Processing I/O since it affects outputNode also. |
| 1821 | + // -------------------------------------------------------------------------------------------- |
| 1822 | + // Step: Configure Voice-Processing I/O |
| 1823 | + // |
1785 | 1824 | if (state.next.IsInputEnabled() && |
1786 | 1825 | inputNode().voiceProcessingEnabled != state.next.voice_processing_enabled) { |
1787 | 1826 | LOGI() << "setVoiceProcessingEnabled (input): " << state.next.voice_processing_enabled ? "YES" |
|
1833 | 1872 | } |
1834 | 1873 | } |
1835 | 1874 |
|
| 1875 | + // -------------------------------------------------------------------------------------------- |
| 1876 | + // Step: Enable output |
| 1877 | + // |
1836 | 1878 | if (state.next.IsOutputEnabled() && |
1837 | 1879 | (!state.prev.IsOutputEnabled() || state.IsEngineRecreateRequired())) { |
1838 | 1880 | LOGI() << "Enabling output for AVAudioEngine..."; |
|
1853 | 1895 | if (output_node_format.sampleRate == 0 || output_node_format.channelCount == 0) { |
1854 | 1896 | LOGE() << "Output device not available, sampleRate=" << output_node_format.sampleRate |
1855 | 1897 | << ", channelCount=" << output_node_format.channelCount; |
1856 | | - return kAudioEnginePlayoutDeviceNotAvailableError; |
| 1898 | + return rollback(kAudioEnginePlayoutDeviceNotAvailableError); |
1857 | 1899 | } |
1858 | 1900 |
|
1859 | 1901 | AVAudioFormat* engine_output_format = [[AVAudioFormat alloc] |
|
1908 | 1950 | outputNode(), engine_output_format, context); |
1909 | 1951 | if (result != 0) { |
1910 | 1952 | LOGE() << "Call to OnEngineWillConnectOutput returned error: " << result; |
1911 | | - return result; |
| 1953 | + return rollback(result); |
1912 | 1954 | } |
1913 | 1955 | } |
1914 | 1956 |
|
|
1932 | 1974 | } |
1933 | 1975 | } |
1934 | 1976 |
|
| 1977 | + // -------------------------------------------------------------------------------------------- |
| 1978 | + // Step: Enable input |
| 1979 | + // |
1935 | 1980 | if (state.next.IsInputEnabled() && |
1936 | 1981 | (!state.prev.IsInputEnabled() || state.IsEngineRecreateRequired())) { |
1937 | 1982 | LOGI() << "Enabling input for AVAudioEngine..."; |
|
1961 | 2006 | if (input_node_format.sampleRate == 0 || input_node_format.channelCount == 0) { |
1962 | 2007 | LOGE() << "Input device not available, sampleRate=" << input_node_format.sampleRate |
1963 | 2008 | << ", channelCount=" << input_node_format.channelCount; |
1964 | | - return kAudioEngineRecordingDeviceNotAvailableError; |
| 2009 | + return rollback(kAudioEngineRecordingDeviceNotAvailableError); |
1965 | 2010 | } |
1966 | 2011 |
|
1967 | 2012 | input_mixer_node_ = [[AVAudioMixerNode alloc] init]; |
|
2039 | 2084 | engine_device_, inputNode(), input_mixer_node_, engine_input_format, context); |
2040 | 2085 | if (result != 0) { |
2041 | 2086 | LOGE() << "Call to OnEngineWillConnectInput returned error: " << result; |
2042 | | - return result; |
| 2087 | + return rollback(result); |
2043 | 2088 | } |
2044 | 2089 | } |
2045 | 2090 |
|
|
2113 | 2158 | converter_buffer_ = nil; |
2114 | 2159 | } |
2115 | 2160 |
|
| 2161 | + // -------------------------------------------------------------------------------------------- |
| 2162 | + // Step: Trigger "engine did disable" event |
| 2163 | + // |
2116 | 2164 | if (state.DidAnyDisable() && observer_ != nullptr) { |
2117 | 2165 | int32_t result = observer_->OnEngineDidDisable(engine_device_, state.next.IsOutputEnabled(), |
2118 | 2166 | state.next.IsInputEnabled()); |
2119 | 2167 | if (result != 0) { |
2120 | 2168 | LOGE() << "Call to OnEngineDidDisable returned error: " << result; |
2121 | | - return result; |
| 2169 | + return rollback(result); |
2122 | 2170 | } |
2123 | 2171 | } |
2124 | 2172 |
|
2125 | | - // Run-time mute toggling if vp mode. |
| 2173 | + // -------------------------------------------------------------------------------------------- |
| 2174 | + // Step: Run-time mute toggling if vp mode. |
| 2175 | + // |
2126 | 2176 | if (state.next.mute_mode == MuteMode::VoiceProcessing && state.next.IsInputEnabled() && |
2127 | 2177 | inputNode().voiceProcessingEnabled && |
2128 | 2178 | inputNode().voiceProcessingInputMuted != state.next.input_muted) { |
2129 | 2179 | LOGI() << "Update mute (voice processing) runtime update" << state.next.input_muted; |
2130 | 2180 | inputNode().voiceProcessingInputMuted = state.next.input_muted; |
2131 | 2181 | } |
2132 | 2182 |
|
2133 | | - // Run-time mute toggling if mixer mute mode. |
| 2183 | + // -------------------------------------------------------------------------------------------- |
| 2184 | + // Step: Run-time mute toggling if mixer mute mode. |
| 2185 | + // |
2134 | 2186 | if (state.next.mute_mode == MuteMode::InputMixer && state.next.IsInputEnabled() && |
2135 | 2187 | input_mixer_node_ != nil) { |
2136 | 2188 | // Only update if the volume has changed. |
|
2141 | 2193 | } |
2142 | 2194 | } |
2143 | 2195 |
|
| 2196 | + // -------------------------------------------------------------------------------------------- |
| 2197 | + // Step: Configure other audio ducking |
| 2198 | + // |
2144 | 2199 | #if !TARGET_OS_TV |
2145 | 2200 | if (state.next.IsInputEnabled() && inputNode().voiceProcessingEnabled && |
2146 | 2201 | (!state.prev.IsInputEnabled() || |
|
2160 | 2215 | } |
2161 | 2216 | #endif |
2162 | 2217 |
|
2163 | | - // Bypass |
| 2218 | + // -------------------------------------------------------------------------------------------- |
| 2219 | + // Step: Bypass voice processing |
| 2220 | + // |
2164 | 2221 | if (state.next.IsInputEnabled() && inputNode().voiceProcessingEnabled && |
2165 | 2222 | inputNode().voiceProcessingBypassed != state.next.voice_processing_bypassed) { |
2166 | 2223 | LOGI() << "setting voiceProcessingBypassed: " << state.next.voice_processing_bypassed; |
2167 | 2224 | inputNode().voiceProcessingBypassed = state.next.voice_processing_bypassed; |
2168 | 2225 | } |
2169 | 2226 |
|
2170 | | - // AGC |
| 2227 | + // -------------------------------------------------------------------------------------------- |
| 2228 | + // Step: Configure AGC |
| 2229 | + // |
2171 | 2230 | if (state.next.IsInputEnabled() && inputNode().voiceProcessingEnabled && |
2172 | 2231 | inputNode().voiceProcessingAGCEnabled != state.next.voice_processing_agc_enabled) { |
2173 | 2232 | LOGI() << "setting voiceProcessingAGCEnabled: " << state.next.voice_processing_agc_enabled; |
2174 | 2233 | inputNode().voiceProcessingAGCEnabled = state.next.voice_processing_agc_enabled; |
2175 | 2234 | } |
2176 | 2235 |
|
2177 | | - // Start playout buffer if output is running |
| 2236 | + // -------------------------------------------------------------------------------------------- |
| 2237 | + // Step: Start playout buffer |
| 2238 | + // |
2178 | 2239 | if (state.next.IsOutputEnabled() && !audio_device_buffer_->IsPlaying()) { |
2179 | 2240 | if (engine_device_ != nullptr) { |
2180 | 2241 | // Rendering must be stopped first. |
|
2185 | 2246 | fine_audio_buffer_->ResetPlayout(); |
2186 | 2247 | } |
2187 | 2248 |
|
2188 | | - // Start recording buffer if input is running |
| 2249 | + // -------------------------------------------------------------------------------------------- |
| 2250 | + // Step: Start recording buffer |
| 2251 | + // |
2189 | 2252 | if (state.next.IsInputEnabled() && !audio_device_buffer_->IsRecording()) { |
2190 | 2253 | if (engine_device_ != nullptr) { |
2191 | 2254 | // Rendering must be stopped first. |
|
2196 | 2259 | fine_audio_buffer_->ResetRecord(); |
2197 | 2260 | } |
2198 | 2261 |
|
| 2262 | + // -------------------------------------------------------------------------------------------- |
| 2263 | + // Step: Start engine |
| 2264 | + // |
2199 | 2265 | if (state.next.IsAnyRunning()) { |
2200 | 2266 | if (!state.prev.IsAnyRunning() || state.DidEndInterruption() || |
2201 | 2267 | state.IsEngineRestartRequired() || state.IsEngineRecreateRequired()) { |
|
2204 | 2270 | state.next.IsInputEnabled()); |
2205 | 2271 | if (result != 0) { |
2206 | 2272 | LOGE() << "Call to OnEngineWillStart returned error: " << result; |
2207 | | - return result; |
| 2273 | + return rollback(result); |
2208 | 2274 | } |
2209 | 2275 | } |
2210 | 2276 |
|
|
2261 | 2327 | } |
2262 | 2328 | } |
2263 | 2329 |
|
| 2330 | + // -------------------------------------------------------------------------------------------- |
| 2331 | + // Step: Release AVAudioEngine |
| 2332 | + // |
2264 | 2333 | if (state.prev.IsAnyEnabled() && !state.next.IsAnyEnabled()) { |
2265 | 2334 | RTC_DCHECK(engine_device_ != nullptr); |
2266 | 2335 |
|
2267 | 2336 | if (observer_ != nullptr) { |
2268 | 2337 | int32_t result = observer_->OnEngineWillRelease(engine_device_); |
2269 | 2338 | if (result != 0) { |
2270 | 2339 | LOGE() << "Call to OnEngineWillRelease returned error: " << result; |
2271 | | - return result; |
| 2340 | + return rollback(result); |
2272 | 2341 | } |
2273 | 2342 | } |
2274 | 2343 |
|
|
0 commit comments