Merge branch 'master' of github.com:limelight-stream/limelight-ios

# By Cameron Gutman
# Via Cameron Gutman
* 'master' of github.com:limelight-stream/limelight-ios:
  Enhance the connection callback protocol to include other connection listener callbacks exposed by common. Start handling some HTTP request errors.
  Display a dialog if DNS resolution fails
  Update common
  Fix crash on app termination
  Don't crash if no PC is selected when a button is pressed
  Add a placeholder in the computer list if none are found. Retry service discovery if it fails.

Conflicts:
	limelight-common-c
This commit is contained in:
Diego Waxemberg 2014-10-21 15:13:07 -04:00
commit 26f7a27117
13 changed files with 186 additions and 39 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -10,15 +10,22 @@
#import "VideoDecoderRenderer.h"
#import "StreamConfiguration.h"
@protocol ConTermCallback <NSObject>
@protocol ConnectionCallbacks <NSObject>
- (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 <NSStreamDelegate>
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionTerminatedCallback:(id<ConTermCallback>)callback;
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks;
-(void) terminate;
-(void) main;

View File

@ -20,11 +20,10 @@
CONNECTION_LISTENER_CALLBACKS _clCallbacks;
DECODER_RENDERER_CALLBACKS _drCallbacks;
AUDIO_RENDERER_CALLBACKS _arCallbacks;
}
static OpusDecoder *opusDecoder;
static id<ConTermCallback> _callback;
static id<ConnectionCallbacks> _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<ConTermCallback>)callback
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)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;

View File

@ -12,7 +12,7 @@
@interface StreamManager : NSOperation
- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionTerminatedCallback:(id<ConTermCallback>)callback;
- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionCallbacks:(id<ConnectionCallbacks>)callback;
- (void) stopStream;
@end

View File

@ -14,15 +14,15 @@
@implementation StreamManager {
StreamConfiguration* _config;
UIView* _renderView;
id<ConTermCallback> _callback;
id<ConnectionCallbacks> _callbacks;
Connection* _connection;
}
- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionTerminatedCallback:(id<ConTermCallback>)callback {
- (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionCallbacks:(id<ConnectionCallbacks>)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

View File

@ -15,5 +15,6 @@
- (id) initWithHost:(NSNetService*)host;
- (id) initWithIp:(NSString*)host;
- (id) initPlaceholder;
@end

View File

@ -28,4 +28,13 @@
return self;
}
- (id) initPlaceholder {
self = [super init];
self.hostName = NULL;
self.displayName = @"No computers found";
return self;
}
@end

View File

@ -67,7 +67,7 @@
return addr;
} else {
NSLog(@"Failed to resolve host: %d", h_errno);
return -1;
return 0;
}
}
}

View File

@ -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

View File

@ -10,6 +10,6 @@
#import <UIKit/UIKit.h>
@interface StreamFrameViewController : UIViewController <ConTermCallback>
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks>
@end

View File

@ -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];