Fixed finished event not raised if item is only item in queue. New timeline method of checking whether item has finished playing won't work if item is the only or last item in the queue as handleAudioQueueOutput isn't called once last packet is played (it's gets called if other items are queued). If no items are queued, the queue is stopped and the queue property event is used to figure out if its finished playing

This commit is contained in:
Thong Nguyen 2014-01-22 19:34:17 +00:00
parent a9bd3557a5
commit dde738dffd
5 changed files with 147 additions and 29 deletions

View File

@ -47,13 +47,24 @@
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
// NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
NSURL* url = [NSURL URLWithString:@"file:///Users/tum/testaudio.aac"];
STKAutoRecoveringHttpDataSource* dataSource = [[STKAutoRecoveringHttpDataSource alloc] initWithHttpDataSource:(STKHttpDataSource*)[audioPlayer dataSourceFromURL:url]];
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
-(void) audioPlayerViewQueueFromHTTPSelected:(AudioPlayerView*)audioPlayerView
{
// NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
NSURL* url = [NSURL URLWithString:@"file:///Users/tum/testaudio.aac"];
STKAutoRecoveringHttpDataSource* dataSource = [[STKAutoRecoveringHttpDataSource alloc] initWithHttpDataSource:(STKHttpDataSource*)[audioPlayer dataSourceFromURL:url]];
[audioPlayer queueDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView
{
NSString * path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"];
@ -62,5 +73,4 @@
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
@end

View File

@ -39,6 +39,7 @@
@protocol AudioPlayerViewDelegate<NSObject>
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewQueueFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView;
@end
@ -49,6 +50,7 @@
UISlider* slider;
UIButton* playButton;
UIButton* playFromHTTPButton;
UIButton* queueFromHttpButton;
UIButton* playFromLocalFileButton;
}

View File

@ -64,7 +64,12 @@
playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 50, size.width, size.height);
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
queueFromHttpButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
queueFromHttpButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 100, size.width, size.height);
[queueFromHttpButton addTarget:self action:@selector(queueFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[queueFromHttpButton setTitle:@"Queue from HTTP" forState:UIControlStateNormal];
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playButton.frame = CGRectMake((320 - size.width) / 2, 350, size.width, size.height);
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
@ -77,6 +82,7 @@
[self addSubview:playButton];
[self addSubview:playFromHTTPButton];
[self addSubview:playFromLocalFileButton];
[self addSubview:queueFromHttpButton];
[self setupTimer];
[self updateControls];
@ -129,6 +135,12 @@
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
}
-(void) queueFromHTTPButtonTouched
{
[self.delegate audioPlayerViewQueueFromHTTPSelected:self];
}
-(void) playButtonPressed
{
if (!audioPlayer)
@ -211,9 +223,9 @@
SampleQueueId* queueId = (SampleQueueId*)queueItemId;
NSLog(@"Requeuing: %@", [queueId.url description]);
// NSLog(@"Requeuing: %@", [queueId.url description]);
[self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]];
// [self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]];
}
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration

View File

@ -55,9 +55,10 @@ typedef enum
AudioPlayerInternalStatePaused = (1 << 5) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateRebuffering = (1 << 6) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateStopping = (1 << 7),
AudioPlayerInternalStateStopped = (1 << 8),
AudioPlayerInternalStateDisposed = (1 << 9),
AudioPlayerInternalStateError = (1 << 10)
AudioPlayerInternalStateFlushingAndStoppingButStillPlaying = (1 << 9),
AudioPlayerInternalStateStopped = (1 << 10),
AudioPlayerInternalStateDisposed = (1 << 11),
AudioPlayerInternalStateError = (1 << 12)
}
AudioPlayerInternalState;

View File

