Cog Audio: Dealt with a major retain cycle leak
This seals up a major memory leak of the playback state whenever a chain is released on stop or on manual track change. CogAudioMulti was retaining the input node due to its listeners, and InputNode was not releasing the listeners when asked to stop running. This is fixed now. Fixes #221 Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
77a079bd53
commit
9e5a70c9ae
7 changed files with 53 additions and 13 deletions
|
@ -68,7 +68,10 @@
|
||||||
ALog(@"Opening file for playback: %@ at seek offset %f%@", url, time, (paused) ? @", starting paused" : @"");
|
ALog(@"Opening file for playback: %@ at seek offset %f%@", url, time, (paused) ? @", starting paused" : @"");
|
||||||
|
|
||||||
[self waitUntilCallbacksExit];
|
[self waitUntilCallbacksExit];
|
||||||
output = nil;
|
if (output) {
|
||||||
|
[output setShouldContinue:NO];
|
||||||
|
output = nil;
|
||||||
|
}
|
||||||
output = [[OutputNode alloc] initWithController:self previous:nil];
|
output = [[OutputNode alloc] initWithController:self previous:nil];
|
||||||
[output setup];
|
[output setup];
|
||||||
[output setVolume: volume];
|
[output setVolume: volume];
|
||||||
|
@ -135,6 +138,20 @@
|
||||||
//Set shouldoContinue to NO on all things
|
//Set shouldoContinue to NO on all things
|
||||||
[self setShouldContinue:NO];
|
[self setShouldContinue:NO];
|
||||||
[self setPlaybackStatus:CogStatusStopped waitUntilDone:YES];
|
[self setPlaybackStatus:CogStatusStopped waitUntilDone:YES];
|
||||||
|
|
||||||
|
@synchronized(chainQueue) {
|
||||||
|
for (id anObject in chainQueue)
|
||||||
|
{
|
||||||
|
[anObject setShouldContinue:NO];
|
||||||
|
}
|
||||||
|
[chainQueue removeAllObjects];
|
||||||
|
endOfInputReached = NO;
|
||||||
|
if (bufferChain)
|
||||||
|
{
|
||||||
|
bufferChain = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pause
|
- (void)pause
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
BOOL shouldSeek;
|
BOOL shouldSeek;
|
||||||
long seekFrame;
|
long seekFrame;
|
||||||
|
|
||||||
|
BOOL observersAdded;
|
||||||
|
|
||||||
Semaphore *exitAtTheEndOfTheStream;
|
Semaphore *exitAtTheEndOfTheStream;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,8 @@
|
||||||
forKeyPath:@"metadata"
|
forKeyPath:@"metadata"
|
||||||
options:(NSKeyValueObservingOptionNew)
|
options:(NSKeyValueObservingOptionNew)
|
||||||
context:NULL];
|
context:NULL];
|
||||||
|
|
||||||
|
observersAdded = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
|
@ -219,8 +221,19 @@
|
||||||
|
|
||||||
- (void)removeObservers
|
- (void)removeObservers
|
||||||
{
|
{
|
||||||
[decoder removeObserver:self forKeyPath:@"properties"];
|
if (observersAdded)
|
||||||
[decoder removeObserver:self forKeyPath:@"metadata"];
|
{
|
||||||
|
[decoder removeObserver:self forKeyPath:@"properties"];
|
||||||
|
[decoder removeObserver:self forKeyPath:@"metadata"];
|
||||||
|
observersAdded = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setShouldContinue:(BOOL)s
|
||||||
|
{
|
||||||
|
[super setShouldContinue:s];
|
||||||
|
if (!s)
|
||||||
|
[self removeObservers];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
|
|
|
@ -137,6 +137,7 @@
|
||||||
- (void)close
|
- (void)close
|
||||||
{
|
{
|
||||||
[output stop];
|
[output stop];
|
||||||
|
output = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setVolume:(double) v
|
- (void)setVolume:(double) v
|
||||||
|
|
|
@ -108,10 +108,11 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
||||||
- (void)close
|
- (void)close
|
||||||
{
|
{
|
||||||
if ( theDecoder != nil ) {
|
if ( theDecoder != nil ) {
|
||||||
[theDecoder close];
|
|
||||||
for (NSDictionary *obsItem in cachedObservers) {
|
for (NSDictionary *obsItem in cachedObservers) {
|
||||||
[theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]];
|
[theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]];
|
||||||
}
|
}
|
||||||
|
[cachedObservers removeAllObjects];
|
||||||
|
[theDecoder close];
|
||||||
theDecoder = nil;
|
theDecoder = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
atomic_long bytesHdcdSustained;
|
atomic_long bytesHdcdSustained;
|
||||||
|
|
||||||
BOOL listenerapplied;
|
BOOL listenerapplied;
|
||||||
|
BOOL observersapplied;
|
||||||
|
|
||||||
float volume;
|
float volume;
|
||||||
|
|
||||||
|
|
|
@ -174,10 +174,7 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
||||||
#ifdef OUTPUT_LOG
|
#ifdef OUTPUT_LOG
|
||||||
_logFile = fopen("/tmp/CogAudioLog.raw", "wb");
|
_logFile = fopen("/tmp/CogAudioLog.raw", "wb");
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:NULL];
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:NULL];
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -704,6 +701,10 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
|
|
||||||
[_au allocateRenderResourcesAndReturnError:&err];
|
[_au allocateRenderResourcesAndReturnError:&err];
|
||||||
|
|
||||||
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:NULL];
|
||||||
|
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:NULL];
|
||||||
|
observersapplied = YES;
|
||||||
|
|
||||||
return (err == nil);
|
return (err == nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,6 +733,12 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
|
|
||||||
- (void)stop
|
- (void)stop
|
||||||
{
|
{
|
||||||
|
stopInvoked = YES;
|
||||||
|
if (observersapplied) {
|
||||||
|
observersapplied = NO;
|
||||||
|
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice"];
|
||||||
|
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable"];
|
||||||
|
}
|
||||||
if (stopNext && started && !paused) {
|
if (stopNext && started && !paused) {
|
||||||
while (![[outputController buffer] isEmpty]) {
|
while (![[outputController buffer] isEmpty]) {
|
||||||
[writeSemaphore signal];
|
[writeSemaphore signal];
|
||||||
|
@ -743,7 +750,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
stopNext = NO;
|
stopNext = NO;
|
||||||
[self signalEndOfStream];
|
[self signalEndOfStream];
|
||||||
}
|
}
|
||||||
stopInvoked = YES;
|
|
||||||
stopping = YES;
|
stopping = YES;
|
||||||
paused = NO;
|
paused = NO;
|
||||||
[writeSemaphore signal];
|
[writeSemaphore signal];
|
||||||
|
@ -782,14 +788,13 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
||||||
_logFile = NULL;
|
_logFile = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
outputController = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[self stop];
|
if (!stopInvoked)
|
||||||
|
[self stop];
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice"];
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pause
|
- (void)pause
|
||||||
|
|
Loading…
Reference in a new issue