让我们看一下audiostreamer.m的init:
- (id)initWithURL:(NSURL *)aURL
{
self = [super init];
if (self != nil)
{
if (url){
url = nil;
[url release];
}
url = [aURL copy];
#ifdef SHOUTCAST_METADATA
if (metaDataString){
metaDataString = nil;
[metaDataString release];
}
//NSLog(@"%d", [metaDataString retainCount]);
metaDataString = [[NSMutableString alloc] initWithString:@""];
#endif
seekTime = 0;
}
return self;
}
这里将url,metaDataString,seekTime初始化。再看最重要的 start方法:
- (void)start
{
@synchronized (self)
{
if (state == AS_PAUSED)
{
[self pause];
}
else if (state == AS_INITIALIZED)
{
NSAssert([[NSThread currentThread] isEqual:[NSThread mainThread]],
@"Playback can only be started from the main thread.");
notificationCenter =
[[NSNotificationCenter defaultCenter] retain];
self.state = AS_STARTING_FILE_THREAD;
internalThread =
[[NSThread alloc]
initWithTarget:self
selector:@selector(startInternal)
object:nil];
[internalThread setName:@"InternalThread"];
[internalThread start];
}
}
}
这里主要是重点注意internalThread =[[NSThread alloc]initWithTarget:self selector:@selector(startInternal) object:nil];中的方法startInternal:
里面有几段代码特重要:
#if TARGET_OS_IPHONE
//
// Set the audio session category so that we continue to play if the
// iPhone/iPod auto-locks.
//
AudioSessionInitialize (
NULL, // 'NULL' to use the default (main) run loop
NULL, // 'NULL' to use the default run loop mode
MyAudioSessionInterruptionListener, // a reference to your interruption callback
self // data to pass to your interruption listener callback
);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (
kAudioSessionProperty_AudioCategory,
sizeof (sessionCategory),
&sessionCategory
);
AudioSessionSetActive(true);
__streamer = self;
#endif
相信大家都看懂,这是让播放器后台运行的代码。
接下来我们要看一句代码,让我们的思绪继续走下去:
if (![self openReadStream])
{
goto cleanup;
}
//
// Process the run loop until playback is finished or failed.
//
BOOL isRunning = YES;
do
{
isRunning = [[NSRunLoop currentRunLoop]
runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
@synchronized(self) {
if (seekWasRequested) {
[self internalSeekToTime:requestedSeekTime];
seekWasRequested = NO;
}
}
//
// If there are no queued buffers, we need to check here since the
// handleBufferCompleteForQueue:buffer: should not change the state
// (may not enter the synchronized section).
//
if (buffersUsed == 0 && self.state == AS_PLAYING)
{
err = AudioQueuePause(audioQueue);
if (err)
{
[self failWithErrorCode:AS_AUDIO_QUEUE_PAUSE_FAILED];
return;
}
self.state = AS_BUFFERING;
}
} while (isRunning && ![self runLoopShouldExit]);
这里第一步会调用 openReadStream方法,第二步当返回yes时,就会继续执行do-while语句,这里会碰到runloop循环:
- (BOOL)openReadStream
{
@synchronized(self)
{
NSAssert([[NSThread currentThread] isEqual:internalThread],
@"File stream download must be started on the internalThread");
NSAssert(stream == nil, @"Download stream already initialized");
NSLog(@"opopop %@",url.absoluteString);
if (url !=nil && [url.absoluteString rangeOfString:@"file"].location == NSNotFound){
//
// Create the HTTP GET request
//
isLocalFile = NO;
}
如果url不是空,而且不是本地文件的时候,设置isLocalFile = NO;
剩下的几句重要的代码就是:
1.set useful headers:
CFHTTPMessageRef message= CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (CFURLRef)url, kCFHTTPVersion1_1);
#ifdef SHOUTCAST_METADATA
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("icy-metadata"), CFSTR("1"));
#endif
2.create the CFReadStreamRef:
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
CFRelease(message);
if (CFReadStreamSetProperty(
stream,
kCFStreamPropertyAppendToFile,
kCFBooleanTrue) == false)
{
[self presentAlertWithTitle:NSLocalizedStringFromTable(@"File Error", @"Errors", nil)
message:NSLocalizedStringFromTable(@"Unable to configure network read stream.", @"Errors", nil)];
return NO;
}
if (!CFReadStreamOpen(stream))
{
CFRelease(stream);
[self presentAlertWithTitle:NSLocalizedStringFromTable(@"File Error", @"Errors", nil) message:NSLocalizedStringFromTable(@"Unable to configure network read stream.", @"Errors", nil)];
return NO;
}
CFStreamClientContext context = {0, self, NULL, NULL, NULL};
CFReadStreamSetClient(
stream,
kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
ASReadStreamCallBack,
&context);
CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);这几句主要是CFHTTPMessage网络请求,与nsurl的get请求有几分相似,可以一起比较一下。
这时候会回调ASReadStreamCallBack 方法。
#pragma mark CFReadStream Callback Function Implementations
//// ReadStreamCallBack
//
// This is the callback for the CFReadStream from the network connection. This
// is where all network data is passed to the AudioFileStream.
//
// Invoked when an error occurs, the stream ends or we have data to read.
//
void ASReadStreamCallBack
(
CFReadStreamRef aStream,
CFStreamEventType eventType,
void* inClientInfo
)
{
AudioStreamer* streamer = (AudioStreamer *)inClientInfo;
[streamer handleReadFromStream:aStream eventType:eventType];
}