@ -327,6 +327,7 @@ AudioQueueBufferRefLookupEntry;
-(void) resetAudioQueueWithReason:(NSString*)reason;
-(BOOL) startAudioQueue;
-(void) stopAudioQueueWithReason:(NSString*)reason;
-(void) stopAudioQueueWithReason:(NSString*)reason immediately:(BOOL)immediately;
-(BOOL) processRunloop;
-(void) wakeupPlaybackThread;
-(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry;
@ -406,6 +407,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
case AudioPlayerInternalStateWaitingForQueueToStart:
case AudioPlayerInternalStatePlaying:
case AudioPlayerInternalStateRebuffering:
case AudioPlayerInternalStateFlushingAndStoppingButStillPlaying:
newState = AudioPlayerStatePlaying;
break;
case AudioPlayerInternalStateStopping:
@ -701,6 +703,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[currentlyReadingEntry updateAudioDataSource];
}
break;
case kAudioFileStreamProperty_FileFormat:
{
char fileFormat[4];
UInt32 fileFormatSize = sizeof(fileFormat);
AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FileFormat, &fileFormatSize, &fileFormat);
NSLog(@"");
}
break;
case kAudioFileStreamProperty_DataFormat:
{
if (currentlyReadingEntry->audioStreamBasicDescription.mSampleRate == 0) {
@ -958,6 +969,27 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
}
-(BOOL) processFinishedPlaying:(STKQueueEntry*)entry
{
NSLog(@"Finished playing");
entry.lastFrameIndex = -1;
if (playbackThread)
{
CFRunLoopPerformBlock([playbackThreadRunLoop getCFRunLoop], NSDefaultRunLoopMode, ^
{
[self audioQueueFinishedPlaying:entry];
});
CFRunLoopWakeUp([playbackThreadRunLoop getCFRunLoop]);
return YES;
}
return NO;
}
-(void) handleAudioQueueOutput:(AudioQueueRef)audioQueueIn buffer:(AudioQueueBufferRef)bufferIn
{
int bufferIndex = -1;
@ -1024,7 +1056,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
signal = YES;
}
if (!audioQueueFlushing && [self progress] > 4.0 && numberOfBuffersUsed == 0 )
if (!audioQueueFlushing && [self progress] > 4.0 && numberOfBuffersUsed == 0 && self.internalState == AudioPlayerInternalStatePlaying)
{
self.internalState = AudioPlayerInternalStateRebuffering;
}
@ -1035,19 +1067,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
if ([self currentTimeInFrames] > entry.lastFrameIndex && entry.lastFrameIndex != -1)
{
NSLog(@"Finished playing");
entry.lastFrameIndex = -1;
if (playbackThread)
if ([self processFinishedPlaying:entry])
{
CFRunLoopPerformBlock([playbackThreadRunLoop getCFRunLoop], NSDefaultRunLoopMode, ^
{
[self audioQueueFinishedPlaying:entry];
});
CFRunLoopWakeUp([playbackThreadRunLoop getCFRunLoop]);
signal = YES;
}
}
@ -1117,10 +1138,33 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
self.internalState = AudioPlayerInternalStateStopped;
}
else if (![self audioQueueIsRunning] && self.internalState == AudioPlayerInternalStateFlushingAndStoppingButStillPlaying)
{
NSRunLoop* runLoop = playbackThreadRunLoop;
if (runLoop)
{
CFRunLoopPerformBlock([runLoop getCFRunLoop], NSDefaultRunLoopMode, ^
{
if (audioQueue)
{
[self stopAudioQueueWithReason:@"handlePropertyChangeForQueue`2"];
}
if (currentlyPlayingEntry)
{
self->stopReason = AudioPlayerStopReasonEof;
self.internalState = AudioPlayerInternalStateStopped;
[self processFinishedPlaying:currentlyPlayingEntry];
}
});
CFRunLoopWakeUp([runLoop getCFRunLoop]);
}
}
else if (self.internalState == AudioPlayerInternalStateWaitingForQueueToStart)
{
[NSRunLoop currentRunLoop];
self.internalState = AudioPlayerInternalStatePlaying;
}
}
@ -1245,6 +1289,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
OSStatus error;
NSLog(@"CreateAudioQueue");
[self startSystemBackgroundTask];
if (audioQueue)
@ -1520,6 +1566,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[self resetAudioQueueWithReason:@"from setCurrentlyReadingEntry"];
pthread_mutex_lock(&queueBuffersMutex);
if (![self audioQueueIsRunning])
{
[self startAudioQueue];
}
}
}
@ -1572,6 +1623,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[self processDidFinishPlaying:entry withNext:next];
}
pthread_mutex_unlock(&queueBuffersMutex);
[self processRunloop];
}
pthread_mutex_unlock(&playerMutex);
}
@ -1647,8 +1700,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
-(BOOL) processRunloop
{
BOOL dontPlayNew = NO;
pthread_mutex_lock(&playerMutex);
{
dontPlayNew = self.internalState == AudioPlayerInternalStateFlushingAndStoppingButStillPlaying;
if (self.internalState == AudioPlayerInternalStatePaused)
{
pthread_mutex_unlock(&playerMutex);
@ -1730,7 +1787,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[self resetAudioQueueWithReason:@"from processRunLoop"];
}
else if (currentlyReadingEntry == nil)
else if (currentlyReadingEntry == nil && !dontPlayNew)
{
BOOL nextIsIncompatible = NO;
@ -1792,7 +1849,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
pthread_mutex_unlock(&playerMutex);
if (currentlyReadingEntry && currentlyReadingEntry->parsedHeader && currentlyReadingEntry != currentlyPlayingEntry)
if (currentlyReadingEntry && currentlyReadingEntry->parsedHeader && currentlyReadingEntry != currentlyPlayingEntry && !dontPlayNew)
{
if (currentAudioStreamBasicDescription.mSampleRate != 0)
{
@ -1973,6 +2030,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
-(void) stopAudioQueueWithReason:(NSString*)reason
{
[self stopAudioQueueWithReason:reason immediately:YES];
}
-(void) stopAudioQueueWithReason:(NSString*)reason immediately:(BOOL)immediately
{
OSStatus error;
@ -1990,8 +2052,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
audioQueueFlushing = YES;
error = AudioQueueStop(audioQueue, true);
error = error | AudioQueueDispose(audioQueue, true);
error = AudioQueueStop(audioQueue, immediately);
if (immediately)
{
error = error | AudioQueueDispose(audioQueue, YES);
}
audioQueue = nil;
}
@ -2172,17 +2238,44 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[self enqueueBuffer];
}
NSLog(@"dataSourceEof");
[self logInfo:[NSString stringWithFormat:@"dataSourceEof for dataSource: %@", dataSourceIn]];
NSObject* queueItemId = currentlyReadingEntry.queueItemId;
dispatch_async(dispatch_get_main_queue(), ^
dispatch_sync(dispatch_get_main_queue(), ^
{
[self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId];
});
pthread_mutex_lock(&playerMutex);
{
BOOL stopping = NO;
BOOL everythingInBufferingQueueBuffered = YES;
for (int i = 0; i < bufferingQueue.count; i++)
{
if ([[bufferingQueue objectAtIndex:i] lastFrameIndex] == -1 && [bufferingQueue objectAtIndex:i] != currentlyReadingEntry)
{
everythingInBufferingQueueBuffered = NO;
}
}
if ([upcomingQueue peek] == nil && everythingInBufferingQueueBuffered)
{
self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying;
if (audioQueue)
{
NSLog(@"stopping audio queue asynchronously in dataSourceEof");
AudioQueueStop(audioQueue, NO);
stopping = YES;
}
}
if (audioQueue)
{
currentlyReadingEntry.lastFrameIndex = self->framesQueued;
@ -2192,7 +2285,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
if (audioQueue)
{
if (![self audioQueueIsRunning])
if (![self audioQueueIsRunning] && !stopping)
{
[self logInfo:@"startAudioQueue from dataSourceEof"];