mamburu: Fixed crashes when seeking near the end of stream

CQTexperiment
Chris Moeller 2013-10-12 13:52:58 -07:00
parent 28e55334a6
commit 56848daad6
9 changed files with 108 additions and 11 deletions

View File

@ -232,6 +232,16 @@
- (BOOL)endOfInputReached:(BufferChain *)sender //Sender is a BufferChain
{
@synchronized (chainQueue) {
// No point in constructing new chain for the next playlist entry
// if there's already one at the head of chainQueue... r-r-right?
for (BufferChain *chain in chainQueue)
{
if ([chain isRunning])
{
return YES;
}
}
BufferChain *newChain = nil;
nextStreamUserInfo = [sender userInfo];
@ -286,6 +296,17 @@
[self addChainToQueue:newChain];
[newChain release];
// I'm stupid and can't hold too much stuff in my head all at once, so writing it here.
//
// Once we get here:
// - buffer chain for previous stream finished reading
// - there are (probably) some bytes of the previous stream in the output buffer which haven't been played
// (by output node) yet
// - self.bufferChain == previous playlist entry's buffer chain
// - self.nextStream == next playlist entry's URL
// - self.nextStreamUserInfo == next playlist entry
// - head of chainQueue is the buffer chain for the next entry (which has launched its threads already)
}
return YES;
@ -293,6 +314,11 @@
- (void)endOfInputPlayed
{
// Once we get here:
// - the buffer chain for the next playlist entry (started in endOfInputReached) have been working for some time
// already, so that there is some decoded and converted data to play
// - the buffer chain for the next entry is the first item in chainQueue
@synchronized(chainQueue) {
endOfInputReached = NO;
@ -307,11 +333,13 @@
return;
}
[bufferChain release];
bufferChain = [chainQueue objectAtIndex:0];
BufferChain *oldChain = bufferChain;
bufferChain = [chainQueue objectAtIndex:0];
[oldChain release];
[bufferChain retain];
[chainQueue removeObjectAtIndex:0];
DLog(@"New!!! %@ %@", bufferChain, [[bufferChain inputNode] decoder]);
[chainQueue removeObjectAtIndex:0];

View File

@ -59,4 +59,6 @@
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format;
- (BOOL)isRunning;
@end

View File

@ -125,7 +125,7 @@
[rgInfo release];
[userInfo release];
[streamURL release];
[inputNode release];
[converterNode release];
@ -136,7 +136,7 @@
- (void)seek:(double)time
{
long frame = time * [[[inputNode properties] objectForKey:@"sampleRate"] floatValue];
long frame = (long) round(time * [[[inputNode properties] objectForKey:@"sampleRate"] floatValue]);
[inputNode seek:frame];
}
@ -192,4 +192,14 @@
[converterNode setShouldContinue:s];
}
- (BOOL)isRunning
{
InputNode *theInputNode = [self inputNode];
if (nil != theInputNode && [theInputNode shouldContinue] && ![theInputNode endOfStream])
{
return YES;
}
return NO;
}
@end

View File

@ -98,7 +98,11 @@ static void scale_by_volume(float * buffer, int count, float volume)
}
//called from the complexfill when the audio is converted...good clean fun
static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
static OSStatus ACInputProc(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription** outDataPacketDescription,
void* inUserData)
{
ConverterNode *converter = (ConverterNode *)inUserData;
OSStatus err = noErr;
@ -116,7 +120,9 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket);
if (converter->callbackBuffer != NULL)
{
free(converter->callbackBuffer);
}
converter->callbackBuffer = malloc(amountToWrite);
amountRead = [converter readData:converter->callbackBuffer amount:amountToWrite];
@ -136,7 +142,11 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
return err;
}
static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
static OSStatus ACFloatProc(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription** outDataPacketDescription,
void* inUserData)
{
ConverterNode *converter = (ConverterNode *)inUserData;
OSStatus err = noErr;

View File

@ -16,6 +16,8 @@
#import "Node.h"
#import "Plugin.h"
#define INPUT_NODE_SEEK
@interface InputNode : Node {
id<CogDecoder> decoder;
@ -26,6 +28,8 @@
BOOL shouldSeek;
long seekFrame;
Semaphore *exitAtTheEndOfTheStream;
}
- (BOOL)openWithSource:(id<CogSource>)source;

View File

@ -16,6 +16,16 @@
@implementation InputNode
- (id)initWithController:(id)c previous:(id)p {
self = [super initWithController:c previous:p];
if (self) {
exitAtTheEndOfTheStream = [[Semaphore alloc] init];
}
return self;
}
- (BOOL)openWithSource:(id<CogSource>)source
{
decoder = [AudioDecoder audioDecoderForSource:source];
@ -131,20 +141,39 @@
}
DLog(@"End of stream? %@", [self properties]);
endOfStream = YES;
shouldClose = [controller endOfInputReached]; //Lets us know if we should keep going or not (occassionally, for track changes within a file)
DLog(@"closing? is %i", shouldClose);
break;
// wait before exiting, as we might still get seeking request
DLog("InputNode: Before wait")
[exitAtTheEndOfTheStream waitIndefinitely];
DLog("InputNode: After wait, should seek = %d", shouldSeek)
if (shouldSeek)
{
endOfStream = NO;
shouldClose = NO;
continue;
}
else
{
break;
}
}
[self writeData:inputBuffer amount:amountInBuffer];
amountInBuffer = 0;
}
}
if (shouldClose)
[decoder close];
free(inputBuffer);
free(inputBuffer);
[exitAtTheEndOfTheStream signal];
}
- (void)seek:(long)frame
@ -153,6 +182,11 @@
shouldSeek = YES;
DLog(@"Should seek!");
[semaphore signal];
if (endOfStream)
{
[exitAtTheEndOfTheStream signal];
}
}
- (BOOL)setTrack:(NSURL *)track
@ -169,6 +203,9 @@
- (void)dealloc
{
DLog(@"Input Node dealloc");
[exitAtTheEndOfTheStream signal];
[exitAtTheEndOfTheStream wait]; // wait for decoder to be closed (see -(void)process )
[exitAtTheEndOfTheStream release];
[decoder removeObserver:self forKeyPath:@"properties"];
[decoder removeObserver:self forKeyPath:@"metadata"];

View File

@ -108,7 +108,7 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
sizeof(AudioDeviceID));
if (err != noErr) {
ALog(@"No output device could be found, your random error code is %i. Have a nice day!", err);
ALog(@"No output device could be found, your random error code is %d. Have a nice day!", err);
return NO;
}

View File

@ -17,5 +17,6 @@
-(void)signal;
-(void)timedWait:(int)seconds;
-(void)wait;
-(void)waitIndefinitely;
@end

View File

@ -40,4 +40,9 @@
semaphore_timedwait(semaphore, t);
}
-(void)waitIndefinitely
{
semaphore_wait(semaphore);
}
@end