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
|
||||
{
|
||||
@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];
|
||||
|
|
|
@ -59,4 +59,6 @@
|
|||
|
||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format;
|
||||
|
||||
- (BOOL)isRunning;
|
||||
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -17,5 +17,6 @@
|
|||
-(void)signal;
|
||||
-(void)timedWait:(int)seconds;
|
||||
-(void)wait;
|
||||
-(void)waitIndefinitely;
|
||||
|
||||
@end
|
||||
|
|
|
@ -40,4 +40,9 @@
|
|||
semaphore_timedwait(semaphore, t);
|
||||
}
|
||||
|
||||
-(void)waitIndefinitely
|
||||
{
|
||||
semaphore_wait(semaphore);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue