From 96d9c82a8de2f3318f60ce1e50325aef6bad8546 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 15 Jun 2012 00:09:54 -0700 Subject: [PATCH] Add the option of infinitely buffering a stream. --- AudioStreamer/AudioStreamer.h | 2 ++ AudioStreamer/AudioStreamer.m | 54 +++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/AudioStreamer/AudioStreamer.h b/AudioStreamer/AudioStreamer.h index 4ccb179..b06efaa 100644 --- a/AudioStreamer/AudioStreamer.h +++ b/AudioStreamer/AudioStreamer.h @@ -85,6 +85,7 @@ struct queued_packet; AudioFileTypeID fileType; UInt32 bufferSize; /* attempted to be guessed, but fallback here */ UInt32 bufferCnt; + BOOL bufferInfinite; /* Creates as part of the [start] method */ CFReadStreamRef stream; @@ -152,6 +153,7 @@ struct queued_packet; @property (readwrite) UInt32 bufferCnt; @property (readwrite) UInt32 bufferSize; @property (readwrite) AudioFileTypeID fileType; +@property (readwrite) BOOL bufferInfinite; + (NSString*) stringForErrorCode:(AudioStreamerErrorCode)anErrorCode; diff --git a/AudioStreamer/AudioStreamer.m b/AudioStreamer/AudioStreamer.m index 74e264d..e1f16cb 100644 --- a/AudioStreamer/AudioStreamer.m +++ b/AudioStreamer/AudioStreamer.m @@ -170,7 +170,7 @@ - (void)handleReadFromStream:(CFReadStreamRef)aStream @implementation AudioStreamer @synthesize errorCode, networkError, httpHeaders, url, bufferCnt, bufferSize, - fileType; + fileType, bufferInfinite; @synthesize state = state_; /* AudioFileStream callback when properties are available */ @@ -868,7 +868,7 @@ - (BOOL)openReadStream { - (void)handleReadFromStream:(CFReadStreamRef)aStream eventType:(CFStreamEventType)eventType { assert(aStream == stream); - assert(!waitingOnBuffer); + assert(!waitingOnBuffer || bufferInfinite); events++; switch (eventType) { @@ -880,6 +880,8 @@ - (void)handleReadFromStream:(CFReadStreamRef)aStream case kCFStreamEventEndEncountered: LOG(@"end"); + [timeout invalidate]; + timeout = nil; /* Flush out extra data if necessary */ if (bytesFilled) { @@ -891,16 +893,6 @@ - (void)handleReadFromStream:(CFReadStreamRef)aStream /* If we never received any packets, then we fail */ if (state_ == AS_WAITING_FOR_DATA) { [self failWithErrorCode:AS_AUDIO_DATA_NOT_FOUND]; - - /* Flush an asynchronously stop the audio queue now that it won't be - receiving any more data */ - } else { - if (audioQueue) { - err = AudioQueueFlush(audioQueue); - CHECK_ERR(err, AS_AUDIO_QUEUE_FLUSH_FAILED); - err = AudioQueueStop(audioQueue, false); - CHECK_ERR(err, AS_AUDIO_QUEUE_STOP_FAILED); - } } return; @@ -1012,14 +1004,36 @@ - (int) enqueueBuffer { bytesFilled = 0; // reset bytes filled packetsFilled = 0; // reset packets filled + /* If we have no more queued data, and the stream has reached its end, then + we're not going to be enqueueing any more buffers to the audio stream. In + this case flush it out and asynchronously stop it */ + if (queued_head == NULL && + CFReadStreamGetStatus(stream) == kCFStreamStatusAtEnd) { + err = AudioQueueFlush(audioQueue); + if (err) { + [self failWithErrorCode:AS_AUDIO_QUEUE_FLUSH_FAILED]; + return -1; + } + err = AudioQueueStop(audioQueue, false); + if (err) { + [self failWithErrorCode:AS_AUDIO_QUEUE_STOP_FAILED]; + return -1; + } + } + + /* The inuse array is also managed by a separate AudioQueue internal thread, + so we need to synchronize around unscheduling the read stream to ensure + that our state is always coherent */ @synchronized(self) { if (inuse[fillBufferIndex]) { LOG(@"waiting for buffer %d", fillBufferIndex); - CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), - kCFRunLoopCommonModes); - /* Make sure we don't have ourselves marked as rescheduled */ - unscheduled = YES; - rescheduled = NO; + if (!bufferInfinite) { + CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), + kCFRunLoopCommonModes); + /* Make sure we don't have ourselves marked as rescheduled */ + unscheduled = YES; + rescheduled = NO; + } waitingOnBuffer = true; return 0; @@ -1337,8 +1351,10 @@ - (void) enqueueCachedData { if (cur == NULL) { queued_tail = NULL; rescheduled = YES; - CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), - kCFRunLoopCommonModes); + if (!bufferInfinite) { + CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), + kCFRunLoopCommonModes); + } } }