mamburu: Fixed crashes when seeking near the end of stream
parent
28e55334a6
commit
56848daad6
|
@ -232,6 +232,16 @@
|
||||||
- (BOOL)endOfInputReached:(BufferChain *)sender //Sender is a BufferChain
|
- (BOOL)endOfInputReached:(BufferChain *)sender //Sender is a BufferChain
|
||||||
{
|
{
|
||||||
@synchronized (chainQueue) {
|
@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;
|
BufferChain *newChain = nil;
|
||||||
|
|
||||||
nextStreamUserInfo = [sender userInfo];
|
nextStreamUserInfo = [sender userInfo];
|
||||||
|
@ -286,6 +296,17 @@
|
||||||
[self addChainToQueue:newChain];
|
[self addChainToQueue:newChain];
|
||||||
|
|
||||||
[newChain release];
|
[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;
|
return YES;
|
||||||
|
@ -293,6 +314,11 @@
|
||||||
|
|
||||||
- (void)endOfInputPlayed
|
- (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) {
|
@synchronized(chainQueue) {
|
||||||
endOfInputReached = NO;
|
endOfInputReached = NO;
|
||||||
|
|
||||||
|
@ -307,11 +333,13 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[bufferChain release];
|
|
||||||
|
BufferChain *oldChain = bufferChain;
|
||||||
bufferChain = [chainQueue objectAtIndex:0];
|
bufferChain = [chainQueue objectAtIndex:0];
|
||||||
|
[oldChain release];
|
||||||
[bufferChain retain];
|
[bufferChain retain];
|
||||||
|
|
||||||
|
[chainQueue removeObjectAtIndex:0];
|
||||||
DLog(@"New!!! %@ %@", bufferChain, [[bufferChain inputNode] decoder]);
|
DLog(@"New!!! %@ %@", bufferChain, [[bufferChain inputNode] decoder]);
|
||||||
|
|
||||||
[chainQueue removeObjectAtIndex:0];
|
[chainQueue removeObjectAtIndex:0];
|
||||||
|
|
|
@ -59,4 +59,6 @@
|
||||||
|
|
||||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format;
|
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format;
|
||||||
|
|
||||||
|
- (BOOL)isRunning;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -125,7 +125,7 @@
|
||||||
[rgInfo release];
|
[rgInfo release];
|
||||||
[userInfo release];
|
[userInfo release];
|
||||||
[streamURL release];
|
[streamURL release];
|
||||||
|
|
||||||
[inputNode release];
|
[inputNode release];
|
||||||
[converterNode release];
|
[converterNode release];
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
|
|
||||||
- (void)seek:(double)time
|
- (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];
|
[inputNode seek:frame];
|
||||||
}
|
}
|
||||||
|
@ -192,4 +192,14 @@
|
||||||
[converterNode setShouldContinue:s];
|
[converterNode setShouldContinue:s];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)isRunning
|
||||||
|
{
|
||||||
|
InputNode *theInputNode = [self inputNode];
|
||||||
|
if (nil != theInputNode && [theInputNode shouldContinue] && ![theInputNode endOfStream])
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -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
|
//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;
|
ConverterNode *converter = (ConverterNode *)inUserData;
|
||||||
OSStatus err = noErr;
|
OSStatus err = noErr;
|
||||||
|
@ -116,7 +120,9 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
|
||||||
amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket);
|
amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket);
|
||||||
|
|
||||||
if (converter->callbackBuffer != NULL)
|
if (converter->callbackBuffer != NULL)
|
||||||
|
{
|
||||||
free(converter->callbackBuffer);
|
free(converter->callbackBuffer);
|
||||||
|
}
|
||||||
converter->callbackBuffer = malloc(amountToWrite);
|
converter->callbackBuffer = malloc(amountToWrite);
|
||||||
|
|
||||||
amountRead = [converter readData:converter->callbackBuffer amount:amountToWrite];
|
amountRead = [converter readData:converter->callbackBuffer amount:amountToWrite];
|
||||||
|
@ -136,7 +142,11 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber
|
||||||
return err;
|
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;
|
ConverterNode *converter = (ConverterNode *)inUserData;
|
||||||
OSStatus err = noErr;
|
OSStatus err = noErr;
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#import "Node.h"
|
#import "Node.h"
|
||||||
#import "Plugin.h"
|
#import "Plugin.h"
|
||||||
|
|
||||||
|
#define INPUT_NODE_SEEK
|
||||||
|
|
||||||
@interface InputNode : Node {
|
@interface InputNode : Node {
|
||||||
id<CogDecoder> decoder;
|
id<CogDecoder> decoder;
|
||||||
|
|
||||||
|
@ -26,6 +28,8 @@
|
||||||
|
|
||||||
BOOL shouldSeek;
|
BOOL shouldSeek;
|
||||||
long seekFrame;
|
long seekFrame;
|
||||||
|
|
||||||
|
Semaphore *exitAtTheEndOfTheStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)openWithSource:(id<CogSource>)source;
|
- (BOOL)openWithSource:(id<CogSource>)source;
|
||||||
|
|
|
@ -16,6 +16,16 @@
|
||||||
|
|
||||||
@implementation InputNode
|
@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
|
- (BOOL)openWithSource:(id<CogSource>)source
|
||||||
{
|
{
|
||||||
decoder = [AudioDecoder audioDecoderForSource:source];
|
decoder = [AudioDecoder audioDecoderForSource:source];
|
||||||
|
@ -131,20 +141,39 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
DLog(@"End of stream? %@", [self properties]);
|
DLog(@"End of stream? %@", [self properties]);
|
||||||
|
|
||||||
endOfStream = YES;
|
endOfStream = YES;
|
||||||
shouldClose = [controller endOfInputReached]; //Lets us know if we should keep going or not (occassionally, for track changes within a file)
|
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);
|
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];
|
[self writeData:inputBuffer amount:amountInBuffer];
|
||||||
amountInBuffer = 0;
|
amountInBuffer = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldClose)
|
if (shouldClose)
|
||||||
[decoder close];
|
[decoder close];
|
||||||
|
|
||||||
free(inputBuffer);
|
|
||||||
|
free(inputBuffer);
|
||||||
|
|
||||||
|
[exitAtTheEndOfTheStream signal];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)seek:(long)frame
|
- (void)seek:(long)frame
|
||||||
|
@ -153,6 +182,11 @@
|
||||||
shouldSeek = YES;
|
shouldSeek = YES;
|
||||||
DLog(@"Should seek!");
|
DLog(@"Should seek!");
|
||||||
[semaphore signal];
|
[semaphore signal];
|
||||||
|
|
||||||
|
if (endOfStream)
|
||||||
|
{
|
||||||
|
[exitAtTheEndOfTheStream signal];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)setTrack:(NSURL *)track
|
- (BOOL)setTrack:(NSURL *)track
|
||||||
|
@ -169,6 +203,9 @@
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
DLog(@"Input Node 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:@"properties"];
|
||||||
[decoder removeObserver:self forKeyPath:@"metadata"];
|
[decoder removeObserver:self forKeyPath:@"metadata"];
|
||||||
|
|
|
@ -108,7 +108,7 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
||||||
sizeof(AudioDeviceID));
|
sizeof(AudioDeviceID));
|
||||||
|
|
||||||
if (err != noErr) {
|
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;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,5 +17,6 @@
|
||||||
-(void)signal;
|
-(void)signal;
|
||||||
-(void)timedWait:(int)seconds;
|
-(void)timedWait:(int)seconds;
|
||||||
-(void)wait;
|
-(void)wait;
|
||||||
|
-(void)waitIndefinitely;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -40,4 +40,9 @@
|
||||||
semaphore_timedwait(semaphore, t);
|
semaphore_timedwait(semaphore, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-(void)waitIndefinitely
|
||||||
|
{
|
||||||
|
semaphore_wait(semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue