Compare commits

..
Author SHA1 Message Date
Diego Waxemberg 5ee9e285e7 created beta 8 2014-11-18 10:28:12 -05:00
Cameron Gutman d8020a367f Update to the new common library 2014-11-11 19:03:46 -08:00
Cameron Gutman 88c917c103 Fix iOS build with new common 2014-10-31 21:43:13 -07:00
Diego Waxemberg 6273f2bb29 Pulled common and created new build 2014-10-27 19:53:50 -04:00
Diego Waxemberg fac48ecf42 Created new build and removed beta builds 2014-10-24 15:56:04 -04:00
Cameron Gutman b5bf26bb2d Add busy warning to pairing 2014-10-23 23:46:41 -04:00
Cameron Gutman 00fab87b4c Merge branch 'master' of github.com:limelight-stream/limelight-ios 2014-10-23 23:41:25 -04:00
Cameron Gutman 3554b40962 Display status messages for failed app launch 2014-10-23 23:41:07 -04:00
Diego Waxemberg aa3752a905 Merge branch 'master' of github.com:limelight-stream/limelight-ios
# By Cameron Gutman
# Via Cameron Gutman
* 'master' of github.com:limelight-stream/limelight-ios:
  Add better messages to pairing
  Fix missing video issue
2014-10-23 23:17:18 -04:00
Diego Waxemberg c0a3ae9371 updated build and fixed an issue with the analyzer 2014-10-23 23:16:24 -04:00
Cameron Gutman 47850cd658 Add better messages to pairing 2014-10-23 23:11:24 -04:00
Cameron Gutman 1b98870b41 Fix missing video issue 2014-10-23 22:35:59 -04:00
Cameron Gutman ef98976ebf Update common rev 2014-10-23 14:59:24 -04:00
Cameron Gutman ba022da665 Don't attempt to start or resume a game if the device isn't paired 2014-10-23 14:53:31 -04:00
Cameron Gutman ae9abf2911 Add shared workspace data to version control 2014-10-23 13:28:52 -04:00
Cameron Gutman 99e7f59df4 Ignore all xcuserdata directories 2014-10-23 13:27:49 -04:00
Michelle Bergeron 8273173080 Update README.md 2014-10-23 01:29:33 -04:00
Cameron Gutman 3357b3882b Use different bitrates for each stream configuration 2014-10-22 00:04:58 -04:00
Cameron Gutman b7510717c8 Merge branch 'master' of github.com:limelight-stream/limelight-ios 2014-10-21 17:27:08 -04:00
Cameron Gutman 5434542877 Retry service resolution 2014-10-21 17:26:50 -04:00
Diego Waxemberg 4c2f6b269a added spinner and message during stream init 2014-10-21 17:18:30 -04:00
Cameron Gutman 0ccf046c69 Fix inverted if 2014-10-21 16:23:03 -04:00
Diego Waxemberg f0b6602b99 Merge branch 'master' of github.com:limelight-stream/limelight-ios
# By Cameron Gutman
# Via Cameron Gutman
* 'master' of github.com:limelight-stream/limelight-ios:
  Add timeouts to HTTP requests
  Change StreamView's background to black
  Display a failure dialog when we fail to launch an app
2014-10-21 16:18:00 -04:00
Diego Waxemberg 72e0da4319 removed my code signing ids 2014-10-21 16:17:36 -04:00
Cameron Gutman d2ad8b12ce Add timeouts to HTTP requests 2014-10-21 16:16:24 -04:00
Cameron Gutman 6ef492662b Change StreamView's background to black 2014-10-21 16:15:59 -04:00
Cameron Gutman 3c7f33a80c Merge branch 'master' of github.com:limelight-stream/limelight-ios 2014-10-21 16:05:59 -04:00
Diego Waxemberg eeec1f1cf5 prepped common for deployment 2014-10-21 16:03:11 -04:00
Cameron Gutman 45545e8f9b Merge branch 'master' of github.com:limelight-stream/limelight-ios 2014-10-21 15:54:47 -04:00
Cameron Gutman 85b65d7112 Display a failure dialog when we fail to launch an app 2014-10-21 15:54:19 -04:00
Diego Waxemberg 26f7a27117 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
2014-10-21 15:13:07 -04:00
Cameron Gutman ea24b39fbe Enhance the connection callback protocol to include other connection listener callbacks exposed by common. Start handling some HTTP request errors. 2014-10-21 14:54:10 -04:00
Cameron Gutman 01a2853032 Display a dialog if DNS resolution fails 2014-10-21 14:31:03 -04:00
Cameron Gutman bfca061694 Update common 2014-10-21 14:18:41 -04:00
Cameron Gutman 4a9bb00e0d Fix crash on app termination 2014-10-21 14:04:01 -04:00
Cameron Gutman 72271a0c74 Don't crash if no PC is selected when a button is pressed 2014-10-21 13:46:05 -04:00
Cameron Gutman 1ca787822b Add a placeholder in the computer list if none are found. Retry service discovery if it fails. 2014-10-21 13:39:14 -04:00
27 changed files with 490 additions and 130 deletions
+1 -1
View File
@@ -1 +1 @@
Limelight.xcodeproj/xcuserdata/
**/xcuserdata/
Binary file not shown.
Binary file not shown.
+10 -10
View File
@@ -771,8 +771,8 @@
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@@ -811,8 +811,8 @@
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution: Diego Waxemberg (DM46QST4M7)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Diego Waxemberg (DM46QST4M7)";
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -823,7 +823,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
PROVISIONING_PROFILE = "d8351c44-2c9d-4b3a-bc67-ea963350dcbe";
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@@ -834,8 +834,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
HEADER_SEARCH_PATHS = (
@@ -862,8 +862,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Distribution: Diego Waxemberg (DM46QST4M7)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Diego Waxemberg (DM46QST4M7)";
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
HEADER_SEARCH_PATHS = (
@@ -880,7 +880,7 @@
"$(PROJECT_DIR)/libs/openssl/lib",
);
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "d8351c44-2c9d-4b3a-bc67-ea963350dcbe";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = NO;
WRAPPER_EXTENSION = app;
};
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Limelight.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>054A62F3-1197-45FB-A607-EA7922396E24</string>
<key>IDESourceControlProjectName</key>
<string>Limelight</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</key>
<string>github.com:limelight-stream/limelight-ios.git</string>
<key>EB99D631D5149240E91F8D168F0F298D7A5BAA89</key>
<string>github.com:limelight-stream/limelight-common-c.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>Limelight.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</key>
<string>../..</string>
<key>EB99D631D5149240E91F8D168F0F298D7A5BAA89</key>
<string>../../limelight-common-c</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>github.com:limelight-stream/limelight-ios.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>EB99D631D5149240E91F8D168F0F298D7A5BAA89</string>
<key>IDESourceControlWCCName</key>
<string>limelight-common-c</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</string>
<key>IDESourceControlWCCName</key>
<string>limelight-ios</string>
</dict>
</array>
</dict>
</plist>
+1 -2
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;
}
+1 -1
View File
@@ -21,7 +21,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>8</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>LSRequiresIPhoneOS</key>
+1
View File
@@ -11,6 +11,7 @@
@interface HttpManager : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
+ (NSString*) getStringFromXML:(NSData*)xml tag:(NSString*)tag;
+ (NSString*) getStatusStringFromXML:(NSData*)xml;
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert;
- (NSURLRequest*) newPairRequest:(NSData*)salt;
+52 -13
View File
@@ -25,6 +25,24 @@
static const NSString* PORT = @"47984";
+ (NSString*) getStatusStringFromXML:(NSData*)xml {
xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]);
if (docPtr == NULL) {
NSLog(@"ERROR: An error occured trying to parse xml.");
return NULL;
}
NSString* string;
xmlNodePtr rootNode = xmlDocGetRootElement(docPtr);
string = [HttpManager getStatusMessage: rootNode];
xmlFree(rootNode);
xmlFree(docPtr);
return string;
}
+ (NSString*) getStringFromXML:(NSData*)xml tag:(NSString*)tag {
xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]);
@@ -38,7 +56,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
@@ -59,6 +78,13 @@ static const NSString* PORT = @"47984";
return value;
}
+ (NSString*) getStatusMessage:(xmlNodePtr)docRoot {
xmlChar* statusMsgXml = xmlGetProp(docRoot, (const xmlChar*)"status_message");
NSString* statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml];
xmlFree(statusMsgXml);
return statusMsg;
}
+ (bool) verifyStatus:(xmlNodePtr)docRoot {
xmlChar* statusStr = xmlGetProp(docRoot, (const xmlChar*)"status_code");
@@ -101,63 +127,75 @@ static const NSString* PORT = @"47984";
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (NSURLRequest*) createRequestFromString:(NSString*) urlString {
- (NSURLRequest*) createRequestFromString:(NSString*) urlString enableTimeout:(BOOL)normalTimeout {
NSString* escapedUrl = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL* url = [[NSURL alloc] initWithString:escapedUrl];
return [NSURLRequest requestWithURL:url];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
if (normalTimeout) {
// Timeout the request after 5 seconds
[request setTimeoutInterval:5];
}
else {
// Timeout the request after 60 seconds
[request setTimeoutInterval:60];
}
return request;
}
- (NSURLRequest*) newPairRequest:(NSData*)salt {
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=getservercert&salt=%@&clientcert=%@",
_baseURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]];
return [self createRequestFromString:urlString];
// This call blocks while waiting for the user to input the PIN on the PC
return [self createRequestFromString:urlString enableTimeout:FALSE];
}
- (NSURLRequest*) newUnpairRequest {
NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseURL, _uniqueId];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest*) newChallengeRequest:(NSData*)challenge {
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientchallenge=%@",
_baseURL, _uniqueId, _deviceName, [self bytesToHex:challenge]];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest*) newChallengeRespRequest:(NSData*)challengeResp {
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&serverchallengeresp=%@",
_baseURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest*) newClientSecretRespRequest:(NSString*)clientPairSecret {
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseURL, _uniqueId, _deviceName, clientPairSecret];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest*) newPairChallenge {
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseURL, _uniqueId, _deviceName];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest *)newAppListRequest {
NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseURL, _uniqueId];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest *)newServerInfoRequest {
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseURL, _uniqueId];
return [self createRequestFromString:urlString];
return [self createRequestFromString:urlString enableTimeout:TRUE];
}
- (NSURLRequest*) newLaunchRequest:(NSString*)appId width:(int)width height:(int)height refreshRate:(int)refreshRate rikey:(NSString*)rikey rikeyid:(int)rikeyid {
NSString* urlString = [NSString stringWithFormat:@"%@/launch?uniqueid=%@&appid=%@&mode=%dx%dx%d&additionalStates=1&sops=1&rikey=%@&rikeyid=%d", _baseURL, _uniqueId, appId, width, height, refreshRate, rikey, rikeyid];
return [self createRequestFromString:urlString];
// This blocks while the app is launching
return [self createRequestFromString:urlString enableTimeout:FALSE];
}
- (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId {
NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d", _baseURL, _uniqueId, riKey, riKeyId];
return [self createRequestFromString:urlString];
// This blocks while the app is resuming
return [self createRequestFromString:urlString enableTimeout:FALSE];
}
- (NSString*) bytesToHex:(NSData*)data {
@@ -229,6 +267,7 @@ static const NSString* PORT = @"47984";
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"connection error: %@", error);
dispatch_semaphore_signal(_requestLock);
}
@end
+51 -5
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 {
@@ -57,12 +61,21 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
- (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict {
NSLog(@"Did not resolve address for: %@\n%@", sender, [errorDict description]);
// Schedule a retry in 2 seconds
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(retryResolveTimerCallback:)
userInfo:nil
repeats:NO];
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
NSLog(@"Found service: %@", aNetService);
[aNetService setDelegate:self];
[aNetService resolveWithTimeout:5];
[services removeObject:aNetService];
[services addObject:aNetService];
}
@@ -74,6 +87,39 @@ 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(retrySearchTimerCallback:)
userInfo:nil
repeats:NO];
}
- (void)retrySearchTimerCallback:(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:@""];
}
- (void)retryResolveTimerCallback:(NSTimer *)timer {
// Check if we've been stopped since this was queued
if (!scanActive) {
return;
}
NSLog(@"Retrying mDNS resolution");
for (NSNetService* service in services) {
if (service.hostName == nil) {
[service setDelegate:self];
[service resolveWithTimeout:5];
}
}
}
@end
+28 -22
View File
@@ -28,12 +28,19 @@
- (void) main {
NSData* serverInfo = [_httpManager executeRequestSynchronously:[_httpManager newServerInfoRequest]];
if (![[HttpManager getStringFromXML:serverInfo tag:@"PairStatus"] isEqual:@"1"]) {
if (serverInfo == NULL) {
[_callback pairFailed:@"Unable to connect to PC"];
return;
}
if (![[HttpManager getStringFromXML:serverInfo tag:@"currentgame"] isEqual:@"0"]) {
[_callback pairFailed:@"You must stop streaming before attempting to pair."];
}
else if (![[HttpManager getStringFromXML:serverInfo tag:@"PairStatus"] isEqual:@"1"]) {
[self initiatePair];
} else {
[_callback pairFailed:@"Already Paired"];
[_callback pairFailed:@"This device is already paired."];
}
[_httpManager executeRequestSynchronously:[_httpManager newAppListRequest]];
}
- (void) initiatePair {
@@ -43,10 +50,11 @@
[_callback showPIN:PIN];
NSData* pairResp = [_httpManager executeRequestSynchronously:[_httpManager newPairRequest:salt]];
if ([[HttpManager getStringFromXML:pairResp tag:@"paired"] intValue] != 1) {
NSString* pairedString;
pairedString = [HttpManager getStringFromXML:pairResp tag:@"paired"];
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"pairResp failed"];
[_callback pairFailed:@"Pairing was declined by the target."];
return;
}
@@ -59,10 +67,10 @@
NSData* encryptedChallenge = [cryptoMan aesEncrypt:randomChallenge withKey:aesKey];
NSData* challengeResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRequest:encryptedChallenge]];
if ([[HttpManager getStringFromXML:challengeResp tag:@"paired"] intValue] != 1) {
pairedString = [HttpManager getStringFromXML:challengeResp tag:@"paired"];
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"challengeResp failed"];
[_callback pairFailed:@"Pairing stage #2 failed"];
return;
}
@@ -77,10 +85,10 @@
NSData* challengeRespEncrypted = [cryptoMan aesEncrypt:challengeRespHash withKey:aesKey];
NSData* secretResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRespRequest:challengeRespEncrypted]];
if ([[HttpManager getStringFromXML:secretResp tag:@"paired"] intValue] != 1) {
pairedString = [HttpManager getStringFromXML:secretResp tag:@"paired"];
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"secretResp failed"];
[_callback pairFailed:@"Pairing stage #3 failed"];
return;
}
@@ -90,33 +98,31 @@
if (![cryptoMan verifySignature:serverSecret withSignature:serverSignature andCert:[Utils hexToBytes:plainCert]]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"verifySignature failed"];
[_callback pairFailed:@"Server certificate invalid"];
return;
}
NSData* serverChallengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:randomChallenge with:[CryptoManager getSignatureFromCert:[Utils hexToBytes:plainCert]]] with:serverSecret]];
if (![serverChallengeRespHash isEqual:serverResponse]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"serverChallengeResp failed"];
[_callback pairFailed:@"Incorrect PIN"];
return;
}
NSData* clientPairingSecret = [self concatData:clientSecret with:[cryptoMan signData:clientSecret withKey:[CryptoManager readKeyFromFile]]];
NSData* clientSecretResp = [_httpManager executeRequestSynchronously:[_httpManager newClientSecretRespRequest:[Utils bytesToHex:clientPairingSecret]]];
if (![[HttpManager getStringFromXML:clientSecretResp tag:@"paired"] isEqual:@"1"]) {
pairedString = [HttpManager getStringFromXML:clientSecretResp tag:@"paired"];
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"clientSecretResp failed"];
[_callback pairFailed:@"Pairing stage #4 failed"];
return;
}
NSData* clientPairChallenge = [_httpManager executeRequestSynchronously:[_httpManager newPairChallenge]];
if (![[HttpManager getStringFromXML:clientPairChallenge tag:@"paired"] isEqual:@"1"]) {
pairedString = [HttpManager getStringFromXML:clientPairChallenge tag:@"paired"];
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
//TODO: better message
[_callback pairFailed:@"clientPairChallenge failed"];
[_callback pairFailed:@"Pairing stage #5 failed"];
return;
}
[_callback pairSuccessful];
+10 -3
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:(NSString*)message;
- (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;
+23 -12
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;
@@ -409,7 +408,19 @@ static OSStatus playbackCallback(void *inRefCon,
-(void) main
{
LiStartConnection(_host, &_streamConfig, &_clCallbacks, &_drCallbacks, &_arCallbacks, NULL, 0);
PLATFORM_CALLBACKS dummyPlCallbacks;
// Only used on Windows
dummyPlCallbacks.threadStart = NULL;
dummyPlCallbacks.debugPrint = NULL;
LiStartConnection(_host,
&_streamConfig,
&_clCallbacks,
&_drCallbacks,
&_arCallbacks,
&dummyPlCallbacks,
NULL, 0);
}
+1 -1
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
+47 -12
View File
@@ -14,18 +14,17 @@
@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;
return self;
}
@@ -41,16 +40,34 @@
cert:cert];
NSData* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
if (![[HttpManager getStringFromXML:serverInfoResp tag:@"currentgame"] isEqualToString:@"0"]) {
NSString* currentGame = [HttpManager getStringFromXML:serverInfoResp tag:@"currentgame"];
NSString* pairStatus = [HttpManager getStringFromXML:serverInfoResp tag:@"PairStatus"];
if (currentGame == NULL || pairStatus == NULL) {
[_callbacks launchFailed:@"Failed to connect to PC"];
return;
}
if (![pairStatus isEqualToString:@"1"]) {
// Not paired
[_callbacks launchFailed:@"Device not paired to PC"];
return;
}
// resumeApp and launchApp handle calling launchFailed
if (![currentGame isEqualToString:@"0"]) {
// App already running, resume it
[self resumeApp:hMan];
if (![self resumeApp:hMan]) {
return;
}
} else {
// Start app
[self launchApp:hMan];
if (![self launchApp:hMan]) {
return;
}
}
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 +77,7 @@
[_connection terminate];
}
- (void) launchApp:(HttpManager*)hMan {
- (BOOL) launchApp:(HttpManager*)hMan {
NSData* launchResp = [hMan executeRequestSynchronously:
[hMan newLaunchRequest:@"67339056"
width:_config.width
@@ -68,14 +85,32 @@
refreshRate:_config.frameRate
rikey:[Utils bytesToHex:_config.riKey]
rikeyid:_config.riKeyId]];
[HttpManager getStringFromXML:launchResp tag:@"gamesession"];
NSString *gameSession = [HttpManager getStringFromXML:launchResp tag:@"gamesession"];
if (launchResp == NULL) {
[_callbacks launchFailed:@"Failed to launch app"];
return FALSE;
} else if (gameSession == NULL || [gameSession isEqualToString:@"0"]) {
[_callbacks launchFailed:[HttpManager getStatusStringFromXML:launchResp]];
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 (resumeResp == NULL) {
[_callbacks launchFailed:@"Failed to resume app"];
return FALSE;
} else if (resume == NULL || [resume isEqualToString:@"0"]) {
[_callbacks launchFailed:[HttpManager getStatusStringFromXML:resumeResp]];
return FALSE;
}
return TRUE;
}
@end
+16 -25
View File
@@ -10,10 +10,9 @@
@implementation VideoDecoderRenderer {
AVSampleBufferDisplayLayer* displayLayer;
Boolean waitingForSps, waitingForPpsA, waitingForPpsB;
Boolean waitingForSps, waitingForPps;
NSData *spsData, *ppsDataA, *ppsDataB;
unsigned char ppsDataAFirstByte;
NSData *spsData, *ppsData;
CMVideoFormatDescriptionRef formatDesc;
}
@@ -30,8 +29,7 @@
// We need some parameter sets before we can properly start decoding frames
waitingForSps = true;
waitingForPpsA = true;
waitingForPpsB = true;
waitingForPps = true;
return self;
}
@@ -114,34 +112,27 @@
unsigned char nalType = data[FRAME_START_PREFIX_SIZE] & 0x1F;
OSStatus status;
if (formatDesc == NULL && (nalType == 0x7 || nalType == 0x8)) {
if (waitingForSps && nalType == 0x7) {
if (nalType == 0x7 || nalType == 0x8) {
if (nalType == 0x7) {
printf("Got SPS\n");
spsData = [NSData dataWithBytes:&data[FRAME_START_PREFIX_SIZE] length:length - FRAME_START_PREFIX_SIZE];
waitingForSps = false;
// We got a new SPS so wait for a new PPS to match it
waitingForPps = true;
}
// Nvidia's stream has 2 PPS NALUs so we'll wait for both of them
else if ((waitingForPpsA || waitingForPpsB) && nalType == 0x8) {
// Read the NALU's PPS index to figure out which PPS this is
if (waitingForPpsA) {
printf("Got PPS 1\n");
ppsDataA = [NSData dataWithBytes:&data[FRAME_START_PREFIX_SIZE] length:length - FRAME_START_PREFIX_SIZE];
waitingForPpsA = false;
ppsDataAFirstByte = data[FRAME_START_PREFIX_SIZE + 1];
}
else if (data[FRAME_START_PREFIX_SIZE + 1] != ppsDataAFirstByte) {
printf("Got PPS 2\n");
ppsDataA = [NSData dataWithBytes:&data[FRAME_START_PREFIX_SIZE] length:length - FRAME_START_PREFIX_SIZE];
waitingForPpsB = false;
}
else if (nalType == 0x8) {
printf("Got PPS\n");
ppsData = [NSData dataWithBytes:&data[FRAME_START_PREFIX_SIZE] length:length - FRAME_START_PREFIX_SIZE];
waitingForPps = false;
}
// See if we've got all the parameter sets we need
if (!waitingForSps && !waitingForPpsA && !waitingForPpsB) {
const uint8_t* const parameterSetPointers[] = { [spsData bytes], [ppsDataA bytes], [ppsDataB bytes] };
const size_t parameterSetSizes[] = { [spsData length], [ppsDataA length], [ppsDataB length] };
if (!waitingForSps && !waitingForPps) {
const uint8_t* const parameterSetPointers[] = { [spsData bytes], [ppsData bytes] };
const size_t parameterSetSizes[] = { [spsData length], [ppsData length] };
printf("Constructing format description\n");
printf("Constructing new format description\n");
status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
2, /* count of parameter sets */
parameterSetPointers,
+1
View File
@@ -15,5 +15,6 @@
- (id) initWithHost:(NSNetService*)host;
- (id) initWithIp:(NSString*)host;
- (id) initPlaceholder;
@end
+9
View File
@@ -28,4 +28,13 @@
return self;
}
- (id) initPlaceholder {
self = [super init];
self.hostName = NULL;
self.displayName = @"No computers found";
return self;
}
@end
+1 -1
View File
@@ -67,7 +67,7 @@
return addr;
} else {
NSLog(@"Failed to resolve host: %d", h_errno);
return -1;
return 0;
}
}
}
@@ -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);
@@ -85,29 +114,29 @@ static StreamConfiguration* streamConfig;
streamConfig.width = 1280;
streamConfig.height = 720;
streamConfig.frameRate = 30;
streamConfig.bitRate = 5000;
break;
default:
case 1:
streamConfig.width = 1280;
streamConfig.height = 720;
streamConfig.frameRate = 60;
streamConfig.bitRate = 10000;
break;
case 2:
streamConfig.width = 1920;
streamConfig.height = 1080;
streamConfig.frameRate = 30;
streamConfig.bitRate = 10000;
break;
case 3:
streamConfig.width = 1920;
streamConfig.height = 1080;
streamConfig.frameRate = 60;
break;
default:
streamConfig.width = 1280;
streamConfig.height = 720;
streamConfig.frameRate = 60;
streamConfig.bitRate = 20000;
break;
}
NSLog(@"StreamConfig: %@, %d, %dx%dx%d", streamConfig.host, streamConfig.hostAddr, streamConfig.width, streamConfig.height, streamConfig.frameRate);
NSLog(@"StreamConfig: %@, %d, %dx%dx%d at %d Mbps", streamConfig.host, streamConfig.hostAddr, streamConfig.width, streamConfig.height, streamConfig.frameRate, streamConfig.bitRate);
[self performSegueWithIdentifier:@"createStreamFrame" sender:self];
}
@@ -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,27 +192,51 @@ 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
{
[super viewDidAppear:animated];
_mDNSManager = [[MDNSManager alloc] initWithCallback:self];
[_mDNSManager searchForHosts];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[_mDNSManager stopSearching];
}
- (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
@@ -10,6 +10,8 @@
#import <UIKit/UIKit.h>
@interface StreamFrameViewController : UIViewController <ConTermCallback>
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks>
@property (strong, nonatomic) IBOutlet UILabel *stageLabel;
@property (strong, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
@end
@@ -24,12 +24,16 @@
- (void)viewDidLoad
{
[super viewDidLoad];
[self.stageLabel setText:@"Starting App"];
[UIApplication sharedApplication].idleTimerDisabled = YES;
_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 +48,17 @@
[self performSegueWithIdentifier:@"returnToMainFrame" sender:self];
}
- (void)connectionTerminated {
- (void) connectionStarted {
printf("Connection started\n");
dispatch_async(dispatch_get_main_queue(), ^{
[self.spinner stopAnimating];
[self.stageLabel setText:@"Waiting for first frame..."];
});
}
- (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 +68,47 @@
[_streamMan stopStream];
}
- (void) stageStarting:(char*)stageName {
printf("Starting %s\n", stageName);
dispatch_async(dispatch_get_main_queue(), ^{
NSString* lowerCase = [NSString stringWithFormat:@"%s in progress...", stageName];
NSString* titleCase = [[[lowerCase substringToIndex:1] uppercaseString] stringByAppendingString:[lowerCase substringFromIndex:1]];
[self.stageLabel setText:titleCase];
});
}
- (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:(NSString*)message {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed"
message:message
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) displayMessage:(char*)message {
printf("Display message: %s\n", message);
}
- (void) displayTransientMessage:(char*)message {
printf("Display transient message: %s\n", message);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
+20 -1
View File
@@ -109,7 +109,24 @@
<view key="view" contentMode="scaleToFill" id="VPm-Ae-rc4" userLabel="RenderView" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<subviews>
<activityIndicatorView opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" translatesAutoresizingMaskIntoConstraints="NO" id="iOs-1X-mSU">
<rect key="frame" x="502" y="374" width="20" height="20"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dDs-kT-po6">
<rect key="frame" x="491" y="402" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="dDs-kT-po6" firstAttribute="top" secondItem="iOs-1X-mSU" secondAttribute="bottom" constant="8" id="VZj-wk-dHp"/>
<constraint firstAttribute="centerY" secondItem="iOs-1X-mSU" secondAttribute="centerY" id="YAN-0k-Rds"/>
<constraint firstAttribute="centerX" secondItem="dDs-kT-po6" secondAttribute="centerX" id="bLr-5Z-OH3"/>
<constraint firstAttribute="centerX" secondItem="iOs-1X-mSU" secondAttribute="centerX" id="ruD-8B-gj3"/>
</constraints>
</view>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
@@ -117,6 +134,8 @@
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
<simulatedScreenMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="spinner" destination="iOs-1X-mSU" id="LSl-lr-7fF"/>
<outlet property="stageLabel" destination="dDs-kT-po6" id="ziI-M8-UVf"/>
<segue destination="wb7-af-jn8" kind="modal" identifier="returnToMainFrame" id="Ryr-0y-o43"/>
</connections>
</viewController>
+20 -1
View File
@@ -103,12 +103,31 @@
<view key="view" contentMode="scaleToFill" id="eir-e9-IPE" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" translatesAutoresizingMaskIntoConstraints="NO" id="0vm-Iv-K4b">
<rect key="frame" x="150" y="274" width="20" height="20"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2HK-Z5-4Ch">
<rect key="frame" x="138" y="302" width="45" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="2HK-Z5-4Ch" secondAttribute="centerX" constant="-0.5" id="Blw-Hy-66z"/>
<constraint firstAttribute="centerX" secondItem="0vm-Iv-K4b" secondAttribute="centerX" id="mCQ-CP-Yik"/>
<constraint firstAttribute="centerY" secondItem="0vm-Iv-K4b" secondAttribute="centerY" id="t8e-qp-g1j"/>
<constraint firstItem="2HK-Z5-4Ch" firstAttribute="top" secondItem="0vm-Iv-K4b" secondAttribute="bottom" constant="8" id="tta-Uo-bBO"/>
</constraints>
</view>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="spinner" destination="0vm-Iv-K4b" id="Lif-zG-6Jh"/>
<outlet property="stageLabel" destination="2HK-Z5-4Ch" id="1bI-Ig-OpD"/>
<segue destination="dgh-JZ-Q7z" kind="modal" identifier="returnToMainFrame" id="djB-MF-L5D"/>
</connections>
</viewController>
+3 -5
View File
@@ -3,12 +3,10 @@
[Limelight](https://github.com/limelight-stream) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for iOS. Limelight iOS will allow you to stream your full collection of Steam games from
your powerful desktop computer to your iOS Device.
#####Limelight iOS is not stable or buildable - it is in the very early stages of development. Please refrain from opening issues regarding Limelight iOS - we know it doesn't work yet.
This is in development and is not considered stable.
This is in development and is not considered stable (or buildable). Do not use it. It is not ready.
We understand that Limelight users are eager for the opportunity to stream their Steam collection to their iOS devices. Limelight for iOS is in the testing stage and a preliminary version will soon be available for download.
We understand that Limelight users are eager for the opportunity to stream their Steam collection to their iOS devices. Currently there is no ETA on an alpha version, but we'll post an update whenever we reach that stage.
In the meantime, check out the existing versions for [Android](https://github.com/limelight-stream/limelight-android) and [PC](https://github.com/limelight-stream/limelight-pc). Also, a client for [Windows Phone 8.1](https://github.com/limelight-stream/limelight-wp) is in the works and actively being developed.
In the meantime, check out the existing versions for [Android](https://github.com/limelight-stream/limelight-android) and [PC](https://github.com/limelight-stream/limelight-pc). Also, a client for [Windows and Windows Phone](https://github.com/limelight-stream/limelight-windows) is in the works and actively being developed.
Please be patient as we work to bring Limelight to more platforms.