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 #import <UIKit/UIKit.h> |
| 14 |
13 #include "webrtc/base/atomicops.h" | 15 #include "webrtc/base/atomicops.h" |
14 #include "webrtc/base/checks.h" | 16 #include "webrtc/base/checks.h" |
15 #include "webrtc/base/criticalsection.h" | 17 #include "webrtc/base/criticalsection.h" |
16 #include "webrtc/modules/audio_device/ios/audio_device_ios.h" | 18 #include "webrtc/modules/audio_device/ios/audio_device_ios.h" |
17 | 19 |
18 #import "WebRTC/RTCLogging.h" | 20 #import "WebRTC/RTCLogging.h" |
19 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h" | 21 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h" |
20 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h" | 22 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h" |
21 | 23 |
22 NSString * const kRTCAudioSessionErrorDomain = @"org.webrtc.RTCAudioSession"; | 24 NSString * const kRTCAudioSessionErrorDomain = @"org.webrtc.RTCAudioSession"; |
23 NSInteger const kRTCAudioSessionErrorLockRequired = -1; | 25 NSInteger const kRTCAudioSessionErrorLockRequired = -1; |
24 NSInteger const kRTCAudioSessionErrorConfiguration = -2; | 26 NSInteger const kRTCAudioSessionErrorConfiguration = -2; |
25 | 27 |
26 // This class needs to be thread-safe because it is accessed from many threads. | 28 // This class needs to be thread-safe because it is accessed from many threads. |
27 // TODO(tkchin): Consider more granular locking. We're not expecting a lot of | 29 // TODO(tkchin): Consider more granular locking. We're not expecting a lot of |
28 // lock contention so coarse locks should be fine for now. | 30 // lock contention so coarse locks should be fine for now. |
29 @implementation RTCAudioSession { | 31 @implementation RTCAudioSession { |
30 rtc::CriticalSection _crit; | 32 rtc::CriticalSection _crit; |
31 AVAudioSession *_session; | 33 AVAudioSession *_session; |
32 volatile int _activationCount; | 34 volatile int _activationCount; |
33 volatile int _lockRecursionCount; | 35 volatile int _lockRecursionCount; |
34 volatile int _webRTCSessionCount; | 36 volatile int _webRTCSessionCount; |
35 BOOL _isActive; | 37 BOOL _isActive; |
36 BOOL _useManualAudio; | 38 BOOL _useManualAudio; |
37 BOOL _isAudioEnabled; | 39 BOOL _isAudioEnabled; |
38 BOOL _canPlayOrRecord; | 40 BOOL _canPlayOrRecord; |
| 41 BOOL _isInterrupted; |
39 } | 42 } |
40 | 43 |
41 @synthesize session = _session; | 44 @synthesize session = _session; |
42 @synthesize delegates = _delegates; | 45 @synthesize delegates = _delegates; |
43 | 46 |
44 + (instancetype)sharedInstance { | 47 + (instancetype)sharedInstance { |
45 static dispatch_once_t onceToken; | 48 static dispatch_once_t onceToken; |
46 static RTCAudioSession *sharedInstance = nil; | 49 static RTCAudioSession *sharedInstance = nil; |
47 dispatch_once(&onceToken, ^{ | 50 dispatch_once(&onceToken, ^{ |
48 sharedInstance = [[self alloc] init]; | 51 sharedInstance = [[self alloc] init]; |
(...skipping 16 matching lines...) Expand all Loading... |
65 object:nil]; | 68 object:nil]; |
66 // TODO(tkchin): Maybe listen to SilenceSecondaryAudioHintNotification. | 69 // TODO(tkchin): Maybe listen to SilenceSecondaryAudioHintNotification. |
67 [center addObserver:self | 70 [center addObserver:self |
68 selector:@selector(handleMediaServicesWereLost:) | 71 selector:@selector(handleMediaServicesWereLost:) |
69 name:AVAudioSessionMediaServicesWereLostNotification | 72 name:AVAudioSessionMediaServicesWereLostNotification |
70 object:nil]; | 73 object:nil]; |
71 [center addObserver:self | 74 [center addObserver:self |
72 selector:@selector(handleMediaServicesWereReset:) | 75 selector:@selector(handleMediaServicesWereReset:) |
73 name:AVAudioSessionMediaServicesWereResetNotification | 76 name:AVAudioSessionMediaServicesWereResetNotification |
74 object:nil]; | 77 object:nil]; |
| 78 // Also track foreground event in order to deal with interruption ended situ
ation. |
| 79 [center addObserver:self |
| 80 selector:@selector(handleApplicationDidBecomeActive:) |
| 81 name:UIApplicationDidBecomeActiveNotification |
| 82 object:nil]; |
75 } | 83 } |
76 return self; | 84 return self; |
77 } | 85 } |
78 | 86 |
79 - (void)dealloc { | 87 - (void)dealloc { |
80 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 88 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
81 } | 89 } |
82 | 90 |
83 - (NSString *)description { | 91 - (NSString *)description { |
84 NSString *format = | 92 NSString *format = |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
427 | 435 |
428 - (void)handleInterruptionNotification:(NSNotification *)notification { | 436 - (void)handleInterruptionNotification:(NSNotification *)notification { |
429 NSNumber* typeNumber = | 437 NSNumber* typeNumber = |
430 notification.userInfo[AVAudioSessionInterruptionTypeKey]; | 438 notification.userInfo[AVAudioSessionInterruptionTypeKey]; |
431 AVAudioSessionInterruptionType type = | 439 AVAudioSessionInterruptionType type = |
432 (AVAudioSessionInterruptionType)typeNumber.unsignedIntegerValue; | 440 (AVAudioSessionInterruptionType)typeNumber.unsignedIntegerValue; |
433 switch (type) { | 441 switch (type) { |
434 case AVAudioSessionInterruptionTypeBegan: | 442 case AVAudioSessionInterruptionTypeBegan: |
435 RTCLog(@"Audio session interruption began."); | 443 RTCLog(@"Audio session interruption began."); |
436 self.isActive = NO; | 444 self.isActive = NO; |
| 445 self.isInterrupted = YES; |
437 [self notifyDidBeginInterruption]; | 446 [self notifyDidBeginInterruption]; |
438 break; | 447 break; |
439 case AVAudioSessionInterruptionTypeEnded: { | 448 case AVAudioSessionInterruptionTypeEnded: { |
440 RTCLog(@"Audio session interruption ended."); | 449 RTCLog(@"Audio session interruption ended."); |
| 450 self.isInterrupted = NO; |
441 [self updateAudioSessionAfterEvent]; | 451 [self updateAudioSessionAfterEvent]; |
442 NSNumber *optionsNumber = | 452 NSNumber *optionsNumber = |
443 notification.userInfo[AVAudioSessionInterruptionOptionKey]; | 453 notification.userInfo[AVAudioSessionInterruptionOptionKey]; |
444 AVAudioSessionInterruptionOptions options = | 454 AVAudioSessionInterruptionOptions options = |
445 optionsNumber.unsignedIntegerValue; | 455 optionsNumber.unsignedIntegerValue; |
446 BOOL shouldResume = | 456 BOOL shouldResume = |
447 options & AVAudioSessionInterruptionOptionShouldResume; | 457 options & AVAudioSessionInterruptionOptionShouldResume; |
448 [self notifyDidEndInterruptionWithShouldResumeSession:shouldResume]; | 458 [self notifyDidEndInterruptionWithShouldResumeSession:shouldResume]; |
449 break; | 459 break; |
450 } | 460 } |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 [self updateAudioSessionAfterEvent]; | 508 [self updateAudioSessionAfterEvent]; |
499 [self notifyMediaServicesWereLost]; | 509 [self notifyMediaServicesWereLost]; |
500 } | 510 } |
501 | 511 |
502 - (void)handleMediaServicesWereReset:(NSNotification *)notification { | 512 - (void)handleMediaServicesWereReset:(NSNotification *)notification { |
503 RTCLog(@"Media services were reset."); | 513 RTCLog(@"Media services were reset."); |
504 [self updateAudioSessionAfterEvent]; | 514 [self updateAudioSessionAfterEvent]; |
505 [self notifyMediaServicesWereReset]; | 515 [self notifyMediaServicesWereReset]; |
506 } | 516 } |
507 | 517 |
| 518 - (void)handleApplicationDidBecomeActive:(NSNotification *)notification { |
| 519 if (self.isInterrupted) { |
| 520 RTCLog(@"Application became active after an interruption. Treating as interr
uption end."); |
| 521 self.isInterrupted = NO; |
| 522 [self updateAudioSessionAfterEvent]; |
| 523 [self notifyDidEndInterruptionWithShouldResumeSession:YES]; |
| 524 } |
| 525 } |
| 526 |
508 #pragma mark - Private | 527 #pragma mark - Private |
509 | 528 |
510 + (NSError *)lockError { | 529 + (NSError *)lockError { |
511 NSDictionary *userInfo = @{ | 530 NSDictionary *userInfo = @{ |
512 NSLocalizedDescriptionKey: | 531 NSLocalizedDescriptionKey: |
513 @"Must call lockForConfiguration before calling this method." | 532 @"Must call lockForConfiguration before calling this method." |
514 }; | 533 }; |
515 NSError *error = | 534 NSError *error = |
516 [[NSError alloc] initWithDomain:kRTCAudioSessionErrorDomain | 535 [[NSError alloc] initWithDomain:kRTCAudioSessionErrorDomain |
517 code:kRTCAudioSessionErrorLockRequired | 536 code:kRTCAudioSessionErrorLockRequired |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 } | 577 } |
559 | 578 |
560 - (int)webRTCSessionCount { | 579 - (int)webRTCSessionCount { |
561 return _webRTCSessionCount; | 580 return _webRTCSessionCount; |
562 } | 581 } |
563 | 582 |
564 - (BOOL)canPlayOrRecord { | 583 - (BOOL)canPlayOrRecord { |
565 return !self.useManualAudio || self.isAudioEnabled; | 584 return !self.useManualAudio || self.isAudioEnabled; |
566 } | 585 } |
567 | 586 |
| 587 - (BOOL)isInterrupted { |
| 588 @synchronized(self) { |
| 589 return _isInterrupted; |
| 590 } |
| 591 } |
| 592 |
| 593 - (void)setIsInterrupted:(BOOL)isInterrupted { |
| 594 @synchronized(self) { |
| 595 if (_isInterrupted == isInterrupted) { |
| 596 return; |
| 597 } |
| 598 _isInterrupted = isInterrupted; |
| 599 } |
| 600 } |
| 601 |
568 - (BOOL)checkLock:(NSError **)outError { | 602 - (BOOL)checkLock:(NSError **)outError { |
569 // Check ivar instead of trying to acquire lock so that we won't accidentally | 603 // Check ivar instead of trying to acquire lock so that we won't accidentally |
570 // acquire lock if it hasn't already been called. | 604 // acquire lock if it hasn't already been called. |
571 if (!self.isLocked) { | 605 if (!self.isLocked) { |
572 if (outError) { | 606 if (outError) { |
573 *outError = [RTCAudioSession lockError]; | 607 *outError = [RTCAudioSession lockError]; |
574 } | 608 } |
575 return NO; | 609 return NO; |
576 } | 610 } |
577 return YES; | 611 return YES; |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
788 - (void)notifyDidStopPlayOrRecord { | 822 - (void)notifyDidStopPlayOrRecord { |
789 for (auto delegate : self.delegates) { | 823 for (auto delegate : self.delegates) { |
790 SEL sel = @selector(audioSessionDidStopPlayOrRecord:); | 824 SEL sel = @selector(audioSessionDidStopPlayOrRecord:); |
791 if ([delegate respondsToSelector:sel]) { | 825 if ([delegate respondsToSelector:sel]) { |
792 [delegate audioSessionDidStopPlayOrRecord:self]; | 826 [delegate audioSessionDidStopPlayOrRecord:self]; |
793 } | 827 } |
794 } | 828 } |
795 } | 829 } |
796 | 830 |
797 @end | 831 @end |
OLD | NEW |