forked from mattgallagher/AudioStreamer
/
iOSStreamer.m
148 lines (117 loc) · 4.37 KB
/
iOSStreamer.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//
// iOSStreamer.m
// AudioStreamer
//
// Created by Bo Anderson on 07/09/2012.
//
#import "iOSStreamer.h"
#if defined(DEBUG)
#define LOG(fmt, args...) NSLog(@"%s " fmt, __PRETTY_FUNCTION__, ##args)
#else
#define LOG(...)
#endif
@interface AudioStreamer (iOSStreamer)
- (void)createQueue;
@end
@implementation iOSStreamer
@synthesize delegate=_delegate; // Required
- (BOOL)start {
if (![super start]) return NO;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleInterruption:)
name:AVAudioSessionInterruptionNotification
object:audioSession];
NSError *error;
BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
if (!success)
{
LOG(@"Error setting AVAudioSession category: %@", [error localizedDescription]);
return YES; // The stream can still continue, but we don't get interruption handling.
}
success = [audioSession setActive:YES error:&error];
if (!success)
{
LOG(@"Error activating AVAudioSession: %@", [error localizedDescription]);
}
return YES;
}
- (void)stop {
[super stop];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error;
BOOL success = [audioSession setActive:NO error:&error];
if (!success)
{
LOG(@"Error deactivating AVAudioSession: %@", [error localizedDescription]);
}
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVAudioSessionInterruptionNotification
object:audioSession];
}
- (void)setDelegate:(id<iOSStreamerDelegate>)delegate
{
[super setDelegate:delegate];
_delegate = delegate;
}
- (void)handleInterruption:(NSNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
AVAudioSessionInterruptionType interruptionType = [userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
switch (interruptionType) {
case AVAudioSessionInterruptionTypeBegan:
if ([self isPlaying])
{
LOG(@"Interrupted");
_interrupted = YES;
__strong id <iOSStreamerDelegate> delegate = _delegate;
BOOL override;
if (delegate && [delegate respondsToSelector:@selector(streamerInterruptionDidBegin:)]) {
override = [delegate streamerInterruptionDidBegin:self];
} else {
override = NO;
}
if (override) return;
[self pause];
}
break;
case AVAudioSessionInterruptionTypeEnded:
if ([self isPaused] && _interrupted)
{
LOG(@"Interruption ended");
_interrupted = NO;
AVAudioSessionInterruptionOptions flags = [userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
__strong id <iOSStreamerDelegate> delegate = _delegate;
BOOL override;
if (delegate && [delegate respondsToSelector:@selector(streamer:interruptionDidEndWithFlags:)]) {
override = [delegate streamer:self interruptionDidEndWithFlags:flags];
} else {
override = NO;
}
if (override) return;
if (flags & AVAudioSessionInterruptionOptionShouldResume)
{
LOG(@"Resuming after interruption...");
[self play];
}
else
{
LOG(@"Not resuming after interruption");
[self stop];
}
}
break;
default:
break;
}
}
- (void)createQueue
{
[super createQueue];
/* "Prefer" hardware playback but not "require" it.
* This means that streams can use software playback if hardware is unavailable.
* This allows for concurrent streams */
UInt32 propVal = kAudioQueueHardwareCodecPolicy_PreferHardware;
AudioQueueSetProperty(audioQueue, kAudioQueueProperty_HardwareCodecPolicy, &propVal, sizeof(propVal));
}
@end