Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: webrtc/modules/audio_device/ios/objc/RTCAudioSession.mm

Issue 1822543002: Support delayed AudioUnit initialization. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Rebase Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/atomicops.h"
13 #include "webrtc/base/checks.h" 14 #include "webrtc/base/checks.h"
14 #include "webrtc/base/criticalsection.h" 15 #include "webrtc/base/criticalsection.h"
15 #include "webrtc/modules/audio_device/ios/audio_device_ios.h" 16 #include "webrtc/modules/audio_device/ios/audio_device_ios.h"
16 17
17 #import "webrtc/base/objc/RTCLogging.h" 18 #import "webrtc/base/objc/RTCLogging.h"
18 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h" 19 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h"
19 20
20 NSString * const kRTCAudioSessionErrorDomain = @"org.webrtc.RTCAudioSession"; 21 NSString * const kRTCAudioSessionErrorDomain = @"org.webrtc.RTCAudioSession";
21 NSInteger const kRTCAudioSessionErrorLockRequired = -1; 22 NSInteger const kRTCAudioSessionErrorLockRequired = -1;
22 NSInteger const kRTCAudioSessionErrorConfiguration = -2; 23 NSInteger const kRTCAudioSessionErrorConfiguration = -2;
23 24
24 // This class needs to be thread-safe because it is accessed from many threads. 25 // This class needs to be thread-safe because it is accessed from many threads.
25 // TODO(tkchin): Consider more granular locking. We're not expecting a lot of 26 // TODO(tkchin): Consider more granular locking. We're not expecting a lot of
26 // lock contention so coarse locks should be fine for now. 27 // lock contention so coarse locks should be fine for now.
27 @implementation RTCAudioSession { 28 @implementation RTCAudioSession {
28 rtc::CriticalSection _crit; 29 rtc::CriticalSection _crit;
29 AVAudioSession *_session; 30 AVAudioSession *_session;
30 NSInteger _activationCount; 31 volatile int _activationCount;
31 NSInteger _lockRecursionCount; 32 volatile int _lockRecursionCount;
33 volatile int _webRTCSessionCount;
32 BOOL _isActive; 34 BOOL _isActive;
33 BOOL _shouldDelayAudioConfiguration; 35 BOOL _shouldDelayAudioConfiguration;
34 } 36 }
35 37
36 @synthesize session = _session; 38 @synthesize session = _session;
37 @synthesize delegates = _delegates; 39 @synthesize delegates = _delegates;
40 @synthesize savedConfiguration = _savedConfiguration;
38 41
39 + (instancetype)sharedInstance { 42 + (instancetype)sharedInstance {
40 static dispatch_once_t onceToken; 43 static dispatch_once_t onceToken;
41 static RTCAudioSession *sharedInstance = nil; 44 static RTCAudioSession *sharedInstance = nil;
42 dispatch_once(&onceToken, ^{ 45 dispatch_once(&onceToken, ^{
43 sharedInstance = [[RTCAudioSession alloc] init]; 46 sharedInstance = [[self alloc] init];
44 }); 47 });
45 return sharedInstance; 48 return sharedInstance;
46 } 49 }
47 50
48 - (instancetype)init { 51 - (instancetype)init {
49 if (self = [super init]) { 52 if (self = [super init]) {
50 _session = [AVAudioSession sharedInstance]; 53 _session = [AVAudioSession sharedInstance];
51 54
52 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 55 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
53 [center addObserver:self 56 [center addObserver:self
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 } 102 }
100 } 103 }
101 104
102 - (BOOL)isActive { 105 - (BOOL)isActive {
103 @synchronized(self) { 106 @synchronized(self) {
104 return _isActive; 107 return _isActive;
105 } 108 }
106 } 109 }
107 110
108 - (BOOL)isLocked { 111 - (BOOL)isLocked {
109 @synchronized(self) { 112 return _lockRecursionCount > 0;
110 return _lockRecursionCount > 0;
111 }
112 } 113 }
113 114
114 - (void)setShouldDelayAudioConfiguration:(BOOL)shouldDelayAudioConfiguration { 115 - (void)setShouldDelayAudioConfiguration:(BOOL)shouldDelayAudioConfiguration {
115 @synchronized(self) { 116 @synchronized(self) {
117 // No one should be changing this while an audio device is active.
118 RTC_DCHECK(!self.isConfiguredForWebRTC);
116 if (_shouldDelayAudioConfiguration == shouldDelayAudioConfiguration) { 119 if (_shouldDelayAudioConfiguration == shouldDelayAudioConfiguration) {
117 return; 120 return;
118 } 121 }
119 _shouldDelayAudioConfiguration = shouldDelayAudioConfiguration; 122 _shouldDelayAudioConfiguration = shouldDelayAudioConfiguration;
120 } 123 }
121 } 124 }
122 125
123 - (BOOL)shouldDelayAudioConfiguration { 126 - (BOOL)shouldDelayAudioConfiguration {
124 @synchronized(self) { 127 @synchronized(self) {
125 return _shouldDelayAudioConfiguration; 128 return _shouldDelayAudioConfiguration;
126 } 129 }
127 } 130 }
128 131
132 // TODO(tkchin): Check for duplicates.
129 - (void)addDelegate:(id<RTCAudioSessionDelegate>)delegate { 133 - (void)addDelegate:(id<RTCAudioSessionDelegate>)delegate {
130 if (!delegate) { 134 if (!delegate) {
131 return; 135 return;
132 } 136 }
133 @synchronized(self) { 137 @synchronized(self) {
134 _delegates.push_back(delegate); 138 _delegates.push_back(delegate);
135 [self removeZeroedDelegates]; 139 [self removeZeroedDelegates];
136 } 140 }
137 } 141 }
138 142
139 - (void)removeDelegate:(id<RTCAudioSessionDelegate>)delegate { 143 - (void)removeDelegate:(id<RTCAudioSessionDelegate>)delegate {
140 if (!delegate) { 144 if (!delegate) {
141 return; 145 return;
142 } 146 }
143 @synchronized(self) { 147 @synchronized(self) {
144 _delegates.erase(std::remove(_delegates.begin(), 148 _delegates.erase(std::remove(_delegates.begin(),
145 _delegates.end(), 149 _delegates.end(),
146 delegate)); 150 delegate));
147 [self removeZeroedDelegates]; 151 [self removeZeroedDelegates];
148 } 152 }
149 } 153 }
150 154
151 - (void)lockForConfiguration { 155 - (void)lockForConfiguration {
152 _crit.Enter(); 156 _crit.Enter();
153 @synchronized(self) { 157 rtc::AtomicOps::Increment(&_lockRecursionCount);
154 ++_lockRecursionCount;
155 }
156 } 158 }
157 159
158 - (void)unlockForConfiguration { 160 - (void)unlockForConfiguration {
159 // Don't let threads other than the one that called lockForConfiguration 161 // Don't let threads other than the one that called lockForConfiguration
160 // unlock. 162 // unlock.
161 if (_crit.TryEnter()) { 163 if (_crit.TryEnter()) {
162 @synchronized(self) { 164 rtc::AtomicOps::Decrement(&_lockRecursionCount);
163 --_lockRecursionCount;
164 }
165 // One unlock for the tryLock, and another one to actually unlock. If this 165 // One unlock for the tryLock, and another one to actually unlock. If this
166 // was called without anyone calling lock, we will hit an assertion. 166 // was called without anyone calling lock, we will hit an assertion.
167 _crit.Leave(); 167 _crit.Leave();
168 _crit.Leave(); 168 _crit.Leave();
169 } 169 }
170 } 170 }
171 171
172 #pragma mark - AVAudioSession proxy methods 172 #pragma mark - AVAudioSession proxy methods
173 173
174 - (NSString *)category { 174 - (NSString *)category {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 return self.session.IOBufferDuration; 255 return self.session.IOBufferDuration;
256 } 256 }
257 257
258 // TODO(tkchin): Simplify the amount of locking happening here. Likely that we 258 // TODO(tkchin): Simplify the amount of locking happening here. Likely that we
259 // can just do atomic increments / decrements. 259 // can just do atomic increments / decrements.
260 - (BOOL)setActive:(BOOL)active 260 - (BOOL)setActive:(BOOL)active
261 error:(NSError **)outError { 261 error:(NSError **)outError {
262 if (![self checkLock:outError]) { 262 if (![self checkLock:outError]) {
263 return NO; 263 return NO;
264 } 264 }
265 NSInteger activationCount = self.activationCount; 265 int activationCount = _activationCount;
266 if (!active && activationCount == 0) { 266 if (!active && activationCount == 0) {
267 RTCLogWarning(@"Attempting to deactivate without prior activation."); 267 RTCLogWarning(@"Attempting to deactivate without prior activation.");
268 } 268 }
269 BOOL success = YES; 269 BOOL success = YES;
270 BOOL isActive = self.isActive; 270 BOOL isActive = self.isActive;
271 // Keep a local error so we can log it. 271 // Keep a local error so we can log it.
272 NSError *error = nil; 272 NSError *error = nil;
273 BOOL shouldSetActive = 273 BOOL shouldSetActive =
274 (active && !isActive) || (!active && isActive && activationCount == 1); 274 (active && !isActive) || (!active && isActive && activationCount == 1);
275 // Attempt to activate if we're not active. 275 // Attempt to activate if we're not active.
(...skipping 21 matching lines...) Expand all
297 [self incrementActivationCount]; 297 [self incrementActivationCount];
298 } 298 }
299 } else { 299 } else {
300 RTCLogError(@"Failed to setActive:%d. Error: %@", 300 RTCLogError(@"Failed to setActive:%d. Error: %@",
301 active, error.localizedDescription); 301 active, error.localizedDescription);
302 } 302 }
303 // Decrement activation count on deactivation whether or not it succeeded. 303 // Decrement activation count on deactivation whether or not it succeeded.
304 if (!active) { 304 if (!active) {
305 [self decrementActivationCount]; 305 [self decrementActivationCount];
306 } 306 }
307 RTCLog(@"Number of current activations: %ld", (long)self.activationCount); 307 RTCLog(@"Number of current activations: %d", _activationCount);
308 return success; 308 return success;
309 } 309 }
310 310
311 - (BOOL)setCategory:(NSString *)category 311 - (BOOL)setCategory:(NSString *)category
312 withOptions:(AVAudioSessionCategoryOptions)options 312 withOptions:(AVAudioSessionCategoryOptions)options
313 error:(NSError **)outError { 313 error:(NSError **)outError {
314 if (![self checkLock:outError]) { 314 if (![self checkLock:outError]) {
315 return NO; 315 return NO;
316 } 316 }
317 return [self.session setCategory:category withOptions:options error:outError]; 317 return [self.session setCategory:category withOptions:options error:outError];
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
489 return error; 489 return error;
490 } 490 }
491 491
492 - (std::vector<__weak id<RTCAudioSessionDelegate> >)delegates { 492 - (std::vector<__weak id<RTCAudioSessionDelegate> >)delegates {
493 @synchronized(self) { 493 @synchronized(self) {
494 // Note: this returns a copy. 494 // Note: this returns a copy.
495 return _delegates; 495 return _delegates;
496 } 496 }
497 } 497 }
498 498
499 - (void)setSavedConfiguration:(RTCAudioSessionConfiguration *)configuration {
500 @synchronized(self) {
501 if (_savedConfiguration == configuration) {
502 return;
503 }
504 _savedConfiguration = configuration;
505 }
506 }
507
508 - (RTCAudioSessionConfiguration *)savedConfiguration {
509 @synchronized(self) {
510 return _savedConfiguration;
511 }
512 }
513
514 // TODO(tkchin): check for duplicates.
499 - (void)pushDelegate:(id<RTCAudioSessionDelegate>)delegate { 515 - (void)pushDelegate:(id<RTCAudioSessionDelegate>)delegate {
500 @synchronized(self) { 516 @synchronized(self) {
501 _delegates.insert(_delegates.begin(), delegate); 517 _delegates.insert(_delegates.begin(), delegate);
502 } 518 }
503 } 519 }
504 520
505 - (void)removeZeroedDelegates { 521 - (void)removeZeroedDelegates {
506 @synchronized(self) { 522 @synchronized(self) {
507 for (auto it = _delegates.begin(); it != _delegates.end(); ++it) { 523 for (auto it = _delegates.begin(); it != _delegates.end(); ++it) {
508 if (!*it) { 524 if (!*it) {
509 _delegates.erase(it); 525 _delegates.erase(it);
510 } 526 }
511 } 527 }
512 } 528 }
513 } 529 }
514 530
515 - (NSInteger)activationCount { 531 - (int)activationCount {
516 @synchronized(self) { 532 return _activationCount;
517 return _activationCount;
518 }
519 } 533 }
520 534
521 - (NSInteger)incrementActivationCount { 535 - (int)incrementActivationCount {
522 RTCLog(@"Incrementing activation count."); 536 RTCLog(@"Incrementing activation count.");
523 @synchronized(self) { 537 return rtc::AtomicOps::Increment(&_activationCount);
524 return ++_activationCount;
525 }
526 } 538 }
527 539
528 - (NSInteger)decrementActivationCount { 540 - (NSInteger)decrementActivationCount {
529 RTCLog(@"Decrementing activation count."); 541 RTCLog(@"Decrementing activation count.");
530 @synchronized(self) { 542 return rtc::AtomicOps::Decrement(&_activationCount);
531 return --_activationCount; 543 }
532 } 544
545 - (int)webRTCSessionCount {
546 return _webRTCSessionCount;
533 } 547 }
534 548
535 - (BOOL)checkLock:(NSError **)outError { 549 - (BOOL)checkLock:(NSError **)outError {
536 // Check ivar instead of trying to acquire lock so that we won't accidentally 550 // Check ivar instead of trying to acquire lock so that we won't accidentally
537 // acquire lock if it hasn't already been called. 551 // acquire lock if it hasn't already been called.
538 if (!self.isLocked) { 552 if (!self.isLocked) {
539 if (outError) { 553 if (outError) {
540 *outError = [RTCAudioSession lockError]; 554 *outError = [RTCAudioSession lockError];
541 } 555 }
542 return NO; 556 return NO;
543 } 557 }
544 return YES; 558 return YES;
545 } 559 }
546 560
561 - (BOOL)beginWebRTCSession:(NSError **)outError {
562 if (outError) {
563 *outError = nil;
564 }
565 if (![self checkLock:outError]) {
566 return NO;
567 }
568 NSInteger sessionCount = rtc::AtomicOps::Increment(&_webRTCSessionCount);
569 if (sessionCount > 1) {
570 // Should already be configured.
571 RTC_DCHECK(self.isConfiguredForWebRTC);
572 return YES;
573 }
574
575 // Only perform configuration steps once. Application might have already
576 // configured the session.
577 if (self.isConfiguredForWebRTC) {
578 // Nothing more to do, already configured.
579 return YES;
580 }
581
582 // If application has prevented automatic configuration, return here and wait
583 // for application to call configureWebRTCSession.
584 if (self.shouldDelayAudioConfiguration) {
585 [self notifyShouldConfigure];
586 return YES;
587 }
588
589 // Configure audio session.
590 NSError *error = nil;
591 if (![self configureWebRTCSession:&error]) {
592 RTCLogError(@"Error configuring audio session: %@",
593 error.localizedDescription);
594 if (outError) {
595 *outError = error;
596 }
597 return NO;
598 }
599
600 return YES;
601 }
602
603 - (BOOL)endWebRTCSession:(NSError **)outError {
604 if (outError) {
605 *outError = nil;
606 }
607 if (![self checkLock:outError]) {
608 return NO;
609 }
610 int sessionCount = rtc::AtomicOps::Decrement(&_webRTCSessionCount);
611 RTC_DCHECK_GE(sessionCount, 0);
612 if (sessionCount != 0) {
613 // Should still be configured.
614 RTC_DCHECK(self.isConfiguredForWebRTC);
615 return YES;
616 }
617
618 // Only unconfigure if application has not done it.
619 if (!self.isConfiguredForWebRTC) {
620 // Nothing more to do, already unconfigured.
621 return YES;
622 }
623
624 // If application has prevented automatic configuration, return here and wait
625 // for application to call unconfigureWebRTCSession.
626 if (self.shouldDelayAudioConfiguration) {
627 [self notifyShouldUnconfigure];
628 return YES;
629 }
630
631 // Unconfigure audio session.
632 NSError *error = nil;
633 if (![self unconfigureWebRTCSession:&error]) {
634 RTCLogError(@"Error unconfiguring audio session: %@",
635 error.localizedDescription);
636 if (outError) {
637 *outError = error;
638 }
639 return NO;
640 }
641
642 return YES;
643 }
644
645 - (NSError *)configurationErrorWithDescription:(NSString *)description {
646 NSDictionary* userInfo = @{
647 NSLocalizedDescriptionKey: description,
648 };
649 return [[NSError alloc] initWithDomain:kRTCAudioSessionErrorDomain
650 code:kRTCAudioSessionErrorConfiguration
651 userInfo:userInfo];
652 }
653
547 - (void)updateAudioSessionAfterEvent { 654 - (void)updateAudioSessionAfterEvent {
548 BOOL shouldActivate = self.activationCount > 0; 655 BOOL shouldActivate = self.activationCount > 0;
549 AVAudioSessionSetActiveOptions options = shouldActivate ? 656 AVAudioSessionSetActiveOptions options = shouldActivate ?
550 0 : AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation; 657 0 : AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation;
551 NSError *error = nil; 658 NSError *error = nil;
552 if ([self.session setActive:shouldActivate 659 if ([self.session setActive:shouldActivate
553 withOptions:options 660 withOptions:options
554 error:&error]) { 661 error:&error]) {
555 self.isActive = shouldActivate; 662 self.isActive = shouldActivate;
556 } else { 663 } else {
557 RTCLogError(@"Failed to set session active to %d. Error:%@", 664 RTCLogError(@"Failed to set session active to %d. Error:%@",
558 shouldActivate, error.localizedDescription); 665 shouldActivate, error.localizedDescription);
559 } 666 }
560 } 667 }
561 668
562 - (void)notifyDidBeginInterruption { 669 - (void)notifyDidBeginInterruption {
563 for (auto delegate : self.delegates) { 670 for (auto delegate : self.delegates) {
564 [delegate audioSessionDidBeginInterruption:self]; 671 SEL sel = @selector(audioSessionDidBeginInterruption:);
672 if ([delegate respondsToSelector:sel]) {
673 [delegate audioSessionDidBeginInterruption:self];
674 }
565 } 675 }
566 } 676 }
567 677
568 - (void)notifyDidEndInterruptionWithShouldResumeSession: 678 - (void)notifyDidEndInterruptionWithShouldResumeSession:
569 (BOOL)shouldResumeSession { 679 (BOOL)shouldResumeSession {
570 for (auto delegate : self.delegates) { 680 for (auto delegate : self.delegates) {
571 [delegate audioSessionDidEndInterruption:self 681 SEL sel = @selector(audioSessionDidEndInterruption:shouldResumeSession:);
572 shouldResumeSession:shouldResumeSession]; 682 if ([delegate respondsToSelector:sel]) {
683 [delegate audioSessionDidEndInterruption:self
684 shouldResumeSession:shouldResumeSession];
685 }
573 } 686 }
574
575 } 687 }
576 688
577 - (void)notifyDidChangeRouteWithReason:(AVAudioSessionRouteChangeReason)reason 689 - (void)notifyDidChangeRouteWithReason:(AVAudioSessionRouteChangeReason)reason
578 previousRoute:(AVAudioSessionRouteDescription *)previousRoute { 690 previousRoute:(AVAudioSessionRouteDescription *)previousRoute {
579 for (auto delegate : self.delegates) { 691 for (auto delegate : self.delegates) {
580 [delegate audioSessionDidChangeRoute:self 692 SEL sel = @selector(audioSessionDidChangeRoute:reason:previousRoute:);
581 reason:reason 693 if ([delegate respondsToSelector:sel]) {
582 previousRoute:previousRoute]; 694 [delegate audioSessionDidChangeRoute:self
695 reason:reason
696 previousRoute:previousRoute];
697 }
583 } 698 }
584 } 699 }
585 700
586 - (void)notifyMediaServicesWereLost { 701 - (void)notifyMediaServicesWereLost {
587 for (auto delegate : self.delegates) { 702 for (auto delegate : self.delegates) {
588 [delegate audioSessionMediaServicesWereLost:self]; 703 SEL sel = @selector(audioSessionMediaServicesWereLost:);
704 if ([delegate respondsToSelector:sel]) {
705 [delegate audioSessionMediaServicesWereLost:self];
706 }
589 } 707 }
590 } 708 }
591 709
592 - (void)notifyMediaServicesWereReset { 710 - (void)notifyMediaServicesWereReset {
593 for (auto delegate : self.delegates) { 711 for (auto delegate : self.delegates) {
594 [delegate audioSessionMediaServicesWereReset:self]; 712 SEL sel = @selector(audioSessionMediaServicesWereReset:);
713 if ([delegate respondsToSelector:sel]) {
714 [delegate audioSessionMediaServicesWereReset:self];
715 }
595 } 716 }
596 } 717 }
597 718
719 - (void)notifyShouldConfigure {
720 for (auto delegate : self.delegates) {
721 SEL sel = @selector(audioSessionShouldConfigure:);
722 if ([delegate respondsToSelector:sel]) {
723 [delegate audioSessionShouldConfigure:self];
724 }
725 }
726 }
727
728 - (void)notifyShouldUnconfigure {
729 for (auto delegate : self.delegates) {
730 SEL sel = @selector(audioSessionShouldUnconfigure:);
731 if ([delegate respondsToSelector:sel]) {
732 [delegate audioSessionShouldUnconfigure:self];
733 }
734 }
735 }
736
737 - (void)notifyDidConfigure {
738 for (auto delegate : self.delegates) {
739 SEL sel = @selector(audioSessionDidConfigure:);
740 if ([delegate respondsToSelector:sel]) {
741 [delegate audioSessionDidConfigure:self];
742 }
743 }
744 }
745
746 - (void)notifyDidUnconfigure {
747 for (auto delegate : self.delegates) {
748 SEL sel = @selector(audioSessionDidUnconfigure:);
749 if ([delegate respondsToSelector:sel]) {
750 [delegate audioSessionDidUnconfigure:self];
751 }
752 }
753 }
754
598 @end 755 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698