diff --git a/Limelight/AppDelegate.m b/Limelight/AppDelegate.m index 2783fd1..680aa5f 100644 --- a/Limelight/AppDelegate.m +++ b/Limelight/AppDelegate.m @@ -88,8 +88,7 @@ static NSOperationQueue* mainQueue; if (_managedObjectModel != nil) { return _managedObjectModel; } - NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Limelight_iOS" withExtension:@"momd"]; - _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; + _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil]; return _managedObjectModel; } diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index 2ac68e9..21121e7 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -38,7 +38,8 @@ static const NSString* PORT = @"47984"; // Check root status_code if (![HttpManager verifyStatus: rootNode]) { - //TODO: handle error + NSLog(@"ERROR: Request returned with failure status"); + return NULL; } // Skip the root node @@ -229,6 +230,7 @@ static const NSString* PORT = @"47984"; - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"connection error: %@", error); + dispatch_semaphore_signal(_requestLock); } @end diff --git a/Limelight/Network/MDNSManager.m b/Limelight/Network/MDNSManager.m index ce28a79..0feb314 100644 --- a/Limelight/Network/MDNSManager.m +++ b/Limelight/Network/MDNSManager.m @@ -9,10 +9,12 @@ #import "MDNSManager.h" #import "Computer.h" -@implementation MDNSManager : NSObject -NSNetServiceBrowser* mDNSBrowser; -NSMutableArray* domains; -NSMutableArray* services; +@implementation MDNSManager { + NSNetServiceBrowser* mDNSBrowser; + NSMutableArray* domains; + NSMutableArray* services; + BOOL scanActive; +} static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp"; @@ -32,11 +34,13 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp"; - (void) searchForHosts { NSLog(@"Starting mDNS discovery"); + scanActive = TRUE; [mDNSBrowser searchForServicesOfType:NV_SERVICE_TYPE inDomain:@""]; } - (void) stopSearching { NSLog(@"Stopping mDNS discovery"); + scanActive = FALSE; [mDNSBrowser stop]; } @@ -47,7 +51,7 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp"; [hosts addObject:[[Computer alloc] initWithHost:service]]; } } - return [[NSArray alloc] initWithArray:hosts]; + return hosts; } - (void)netServiceDidResolveAddress:(NSNetService *)service { @@ -74,6 +78,24 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp"; - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict { NSLog(@"Did not perform search"); NSLog(@"%@", [errorDict description]); + + // Schedule a retry in 2 seconds + [NSTimer scheduledTimerWithTimeInterval:2.0 + target:self + selector:@selector(retryTimerCallback:) + userInfo:nil + repeats:NO]; +} + +- (void)retryTimerCallback:(NSTimer *)timer { + // Check if we've been stopped since this was queued + if (!scanActive) { + return; + } + + NSLog(@"Retrying mDNS search"); + [mDNSBrowser stop]; + [mDNSBrowser searchForServicesOfType:NV_SERVICE_TYPE inDomain:@""]; } @end \ No newline at end of file diff --git a/Limelight/Stream/Connection.h b/Limelight/Stream/Connection.h index e4a9735..05a1384 100644 --- a/Limelight/Stream/Connection.h +++ b/Limelight/Stream/Connection.h @@ -10,15 +10,22 @@ #import "VideoDecoderRenderer.h" #import "StreamConfiguration.h" -@protocol ConTermCallback +@protocol ConnectionCallbacks -- (void) connectionTerminated; +- (void) connectionStarted; +- (void) connectionTerminated:(long)errorCode; +- (void) stageStarting:(char*)stageName; +- (void) stageComplete:(char*)stageName; +- (void) stageFailed:(char*)stageName withError:(long)errorCode; +- (void) launchFailed; +- (void) displayMessage:(char*)message; +- (void) displayTransientMessage:(char*)message; @end @interface Connection : NSOperation --(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionTerminatedCallback:(id)callback; +-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id)callbacks; -(void) terminate; -(void) main; diff --git a/Limelight/Stream/Connection.m b/Limelight/Stream/Connection.m index 139adc6..9679260 100644 --- a/Limelight/Stream/Connection.m +++ b/Limelight/Stream/Connection.m @@ -20,11 +20,10 @@ CONNECTION_LISTENER_CALLBACKS _clCallbacks; DECODER_RENDERER_CALLBACKS _drCallbacks; AUDIO_RENDERER_CALLBACKS _arCallbacks; - } static OpusDecoder *opusDecoder; -static id _callback; +static id _callbacks; #define PCM_BUFFER_SIZE 1024 #define OUTPUT_BUS 0 @@ -243,37 +242,37 @@ void ArDecodeAndPlaySample(char* sampleData, int sampleLength) void ClStageStarting(int stage) { + [_callbacks stageStarting:(char*)LiGetStageName(stage)]; } void ClStageComplete(int stage) { + [_callbacks stageComplete:(char*)LiGetStageName(stage)]; } void ClStageFailed(int stage, long errorCode) { - printf("Stage %d failed: %ld\n", stage, errorCode); + [_callbacks stageFailed:(char*)LiGetStageName(stage) withError:errorCode]; } void ClConnectionStarted(void) { - printf("Connection started\n"); + [_callbacks connectionStarted]; } void ClConnectionTerminated(long errorCode) { - printf("ConnectionTerminated: %ld\n", errorCode); - - [_callback connectionTerminated]; + [_callbacks connectionTerminated: errorCode]; } void ClDisplayMessage(char* message) { - printf("DisplayMessage: %s\n", message); + [_callbacks displayMessage: message]; } void ClDisplayTransientMessage(char* message) { - printf("DisplayTransientMessage: %s\n", message); + [_callbacks displayTransientMessage: message]; } -(void) terminate @@ -286,12 +285,12 @@ void ClDisplayTransientMessage(char* message) }); } --(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionTerminatedCallback:(id)callback +-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id)callbacks { self = [super init]; _host = config.hostAddr; renderer = myRenderer; - _callback = callback; + _callbacks = callbacks; _streamConfig.width = config.width; _streamConfig.height = config.height; _streamConfig.fps = config.frameRate; diff --git a/Limelight/Stream/StreamManager.h b/Limelight/Stream/StreamManager.h index 4f865f2..d0dd6d3 100644 --- a/Limelight/Stream/StreamManager.h +++ b/Limelight/Stream/StreamManager.h @@ -12,7 +12,7 @@ @interface StreamManager : NSOperation -- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionTerminatedCallback:(id)callback; +- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionCallbacks:(id)callback; - (void) stopStream; @end diff --git a/Limelight/Stream/StreamManager.m b/Limelight/Stream/StreamManager.m index 21e4cb7..d8135df 100644 --- a/Limelight/Stream/StreamManager.m +++ b/Limelight/Stream/StreamManager.m @@ -14,15 +14,15 @@ @implementation StreamManager { StreamConfiguration* _config; UIView* _renderView; - id _callback; + id _callbacks; Connection* _connection; } -- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionTerminatedCallback:(id)callback { +- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionCallbacks:(id)callbacks { self = [super init]; _config = config; _renderView = view; - _callback = callback; + _callbacks = callbacks; _config.riKey = [Utils randomBytes:16]; _config.riKeyId = arc4random(); _config.bitRate = 10000; @@ -50,7 +50,7 @@ } VideoDecoderRenderer* renderer = [[VideoDecoderRenderer alloc]initWithView:_renderView]; - _connection = [[Connection alloc] initWithConfig:_config renderer:renderer connectionTerminatedCallback:_callback]; + _connection = [[Connection alloc] initWithConfig:_config renderer:renderer connectionCallbacks:_callbacks]; NSOperationQueue* opQueue = [[NSOperationQueue alloc] init]; [opQueue addOperation:_connection]; } @@ -60,7 +60,7 @@ [_connection terminate]; } -- (void) launchApp:(HttpManager*)hMan { +- (BOOL) launchApp:(HttpManager*)hMan { NSData* launchResp = [hMan executeRequestSynchronously: [hMan newLaunchRequest:@"67339056" width:_config.width @@ -68,14 +68,24 @@ refreshRate:_config.frameRate rikey:[Utils bytesToHex:_config.riKey] rikeyid:_config.riKeyId]]; - [HttpManager getStringFromXML:launchResp tag:@"gamesession"]; + NSString *gameSession = [HttpManager getStringFromXML:launchResp tag:@"gamesession"]; + if (gameSession == NULL || [gameSession isEqualToString:@"0"]) { + return FALSE; + } + + return TRUE; } -- (void) resumeApp:(HttpManager*)hMan { +- (BOOL) resumeApp:(HttpManager*)hMan { NSData* resumeResp = [hMan executeRequestSynchronously: [hMan newResumeRequestWithRiKey:[Utils bytesToHex:_config.riKey] riKeyId:_config.riKeyId]]; - [HttpManager getStringFromXML:resumeResp tag:@"gamesession"]; + NSString *resume = [HttpManager getStringFromXML:resumeResp tag:@"resume"]; + if (resume == NULL || [resume isEqualToString:@"0"]) { + return FALSE; + } + + return TRUE; } @end diff --git a/Limelight/Utility/Computer.h b/Limelight/Utility/Computer.h index 854a3dd..dc0058c 100644 --- a/Limelight/Utility/Computer.h +++ b/Limelight/Utility/Computer.h @@ -15,5 +15,6 @@ - (id) initWithHost:(NSNetService*)host; - (id) initWithIp:(NSString*)host; +- (id) initPlaceholder; @end diff --git a/Limelight/Utility/Computer.m b/Limelight/Utility/Computer.m index 6a7cb22..fa8deb9 100644 --- a/Limelight/Utility/Computer.m +++ b/Limelight/Utility/Computer.m @@ -28,4 +28,13 @@ return self; } +- (id) initPlaceholder { + self = [super init]; + + self.hostName = NULL; + self.displayName = @"No computers found"; + + return self; +} + @end diff --git a/Limelight/Utility/Utils.m b/Limelight/Utility/Utils.m index f030295..96d678a 100644 --- a/Limelight/Utility/Utils.m +++ b/Limelight/Utility/Utils.m @@ -67,7 +67,7 @@ return addr; } else { NSLog(@"Failed to resolve host: %d", h_errno); - return -1; + return 0; } } } diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 07db5a6..6c1dd61 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -34,10 +34,21 @@ static StreamConfiguration* streamConfig; _selectedHost = [[Computer alloc] initWithIp:self.hostTextField.text]; NSLog(@"Using custom host: %@", self.hostTextField.text); } + + if (![self validatePcSelected]) { + NSLog(@"No valid PC selected"); + return; + } + [CryptoManager generateKeyPairUsingSSl]; NSString* uniqueId = [CryptoManager getUniqueID]; NSData* cert = [CryptoManager readCertFromFile]; + if ([Utils resolveHost:_selectedHost.hostName] == 0) { + [self displayDnsFailedDialog]; + return; + } + HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.hostName uniqueId:uniqueId deviceName:@"roth" cert:cert]; PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:cert callback:self]; @@ -67,6 +78,14 @@ static StreamConfiguration* streamConfig; }); } +- (void)displayDnsFailedDialog { + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Network Error" + message:@"Failed to resolve host." + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]]; + [self presentViewController:alert animated:YES completion:nil]; +} + - (void)StreamButton:(UIButton *)sender { NSLog(@"Stream Button Pressed!"); @@ -74,9 +93,19 @@ static StreamConfiguration* streamConfig; _selectedHost = [[Computer alloc] initWithIp:self.hostTextField.text]; NSLog(@"Using custom host: %@", self.hostTextField.text); } + + if (![self validatePcSelected]) { + NSLog(@"No valid PC selected"); + return; + } + streamConfig = [[StreamConfiguration alloc] init]; streamConfig.host = _selectedHost.hostName; streamConfig.hostAddr = [Utils resolveHost:_selectedHost.hostName]; + if (streamConfig.hostAddr == 0) { + [self displayDnsFailedDialog]; + return; + } unsigned long selectedConf = [self.StreamConfigs selectedRowInComponent:0]; NSLog(@"selectedConf: %ld", selectedConf); @@ -122,10 +151,19 @@ static StreamConfiguration* streamConfig; } } +- (void)setSelectedHost:(NSInteger)selectedIndex +{ + _selectedHost = (Computer*)([self.hostPickerVals objectAtIndex:selectedIndex]); + if (_selectedHost.hostName == NULL) { + // This must be the placeholder computer + _selectedHost = NULL; + } +} + - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { if (pickerView == self.HostPicker) { - _selectedHost = (Computer*)([self.hostPickerVals objectAtIndex:[self.HostPicker selectedRowInComponent:0]]); + [self setSelectedHost:[self.HostPicker selectedRowInComponent:0]]; } //TODO: figure out how to save this info!! @@ -154,10 +192,12 @@ static StreamConfiguration* streamConfig; [super viewDidLoad]; self.streamConfigVals = [[NSArray alloc] initWithObjects:@"1280x720 (30Hz)", @"1280x720 (60Hz)", @"1920x1080 (30Hz)", @"1920x1080 (60Hz)",nil]; - self.hostPickerVals = [[NSArray alloc] init]; [self.StreamConfigs selectRow:1 inComponent:0 animated:NO]; _opQueue = [[NSOperationQueue alloc] init]; + + // Initialize the host picker list + [self updateHosts:[[NSArray alloc] init]]; } - (void)viewDidAppear:(BOOL)animated @@ -172,9 +212,29 @@ static StreamConfiguration* streamConfig; } - (void)updateHosts:(NSArray *)hosts { - self.hostPickerVals = hosts; + NSMutableArray *hostPickerValues = [[NSMutableArray alloc] initWithArray:hosts]; + + if ([hostPickerValues count] == 0) { + [hostPickerValues addObject:[[Computer alloc] initPlaceholder]]; + } + + self.hostPickerVals = hostPickerValues; [self.HostPicker reloadAllComponents]; - _selectedHost = (Computer*)([self.hostPickerVals objectAtIndex:[self.HostPicker selectedRowInComponent:0]]); + + [self setSelectedHost:[self.HostPicker selectedRowInComponent:0]]; +} + +- (BOOL)validatePcSelected { + if (_selectedHost == NULL) { + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"No PC Selected" + message:@"You must select a PC to continue." + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]]; + [self presentViewController:alert animated:YES completion:nil]; + return FALSE; + } + + return TRUE; } - (void)didReceiveMemoryWarning diff --git a/Limelight/ViewControllers/StreamFrameViewController.h b/Limelight/ViewControllers/StreamFrameViewController.h index adbcd29..07a1374 100644 --- a/Limelight/ViewControllers/StreamFrameViewController.h +++ b/Limelight/ViewControllers/StreamFrameViewController.h @@ -10,6 +10,6 @@ #import -@interface StreamFrameViewController : UIViewController +@interface StreamFrameViewController : UIViewController @end diff --git a/Limelight/ViewControllers/StreamFrameViewController.m b/Limelight/ViewControllers/StreamFrameViewController.m index f8a2103..18758c2 100644 --- a/Limelight/ViewControllers/StreamFrameViewController.m +++ b/Limelight/ViewControllers/StreamFrameViewController.m @@ -29,7 +29,9 @@ _controllerSupport = [[ControllerSupport alloc] init]; - _streamMan = [[StreamManager alloc] initWithConfig:[MainFrameViewController getStreamConfiguration] renderView:self.view connectionTerminatedCallback:self]; + _streamMan = [[StreamManager alloc] initWithConfig:[MainFrameViewController getStreamConfiguration] + renderView:self.view + connectionCallbacks:self]; NSOperationQueue* opQueue = [[NSOperationQueue alloc] init]; [opQueue addOperation:_streamMan]; @@ -44,7 +46,13 @@ [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; } -- (void)connectionTerminated { +- (void) connectionStarted { + printf("Connection started\n"); +} + +- (void)connectionTerminated:(long)errorCode { + printf("Connection terminated: %ld\n", errorCode); + UIAlertController* conTermAlert = [UIAlertController alertControllerWithTitle:@"Connection Terminated" message:@"The connection terminated unexpectedly" preferredStyle:UIAlertControllerStyleAlert]; [conTermAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; @@ -54,6 +62,36 @@ [_streamMan stopStream]; } +- (void) stageStarting:(char*)stageName { + printf("Starting %s\n", stageName); +} + +- (void) stageComplete:(char*)stageName { +} + +- (void) stageFailed:(char*)stageName withError:(long)errorCode { + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed" + message:[NSString stringWithFormat:@"%s failed with error %ld", + stageName, errorCode] + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ + [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; + }]]; + [self presentViewController:alert animated:YES completion:nil]; +} + +- (void) launchFailed { + +} + +- (void) displayMessage:(char*)message { + printf("Display message: %s\n", message); +} + +- (void) displayTransientMessage:(char*)message { + printf("Display transient message: %s\n", message); +} + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning];