From 569764d869bcc1139ace060235874dbe1f186a42 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Thu, 13 Feb 2014 17:33:45 +0000 Subject: [PATCH] Added watchdog to STKAutoRecoveringHTTPDataSource to catch TCP timeouts which otherwise would not raise an error since we aren't do any writes to the socket --- StreamingKit/StreamingKit/STKAudioPlayer.m | 17 +-- .../STKAutoRecoveringHTTPDataSource.h | 7 ++ .../STKAutoRecoveringHTTPDataSource.m | 118 +++++++++++++++++- .../STKCoreFoundationDataSource.h | 1 + .../STKCoreFoundationDataSource.m | 7 ++ StreamingKit/StreamingKit/STKHTTPDataSource.h | 1 + StreamingKit/StreamingKit/STKHTTPDataSource.m | 11 ++ 7 files changed, 146 insertions(+), 16 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 5fcd9fe..8c6d66f 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -42,7 +42,7 @@ #import "libkern/OSAtomic.h" #import -#pragma mark Defines% +#pragma mark Defines #define kOutputBus 0 #define kInputBus 1 @@ -2728,15 +2728,8 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* NSArray* retval; NSMutableArray* mutableArray = [[NSMutableArray alloc] initWithCapacity:upcomingQueue.count + bufferingQueue.count]; - for (STKQueueEntry* entry in upcomingQueue) - { - [mutableArray addObject:[entry queueItemId]]; - } - - for (STKQueueEntry* entry in bufferingQueue) - { - [mutableArray addObject:[entry queueItemId]]; - } + [mutableArray skipQueueWithQueue:upcomingQueue]; + [mutableArray skipQueueWithQueue:bufferingQueue]; retval = [NSArray arrayWithArray:mutableArray]; @@ -2762,7 +2755,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* if (upcomingQueue.count > 0) { - NSObject* retval = [[upcomingQueue objectAtIndex:0] queueItemId]; + NSObject* retval = [upcomingQueue objectAtIndex:0]; pthread_mutex_unlock(&playerMutex); @@ -2771,7 +2764,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* if (bufferingQueue.count > 0) { - NSObject* retval = [[bufferingQueue objectAtIndex:0] queueItemId]; + NSObject* retval = [bufferingQueue objectAtIndex:0]; pthread_mutex_unlock(&playerMutex); diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h index 45cc2bc..c55895f 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h @@ -36,6 +36,13 @@ #import "STKHTTPDataSource.h" #import "STKDataSourceWrapper.h" +typedef struct +{ + int watchdogPeriodSeconds; + int inactivePeriodBeforeReconnectSeconds; +} +STKAutoRecoveringHTTPDataSourceOptions; + @interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper -(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource; diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index c98a190..acbc85f 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -38,18 +38,38 @@ #import #import #import +#import "mach/mach_time.h" #import #import #import "STKAutoRecoveringHTTPDataSource.h" -#define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (2) +#define DEFAULT_WATCHDOG_PERIOD_SECONDS (5) +#define DEFAULT_INACTIVE_PERIOD_BEFORE_RECONNECT_SECONDS (5) + +static uint64_t GetTickCount(void) +{ + static mach_timebase_info_data_t sTimebaseInfo; + uint64_t machTime = mach_absolute_time(); + + if (sTimebaseInfo.denom == 0 ) + { + (void) mach_timebase_info(&sTimebaseInfo); + } + + uint64_t millis = ((machTime / 1000000) * sTimebaseInfo.numer) / sTimebaseInfo.denom; + + return millis; +} @interface STKAutoRecoveringHTTPDataSource() { int serial; int waitSeconds; + NSTimer* timeoutTimer; BOOL waitingForNetwork; + uint64_t ticksWhenLastDataReceived; SCNetworkReachabilityRef reachabilityRef; + STKAutoRecoveringHTTPDataSourceOptions options; } -(void) reachabilityChanged; @@ -66,6 +86,19 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach } } +static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* options) +{ + if (options->watchdogPeriodSeconds == 0) + { + options->watchdogPeriodSeconds = DEFAULT_WATCHDOG_PERIOD_SECONDS; + } + + if (options->inactivePeriodBeforeReconnectSeconds == 0) + { + options->inactivePeriodBeforeReconnectSeconds = DEFAULT_INACTIVE_PERIOD_BEFORE_RECONNECT_SECONDS; + } +} + @implementation STKAutoRecoveringHTTPDataSource -(STKHTTPDataSource*) innerHTTPDataSource @@ -79,6 +112,11 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach } -(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn +{ + return [self initWithHTTPDataSource:innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions){}]; +} + +-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions)optionsIn { if (self = [super initWithDataSource:innerDataSourceIn]) { @@ -90,6 +128,10 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach zeroAddress.sin_len = sizeof(zeroAddress); zeroAddress.sin_family = AF_INET; + PopulateOptionsWithDefault(&optionsIn); + + self->options = optionsIn; + reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress); } @@ -117,14 +159,68 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach [super registerForEvents:runLoop]; [self startNotifierOnRunLoop:runLoop]; + if (timeoutTimer) + { + [timeoutTimer invalidate]; + timeoutTimer = nil; + } + + [self createTimeoutTimer]; + return YES; } -(void) unregisterForEvents { [super unregisterForEvents]; - [self stopNotifier]; + + [self destroyTimeoutTimer]; +} + +-(void) timeoutTimerTick:(NSTimer*)timer +{ + if (![self hasBytesAvailable]) + { + if ([self hasGotNetworkConnection]) + { + uint64_t currentTicks = GetTickCount(); + + if (((currentTicks - ticksWhenLastDataReceived) / 1000) >= options.inactivePeriodBeforeReconnectSeconds) + { + serial++; + + NSLog(@"timeoutTimerTick %lld/%lld", self.position, self.length); + + [self attemptReconnectWithSerial:@(serial)]; + } + } + } +} + +-(void) createTimeoutTimer +{ + [self destroyTimeoutTimer]; + + NSRunLoop* runLoop = self.innerDataSource.eventsRunLoop; + + if (runLoop == nil) + { + return; + } + + timeoutTimer = [NSTimer timerWithTimeInterval:options.watchdogPeriodSeconds target:self selector:@selector(timeoutTimerTick:) userInfo:@(serial) repeats:YES]; + + [runLoop addTimer:timeoutTimer forMode:NSRunLoopCommonModes]; +} + +-(void) destroyTimeoutTimer +{ + if (timeoutTimer) + { + [timeoutTimer invalidate]; + timeoutTimer = nil; + } } -(void) stopNotifier @@ -148,6 +244,12 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach return NO; } +-(void) close +{ + [self destroyTimeoutTimer]; + [super close]; +} + -(void) dealloc { NSLog(@"STKAutoRecoveringHTTPDataSource dealloc"); @@ -155,6 +257,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach self.innerDataSource.delegate = nil; [self stopNotifier]; + [self destroyTimeoutTimer]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; @@ -170,6 +273,10 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { waitingForNetwork = NO; + NSLog(@"reachabilityChanged %lld/%lld", self.position, self.length); + + serial++; + [self attemptReconnectWithSerial:@(serial)]; } } @@ -178,7 +285,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { serial++; waitSeconds = 1; - + ticksWhenLastDataReceived = GetTickCount(); + [super dataSourceDataAvailable:dataSource]; } @@ -191,7 +299,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach NSLog(@"attemptReconnect %lld/%lld", self.position, self.length); - [self seekToOffset:self.position]; + [self.innerDataSource reconnect]; } -(void) attemptReconnectWithTimer:(NSTimer*)timer @@ -220,6 +328,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach } else { + serial++; + NSTimer* timer = [NSTimer timerWithTimeInterval:waitSeconds target:self selector:@selector(attemptReconnectWithTimer:) userInfo:@(serial) repeats:NO]; [runLoop addTimer:timer forMode:NSRunLoopCommonModes]; diff --git a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h index 8892bf1..731de5d 100644 --- a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h +++ b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h @@ -54,6 +54,7 @@ -(BOOL) reregisterForEvents; -(void) open; +-(void) openCompleted; -(void) dataAvailable; -(void) eof; -(void) errorOccured; diff --git a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m index 5b28947..4b9c7b3 100644 --- a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m +++ b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m @@ -49,6 +49,9 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve case kCFStreamEventHasBytesAvailable: [datasource dataAvailable]; break; + case kCFStreamEventOpenCompleted: + [datasource openCompleted]; + break; default: break; } @@ -191,4 +194,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve return 0; } +-(void) openCompleted +{ +} + @end diff --git a/StreamingKit/StreamingKit/STKHTTPDataSource.h b/StreamingKit/StreamingKit/STKHTTPDataSource.h index c599ed3..dda2924 100644 --- a/StreamingKit/StreamingKit/STKHTTPDataSource.h +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.h @@ -50,5 +50,6 @@ typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek, -(id) initWithURLProvider:(STKURLProvider)urlProvider; -(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider; -(NSRunLoop*) eventsRunLoop; +-(void) reconnect; @end diff --git a/StreamingKit/StreamingKit/STKHTTPDataSource.m b/StreamingKit/StreamingKit/STKHTTPDataSource.m index 65a191e..bf07027 100644 --- a/StreamingKit/StreamingKit/STKHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.m @@ -214,6 +214,17 @@ return fileLength >= 0 ? fileLength : 0; } +-(void) reconnect +{ + NSRunLoop* savedEventsRunLoop = eventsRunLoop; + + [self close]; + + eventsRunLoop = savedEventsRunLoop; + + [self seekToOffset:self.position]; +} + -(void) seekToOffset:(long long)offset { NSRunLoop* savedEventsRunLoop = eventsRunLoop;