1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGAudioSessionManager.m
2016-02-25 01:03:51 +01:00

250 lines
8.8 KiB
Objective-C

#import "TGAudioSessionManager.h"
#import <pthread.h>
#import <AVFoundation/AVFoundation.h>
#import "TGTelegraph.h"
@interface TGAudioSessionManager ()
{
pthread_mutex_t _mutex;
int32_t _clientId;
TGAudioSessionType _currentType;
bool _currentActive;
NSMutableArray *_currentClientIds;
NSMutableArray *_currentInterruptedArray;
bool _isInterrupting;
}
@end
@implementation TGAudioSessionManager
+ (TGAudioSessionManager *)instance
{
static TGAudioSessionManager *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
singleton = [[TGAudioSessionManager alloc] init];
});
return singleton;
}
- (instancetype)init
{
self = [super init];
if (self != nil)
{
pthread_mutex_init(&_mutex, NULL);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
}
- (NSString *)nativeCategoryForType:(TGAudioSessionType)type
{
switch (type)
{
case TGAudioSessionTypePlayVoice:
case TGAudioSessionTypePlayMusic:
case TGAudioSessionTypePlayVideo:
return AVAudioSessionCategoryPlayback;
case TGAudioSessionTypePlayAndRecord:
return AVAudioSessionCategoryPlayAndRecord;
case TGAudioSessionTypePlayAndRecordHeadphones:
return AVAudioSessionCategoryPlayAndRecord;
}
}
- (id<SDisposable>)requestSessionWithType:(TGAudioSessionType)type interrupted:(void (^)())interrupted
{
NSArray *interruptedToInvoke = nil;
id<SDisposable> result = nil;
if (type == TGAudioSessionTypePlayVideo) {
[TGTelegraphInstance.musicPlayer controlPause];
}
pthread_mutex_lock(&_mutex);
{
if (_isInterrupting)
{
if (interrupted)
interruptedToInvoke = @[[interrupted copy]];
}
else
{
if (_currentInterruptedArray == nil)
_currentInterruptedArray = [[NSMutableArray alloc] init];
if (_currentClientIds == nil)
_currentClientIds = [[NSMutableArray alloc] init];
int32_t clientId = _clientId++;
if (!_currentActive || _currentType != type)
{
_currentActive = true;
_currentType = type;
interruptedToInvoke = [[NSArray alloc] initWithArray:_currentInterruptedArray];
[_currentInterruptedArray removeAllObjects];
[_currentClientIds removeAllObjects];
NSError *error = nil;
TGLog(@"(TGAudioSessionManager setting category %d active overriding port: %d)", (int)type, ((type == TGAudioSessionTypePlayAndRecordHeadphones || type == TGAudioSessionTypePlayMusic || type == TGAudioSessionTypePlayVideo)) ? 1 : 0);
[[AVAudioSession sharedInstance] setCategory:[self nativeCategoryForType:type] withOptions:(type == TGAudioSessionTypePlayAndRecord || type == TGAudioSessionTypePlayAndRecordHeadphones) ? AVAudioSessionCategoryOptionAllowBluetooth : 0 error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager setting category %d error %@)", (int)type, error);
[[AVAudioSession sharedInstance] setActive:true error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager setting active error %@)", error);
[[AVAudioSession sharedInstance] overrideOutputAudioPort:(type == TGAudioSessionTypePlayAndRecordHeadphones || type == TGAudioSessionTypePlayMusic || type == TGAudioSessionTypePlayVideo) ? AVAudioSessionPortOverrideNone : AVAudioSessionPortOverrideSpeaker error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager override port error %@)", error);
}
if (interrupted)
[_currentInterruptedArray addObject:[interrupted copy]];
else
[_currentInterruptedArray addObject:[^{} copy]];
[_currentClientIds addObject:@(clientId)];
__weak TGAudioSessionManager *weakSelf = self;
result = [[SBlockDisposable alloc] initWithBlock:^
{
__strong TGAudioSessionManager *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf endSessionForClientId:clientId];
}];
}
}
pthread_mutex_unlock(&_mutex);
for (void (^f)() in interruptedToInvoke)
{
f();
}
return result;
}
- (void)cancelCurrentSession
{
NSArray *interruptedToInvoke = nil;
pthread_mutex_lock(&_mutex);
{
_isInterrupting = true;
interruptedToInvoke = [[NSArray alloc] initWithArray:_currentInterruptedArray];
}
pthread_mutex_unlock(&_mutex);
for (void (^f)() in interruptedToInvoke)
{
f();
}
pthread_mutex_lock(&_mutex);
{
_isInterrupting = false;
[_currentClientIds removeAllObjects];
[_currentInterruptedArray removeAllObjects];
_currentActive = false;
_currentType = TGAudioSessionTypePlayMusic;
TGLog(@"(TGAudioSessionManager setting inactive)");
NSError *error = nil;
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager override port error %@)", error);
[[AVAudioSession sharedInstance] setCategory:[self nativeCategoryForType:_currentType] error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager setting category error %@)", error);
[[AVAudioSession sharedInstance] setActive:false error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager setting inactive error %@)", error);
}
pthread_mutex_unlock(&_mutex);
}
- (void)endSessionForClientId:(int32_t)clientId
{
pthread_mutex_lock(&_mutex);
{
for (NSUInteger i = 0; i < _currentClientIds.count; i++)
{
if ([_currentClientIds[i] intValue] == clientId)
{
[_currentInterruptedArray removeObjectAtIndex:i];
[_currentClientIds removeObjectAtIndex:i];
break;
}
}
if (_currentActive && _currentClientIds.count == 0)
{
_currentActive = false;
_currentType = TGAudioSessionTypePlayMusic;
TGLog(@"(TGAudioSessionManager setting inactive)");
NSError *error = nil;
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager override port error %@)", error);
[[AVAudioSession sharedInstance] setCategory:[self nativeCategoryForType:_currentType] error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager setting category error %@)", error);
[[AVAudioSession sharedInstance] setActive:false error:&error];
if (error != nil)
TGLog(@"(TGAudioSessionManager setting inactive error %@)", error);
}
}
pthread_mutex_unlock(&_mutex);
}
+ (SSignal *)routeChange
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AVAudioSessionRouteChangeNotification object:nil queue:nil usingBlock:^(NSNotification *notification)
{
if ([notification.userInfo[AVAudioSessionRouteChangeReasonKey] intValue] == AVAudioSessionRouteChangeReasonOldDeviceUnavailable)
{
[subscriber putNext:@(TGAudioSessionRouteChangePause)];
}
else if ([notification.userInfo[AVAudioSessionRouteChangeReasonKey] intValue] == AVAudioSessionRouteChangeReasonNewDeviceAvailable)
{
[subscriber putNext:@(TGAudioSessionRouteChangeResume)];
}
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
}];
}
- (void)audioSessionInterruption:(NSNotification *)notification
{
NSNumber *interruptionType = (NSNumber *)notification.userInfo[AVAudioSessionInterruptionTypeKey];
if ([interruptionType intValue] == AVAudioSessionInterruptionTypeBegan)
[self cancelCurrentSession];
}
@end