diff --git a/CHANGES.md b/CHANGES.md index 8442639b..49c1d240 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -29,6 +29,13 @@ VERSION ファイルを上げただけの場合は変更履歴記録は不要。 ## タイムライン +- 2025-12-12 [ADD] iOS SDK に RTCAudioDeviceModule を追加する + - iOS 実機のマイクインジケータが消灯状態のミュートをできるようにする + - RTCPeerConnectionFactory に RTCAudioDeviceModule を引数とする initWithEncoderFactory:decoderFactory:audioDeviceModule: を追加する + - RTCAudioDeviceModule は公開 API として pauseRecording/resumeRecording を持つ + - AudioDeviceModuleIOS に pauseRecording/resumeRecording を追加する + - AudioDeviceIOS に pauseRecording/resumeRecording を追加する + - @t-miya - 2025-12-12 [RELEASE] m143.7499.2.1 - @zztkm - 2025-12-10 [ADD] iOS SDK 向けに RTCAudioTrackSink を追加する diff --git a/patches/ios_audio_pause_resume.patch b/patches/ios_audio_pause_resume.patch new file mode 100644 index 00000000..e13f8beb --- /dev/null +++ b/patches/ios_audio_pause_resume.patch @@ -0,0 +1,313 @@ +diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn +index f61cf30..aab3684 100644 +--- a/sdk/BUILD.gn ++++ b/sdk/BUILD.gn +@@ -172,6 +172,7 @@ if (is_ios || is_mac) { + sources += [ + "objc/helpers/UIDevice+RTCDevice.h", + "objc/helpers/UIDevice+RTCDevice.mm", ++ "objc/components/audio/RTCAudioDeviceModule.mm", + ] + + if (target_platform != "tvos") { +@@ -1369,6 +1370,7 @@ if (is_ios || is_mac) { + "objc/base/RTCVideoRenderer.h", + "objc/base/RTCYUVPlanarBuffer.h", + "objc/components/audio/RTCAudioDevice.h", ++ "objc/components/audio/RTCAudioDeviceModule.h", + "objc/components/audio/RTCAudioSession.h", + "objc/components/audio/RTCAudioSessionConfiguration.h", + "objc/components/capturer/RTCCameraVideoCapturer.h", +diff --git a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h +index 8287bcf..7090921 100644 +--- a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h ++++ b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h +@@ -11,6 +11,7 @@ + #import + + #import "sdk/objc/base/RTCMacros.h" ++#import "sdk/objc/components/audio/RTCAudioDeviceModule.h" + + NS_ASSUME_NONNULL_BEGIN + +@@ -24,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN + @class RTC_OBJC_TYPE(RTCVideoSource); + @class RTC_OBJC_TYPE(RTCVideoTrack); + @class RTC_OBJC_TYPE(RTCPeerConnectionFactoryOptions); ++@class RTC_OBJC_TYPE(RTCAudioDeviceModule); + @protocol RTC_OBJC_TYPE + (RTCPeerConnectionDelegate); + @protocol RTC_OBJC_TYPE +@@ -61,6 +63,17 @@ RTC_OBJC_EXPORT + audioDevice: + (nullable id)audioDevice; + ++/** ++ * Video encoder/decorder ファクトリと RTCAudioDeviceModule 差し込みによる初期化 ++ */ ++- (instancetype) ++ initWithEncoderFactory: ++ (nullable id)encoderFactory ++ decoderFactory:(nullable id) ++ decoderFactory ++ audioDeviceModule: ++ (nullable RTC_OBJC_TYPE(RTCAudioDeviceModule)*)audioDeviceModule; ++ + /** + * Valid kind values are kRTCMediaStreamTrackKindAudio and + * kRTCMediaStreamTrackKindVideo. +diff --git a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm +index 233cf25..0c20512 100644 +--- a/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm ++++ b/sdk/objc/api/peerconnection/RTCPeerConnectionFactory.mm +@@ -14,6 +14,7 @@ + #import "RTCPeerConnectionFactory+Private.h" + #import "RTCPeerConnectionFactoryOptions+Private.h" + #import "RTCRtpCapabilities+Private.h" ++#import "sdk/objc/components/audio/RTCAudioDeviceModule+Private.h" + + #import "RTCAudioSource+Private.h" + #import "RTCAudioTrack+Private.h" +@@ -189,6 +190,43 @@ - (instancetype)init { + #endif + } + ++// RTCAudioDeviceModule* により ADM を初期化する ++// audioDeviceModule から Native ADM を取得して dependencies.adm にセットする処理以外は ++// initWithEncoderFactory:decoderFactory:audioDevice: と同様の実装 ++- (instancetype) ++ initWithEncoderFactory: ++ (nullable id)encoderFactory ++ decoderFactory:(nullable id) ++ decoderFactory ++ audioDeviceModule: ++ (nullable RTC_OBJC_TYPE(RTCAudioDeviceModule)*)audioDeviceModule { ++#ifdef HAVE_NO_MEDIA ++ return [self initWithNoMedia]; ++#else ++ webrtc::PeerConnectionFactoryDependencies dependencies; ++ dependencies.env = webrtc::CreateEnvironment(); ++ dependencies.audio_encoder_factory = ++ webrtc::CreateBuiltinAudioEncoderFactory(); ++ dependencies.audio_decoder_factory = ++ webrtc::CreateBuiltinAudioDecoderFactory(); ++ if (encoderFactory) { ++ dependencies.video_encoder_factory = ++ webrtc::ObjCToNativeVideoEncoderFactory(encoderFactory); ++ } ++ if (decoderFactory) { ++ dependencies.video_decoder_factory = ++ webrtc::ObjCToNativeVideoDecoderFactory(decoderFactory); ++ } ++ if (audioDeviceModule) { ++ auto adm_ptr = [audioDeviceModule nativeAudioDeviceModule]; ++ dependencies.adm = adm_ptr; ++ } else { ++ dependencies.adm = webrtc::CreateAudioDeviceModule(*dependencies.env); ++ } ++ return [self initWithMediaAndDependencies:dependencies]; ++#endif ++} ++ + - (instancetype)initWithNativeDependencies: + (webrtc::PeerConnectionFactoryDependencies &)dependencies { + self = [super init]; +diff --git a/sdk/objc/components/audio/RTCAudioDeviceModule+Private.h b/sdk/objc/components/audio/RTCAudioDeviceModule+Private.h +new file mode 100644 +index 0000000..3e67c85 +--- /dev/null ++++ b/sdk/objc/components/audio/RTCAudioDeviceModule+Private.h +@@ -0,0 +1,11 @@ ++// 公開APIで隠している C++ 型のネイティブ ADM ポインタ webrtc::AudioDeviceModule* を安全に取得するためのヘッダ ++// C++ 用の型 webrtc::AudioDeviceModule を扱うため ObjC++ からのみインクルードされる想定 ++#import "RTCAudioDeviceModule.h" ++ ++#include "api/audio/audio_device.h" ++ ++@interface RTC_OBJC_TYPE(RTCAudioDeviceModule) () ++ ++- (webrtc::scoped_refptr)nativeAudioDeviceModule; ++ ++@end +diff --git a/sdk/objc/components/audio/RTCAudioDeviceModule.h b/sdk/objc/components/audio/RTCAudioDeviceModule.h +new file mode 100644 +index 0000000..bd3c425 +--- /dev/null ++++ b/sdk/objc/components/audio/RTCAudioDeviceModule.h +@@ -0,0 +1,17 @@ ++// iOS 用の AudioDeviceModule を生成し、pauseRecording()/resumeRecording() を公開するためのラッパー ++#import ++ ++#import "sdk/objc/base/RTCMacros.h" ++ ++RTC_OBJC_EXPORT ++@interface RTC_OBJC_TYPE(RTCAudioDeviceModule) : NSObject ++ ++// 内部で AudioDeviceModuleIOS を生成する ++- (instancetype)init; ++ ++// 録音を一時停止する。内部で AudioDeviceModuleIOS::pauseRecording を呼ぶ ++- (NSInteger)pauseRecording; ++// 録音を再開する。内部で AudioDeviceModuleIOS::resumeRecording を呼ぶ ++- (NSInteger)resumeRecording; ++ ++@end +diff --git a/sdk/objc/components/audio/RTCAudioDeviceModule.mm b/sdk/objc/components/audio/RTCAudioDeviceModule.mm +new file mode 100644 +index 0000000..d1e495f +--- /dev/null ++++ b/sdk/objc/components/audio/RTCAudioDeviceModule.mm +@@ -0,0 +1,40 @@ ++#import "RTCAudioDeviceModule.h" ++ ++#include "api/environment/environment_factory.h" ++#include "api/scoped_refptr.h" ++#include "sdk/objc/native/api/audio_device_module.h" ++#include "sdk/objc/native/src/audio/audio_device_module_ios.h" ++ ++@interface RTC_OBJC_TYPE(RTCAudioDeviceModule) () { ++ webrtc::scoped_refptr _adm; ++} ++@end ++ ++@implementation RTC_OBJC_TYPE(RTCAudioDeviceModule) ++ ++- (instancetype)init { ++ self = [super init]; ++ if (self) { ++ auto env = webrtc::CreateEnvironment(); ++ _adm = webrtc::CreateAudioDeviceModule(env); ++ } ++ return self; ++} ++ ++- (webrtc::scoped_refptr)nativeAudioDeviceModule { ++ return _adm; ++} ++ ++- (NSInteger)pauseRecording { ++ auto ptr = ++ static_cast(_adm.get()); ++ return ptr->PauseRecording(); ++} ++ ++- (NSInteger)resumeRecording { ++ auto ptr = ++ static_cast(_adm.get()); ++ return ptr->ResumeRecording(); ++} ++ ++@end +diff --git a/sdk/objc/native/src/audio/audio_device_ios.h b/sdk/objc/native/src/audio/audio_device_ios.h +index d1788a3..e8b4666 100644 +--- a/sdk/objc/native/src/audio/audio_device_ios.h ++++ b/sdk/objc/native/src/audio/audio_device_ios.h +@@ -80,6 +80,8 @@ class AudioDeviceIOS : public AudioDeviceGeneric, + bool Playing() const override; + + int32_t StartRecording() override; ++ int32_t PauseRecording(); ++ int32_t ResumeRecording(); + int32_t StopRecording() override; + bool Recording() const override; + +diff --git a/sdk/objc/native/src/audio/audio_device_ios.mm b/sdk/objc/native/src/audio/audio_device_ios.mm +index 021f08a..5d2b8b2 100644 +--- a/sdk/objc/native/src/audio/audio_device_ios.mm ++++ b/sdk/objc/native/src/audio/audio_device_ios.mm +@@ -330,7 +330,9 @@ static void LogDeviceInfo() { + int32_t AudioDeviceIOS::StopRecording() { + LOGI() << "StopRecording"; + RTC_DCHECK_RUN_ON(thread_); +- if (!audio_is_initialized_ || !recording_.load()) { ++ // 元々は !recording_.load() との OR 判定だったが、PauseRecording 追加により ++ // StopRecording 実行前に recording_ が 0 となることがあるため分岐を修正している ++ if (!audio_is_initialized_) { + return 0; + } + if (!playing_.load()) { +@@ -340,6 +342,36 @@ static void LogDeviceInfo() { + return 0; + } + ++int32_t AudioDeviceIOS::PauseRecording() { ++ LOGI() << "PauseRecording"; ++ RTC_DCHECK_RUN_ON(thread_); ++ if (!audio_is_initialized_) { ++ RTC_LOG(LS_WARNING) << "PauseRecording called before audio is initialized"; ++ return 0; ++ } ++ if (!recording_.load()) { ++ RTC_LOG(LS_WARNING) << "PauseRecording called while not recording"; ++ return 0; ++ } ++ recording_.store(0, std::memory_order_release); ++ return 0; ++} ++ ++int32_t AudioDeviceIOS::ResumeRecording() { ++ LOGI() << "ResumeRecording"; ++ RTC_DCHECK_RUN_ON(thread_); ++ if (!audio_is_initialized_) { ++ RTC_LOG(LS_WARNING) << "ResumeRecording called before audio is initialized"; ++ return 0; ++ } ++ if (recording_.load()) { ++ RTC_LOG(LS_WARNING) << "ResumeRecording called while recording"; ++ return 0; ++ } ++ recording_.store(1, std::memory_order_release); ++ return 0; ++} ++ + bool AudioDeviceIOS::Recording() const { + return recording_.load(); + } +diff --git a/sdk/objc/native/src/audio/audio_device_module_ios.h b/sdk/objc/native/src/audio/audio_device_module_ios.h +index 5ff5062..987deb8 100644 +--- a/sdk/objc/native/src/audio/audio_device_module_ios.h ++++ b/sdk/objc/native/src/audio/audio_device_module_ios.h +@@ -76,6 +76,8 @@ class AudioDeviceModuleIOS : public AudioDeviceModule { + int32_t StopPlayout() override; + bool Playing() const override; + int32_t StartRecording() override; ++ int32_t PauseRecording(); ++ int32_t ResumeRecording(); + int32_t StopRecording() override; + bool Recording() const override; + +diff --git a/sdk/objc/native/src/audio/audio_device_module_ios.mm b/sdk/objc/native/src/audio/audio_device_module_ios.mm +index e2ea360..9ac6904 100644 +--- a/sdk/objc/native/src/audio/audio_device_module_ios.mm ++++ b/sdk/objc/native/src/audio/audio_device_module_ios.mm +@@ -652,6 +652,30 @@ + return result; + } + ++int32_t AudioDeviceModuleIOS::PauseRecording() { ++ RTC_DLOG(LS_INFO) << __FUNCTION__; ++ CHECKinitialized_(); ++ int32_t result = audio_device_->PauseRecording(); ++ audio_device_buffer_.get()->StopRecording(); ++ if (result < 0) { ++ ReportError(kRecordingFailed); ++ } ++ RTC_DLOG(LS_INFO) << "output: " << result; ++ return result; ++} ++ ++int32_t AudioDeviceModuleIOS::ResumeRecording() { ++ RTC_DLOG(LS_INFO) << __FUNCTION__; ++ CHECKinitialized_(); ++ int32_t result = audio_device_->ResumeRecording(); ++ audio_device_buffer_.get()->StartRecording(); ++ if (result < 0) { ++ ReportError(kRecordingFailed); ++ } ++ RTC_DLOG(LS_INFO) << "output: " << result; ++ return result; ++} ++ + int32_t AudioDeviceModuleIOS::StopRecording() { + RTC_DLOG(LS_INFO) << __FUNCTION__; + CHECKinitialized_(); diff --git a/run.py b/run.py index 1ed2bc6d..234c1f1a 100644 --- a/run.py +++ b/run.py @@ -260,6 +260,7 @@ def get_depot_tools(source_dir, fetch=False): "revert_siso.patch", "ios_revive_copy_framework_header.patch", "ios_audio_track_sink.patch", + "ios_audio_pause_resume.patch", ], "android": [ "add_deps.patch",