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/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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |