OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" | 11 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" |
12 | 12 |
13 #include "webrtc/base/checks.h" | 13 #include "webrtc/base/checks.h" |
| 14 #include "webrtc/base/criticalsection.h" |
14 | 15 |
15 #import "webrtc/base/objc/RTCLogging.h" | 16 #import "webrtc/base/objc/RTCLogging.h" |
16 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h" | 17 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h" |
17 | 18 |
18 NSString * const kRTCAudioSessionErrorDomain = @"org.webrtc.RTCAudioSession"; | 19 NSString * const kRTCAudioSessionErrorDomain = @"org.webrtc.RTCAudioSession"; |
19 NSInteger const kRTCAudioSessionErrorLockRequired = -1; | 20 NSInteger const kRTCAudioSessionErrorLockRequired = -1; |
20 | 21 |
21 // This class needs to be thread-safe because it is accessed from many threads. | 22 // This class needs to be thread-safe because it is accessed from many threads. |
22 // TODO(tkchin): Consider more granular locking. We're not expecting a lot of | 23 // TODO(tkchin): Consider more granular locking. We're not expecting a lot of |
23 // lock contention so coarse locks should be fine for now. | 24 // lock contention so coarse locks should be fine for now. |
24 @implementation RTCAudioSession { | 25 @implementation RTCAudioSession { |
| 26 rtc::CriticalSection _crit; |
25 AVAudioSession *_session; | 27 AVAudioSession *_session; |
26 NSHashTable *_delegates; | 28 NSHashTable *_delegates; |
27 NSInteger _activationCount; | 29 NSInteger _activationCount; |
| 30 NSInteger _lockRecursionCount; |
28 BOOL _isActive; | 31 BOOL _isActive; |
29 BOOL _isLocked; | |
30 } | 32 } |
31 | 33 |
32 @synthesize session = _session; | 34 @synthesize session = _session; |
33 @synthesize lock = _lock; | |
34 | 35 |
35 + (instancetype)sharedInstance { | 36 + (instancetype)sharedInstance { |
36 static dispatch_once_t onceToken; | 37 static dispatch_once_t onceToken; |
37 static RTCAudioSession *sharedInstance = nil; | 38 static RTCAudioSession *sharedInstance = nil; |
38 dispatch_once(&onceToken, ^{ | 39 dispatch_once(&onceToken, ^{ |
39 sharedInstance = [[RTCAudioSession alloc] init]; | 40 sharedInstance = [[RTCAudioSession alloc] init]; |
40 }); | 41 }); |
41 return sharedInstance; | 42 return sharedInstance; |
42 } | 43 } |
43 | 44 |
44 - (instancetype)init { | 45 - (instancetype)init { |
45 if (self = [super init]) { | 46 if (self = [super init]) { |
46 _session = [AVAudioSession sharedInstance]; | 47 _session = [AVAudioSession sharedInstance]; |
47 _delegates = [NSHashTable weakObjectsHashTable]; | 48 _delegates = [NSHashTable weakObjectsHashTable]; |
48 _lock = [[NSRecursiveLock alloc] init]; | 49 |
49 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | 50 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; |
50 [center addObserver:self | 51 [center addObserver:self |
51 selector:@selector(handleInterruptionNotification:) | 52 selector:@selector(handleInterruptionNotification:) |
52 name:AVAudioSessionInterruptionNotification | 53 name:AVAudioSessionInterruptionNotification |
53 object:nil]; | 54 object:nil]; |
54 [center addObserver:self | 55 [center addObserver:self |
55 selector:@selector(handleRouteChangeNotification:) | 56 selector:@selector(handleRouteChangeNotification:) |
56 name:AVAudioSessionRouteChangeNotification | 57 name:AVAudioSessionRouteChangeNotification |
57 object:nil]; | 58 object:nil]; |
58 // TODO(tkchin): Maybe listen to SilenceSecondaryAudioHintNotification. | 59 // TODO(tkchin): Maybe listen to SilenceSecondaryAudioHintNotification. |
(...skipping 20 matching lines...) Expand all Loading... |
79 } | 80 } |
80 | 81 |
81 - (BOOL)isActive { | 82 - (BOOL)isActive { |
82 @synchronized(self) { | 83 @synchronized(self) { |
83 return _isActive; | 84 return _isActive; |
84 } | 85 } |
85 } | 86 } |
86 | 87 |
87 - (BOOL)isLocked { | 88 - (BOOL)isLocked { |
88 @synchronized(self) { | 89 @synchronized(self) { |
89 return _isLocked; | 90 return _lockRecursionCount > 0; |
90 } | 91 } |
91 } | 92 } |
92 | 93 |
93 - (void)addDelegate:(id<RTCAudioSessionDelegate>)delegate { | 94 - (void)addDelegate:(id<RTCAudioSessionDelegate>)delegate { |
94 @synchronized(self) { | 95 @synchronized(self) { |
95 [_delegates addObject:delegate]; | 96 [_delegates addObject:delegate]; |
96 } | 97 } |
97 } | 98 } |
98 | 99 |
99 - (void)removeDelegate:(id<RTCAudioSessionDelegate>)delegate { | 100 - (void)removeDelegate:(id<RTCAudioSessionDelegate>)delegate { |
100 @synchronized(self) { | 101 @synchronized(self) { |
101 [_delegates removeObject:delegate]; | 102 [_delegates removeObject:delegate]; |
102 } | 103 } |
103 } | 104 } |
104 | 105 |
105 - (void)lockForConfiguration { | 106 - (void)lockForConfiguration { |
106 [_lock lock]; | 107 _crit.Enter(); |
107 @synchronized(self) { | 108 @synchronized(self) { |
108 _isLocked = YES; | 109 ++_lockRecursionCount; |
109 } | 110 } |
110 } | 111 } |
111 | 112 |
112 - (void)unlockForConfiguration { | 113 - (void)unlockForConfiguration { |
113 // Don't let threads other than the one that called lockForConfiguration | 114 // Don't let threads other than the one that called lockForConfiguration |
114 // unlock. | 115 // unlock. |
115 if ([_lock tryLock]) { | 116 if (_crit.TryEnter()) { |
116 @synchronized(self) { | 117 @synchronized(self) { |
117 _isLocked = NO; | 118 --_lockRecursionCount; |
118 } | 119 } |
119 // One unlock for the tryLock, and another one to actually unlock. If this | 120 // One unlock for the tryLock, and another one to actually unlock. If this |
120 // was called without anyone calling lock, the underlying NSRecursiveLock | 121 // was called without anyone calling lock, we will hit an assertion. |
121 // should spit out an error. | 122 _crit.Leave(); |
122 [_lock unlock]; | 123 _crit.Leave(); |
123 [_lock unlock]; | |
124 } | 124 } |
125 } | 125 } |
126 | 126 |
127 #pragma mark - AVAudioSession proxy methods | 127 #pragma mark - AVAudioSession proxy methods |
128 | 128 |
129 - (NSString *)category { | 129 - (NSString *)category { |
130 return self.session.category; | 130 return self.session.category; |
131 } | 131 } |
132 | 132 |
133 - (AVAudioSessionCategoryOptions)categoryOptions { | 133 - (AVAudioSessionCategoryOptions)categoryOptions { |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
524 } | 524 } |
525 } | 525 } |
526 | 526 |
527 - (void)notifyMediaServicesWereReset { | 527 - (void)notifyMediaServicesWereReset { |
528 for (id<RTCAudioSessionDelegate> delegate in self.delegates) { | 528 for (id<RTCAudioSessionDelegate> delegate in self.delegates) { |
529 [delegate audioSessionMediaServicesWereReset:self]; | 529 [delegate audioSessionMediaServicesWereReset:self]; |
530 } | 530 } |
531 } | 531 } |
532 | 532 |
533 @end | 533 @end |
OLD | NEW |