// // StreamFrameViewController.m // Moonlight // // Created by Diego Waxemberg on 1/18/14. // Copyright (c) 2015 Moonlight Stream. All rights reserved. // #import "StreamFrameViewController.h" #import "MainFrameViewController.h" #import "VideoDecoderRenderer.h" #import "StreamManager.h" #import "ControllerSupport.h" #include #include #include @implementation StreamFrameViewController { ControllerSupport *_controllerSupport; StreamManager *_streamMan; NSTimer *_inactivityTimer; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; #if !TARGET_OS_TV [[self revealViewController] setPrimaryViewController:self]; #endif } - (void)viewDidLoad { [super viewDidLoad]; [self.navigationController setNavigationBarHidden:YES animated:YES]; [self.stageLabel setText:[NSString stringWithFormat:@"Starting %@...", self.streamConfig.appName]]; [self.stageLabel sizeToFit]; self.stageLabel.textAlignment = NSTextAlignmentCenter; self.stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2); self.spinner.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2 - self.stageLabel.frame.size.height - self.spinner.frame.size.height); [UIApplication sharedApplication].idleTimerDisabled = YES; _controllerSupport = [[ControllerSupport alloc] initWithConfig:self.streamConfig]; _inactivityTimer = nil; _streamMan = [[StreamManager alloc] initWithConfig:self.streamConfig renderView:self.view connectionCallbacks:self]; NSOperationQueue* opQueue = [[NSOperationQueue alloc] init]; [opQueue addOperation:_streamMan]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationDidBecomeActive:) name: UIApplicationDidBecomeActiveNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationDidEnterBackground:) name: UIApplicationDidEnterBackgroundNotification object: nil]; } - (void)viewDidDisappear:(BOOL)animated { [_controllerSupport cleanup]; [UIApplication sharedApplication].idleTimerDisabled = NO; [_streamMan stopStream]; if (_inactivityTimer != nil) { [_inactivityTimer invalidate]; _inactivityTimer = nil; } [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void) returnToMainFrame { [self.navigationController popToRootViewControllerAnimated:YES]; } // This will fire if the user opens control center or gets a low battery message - (void)applicationWillResignActive:(NSNotification *)notification { if (_inactivityTimer != nil) { [_inactivityTimer invalidate]; } // Terminate the stream if the app is inactive for 10 seconds Log(LOG_I, @"Starting inactivity termination timer"); _inactivityTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(inactiveTimerExpired:) userInfo:nil repeats:NO]; } - (void)inactiveTimerExpired:(NSTimer*)timer { Log(LOG_I, @"Terminating stream after inactivity"); [self returnToMainFrame]; _inactivityTimer = nil; } - (void)applicationDidBecomeActive:(NSNotification *)notification { // Stop the background timer, since we're foregrounded again if (_inactivityTimer != nil) { Log(LOG_I, @"Stopping inactivity timer after becoming active again"); [_inactivityTimer invalidate]; _inactivityTimer = nil; } } // This fires when the home button is pressed - (void)applicationDidEnterBackground:(UIApplication *)application { Log(LOG_I, @"Terminating stream immediately for backgrounding"); if (_inactivityTimer != nil) { [_inactivityTimer invalidate]; _inactivityTimer = nil; } [self returnToMainFrame]; } - (void)edgeSwiped { Log(LOG_I, @"User swiped to end stream"); [self returnToMainFrame]; } - (void) connectionStarted { Log(LOG_I, @"Connection started"); dispatch_async(dispatch_get_main_queue(), ^{ // Leave the spinner spinning until it's obscured by // the first frame of video. self.stageLabel.hidden = YES; #if !TARGET_OS_TV [(StreamView*)self.view setupOnScreenControls: self->_controllerSupport swipeDelegate:self]; #endif }); } - (void)connectionTerminated:(long)errorCode { Log(LOG_I, @"Connection terminated: %ld", errorCode); dispatch_async(dispatch_get_main_queue(), ^{ // Allow the display to go to sleep now [UIApplication sharedApplication].idleTimerDisabled = NO; UIAlertController* conTermAlert = [UIAlertController alertControllerWithTitle:@"Connection Terminated" message:@"The connection was terminated" preferredStyle:UIAlertControllerStyleAlert]; [conTermAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [self returnToMainFrame]; }]]; [self presentViewController:conTermAlert animated:YES completion:nil]; }); [_streamMan stopStream]; } - (void) stageStarting:(const char*)stageName { Log(LOG_I, @"Starting %s", 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]; [self.stageLabel sizeToFit]; self.stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.stageLabel.center.y); }); } - (void) stageComplete:(const char*)stageName { } - (void) stageFailed:(const char*)stageName withError:(long)errorCode { Log(LOG_I, @"Stage %s failed: %ld", stageName, errorCode); dispatch_async(dispatch_get_main_queue(), ^{ // Allow the display to go to sleep now [UIApplication sharedApplication].idleTimerDisabled = NO; UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed" message:[NSString stringWithFormat:@"%s failed with error %ld", stageName, errorCode] preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"Help" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting"]]; }]]; [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [self returnToMainFrame]; }]]; [self presentViewController:alert animated:YES completion:nil]; }); [_streamMan stopStream]; } - (void) launchFailed:(NSString*)message { Log(LOG_I, @"Launch failed: %@", message); dispatch_async(dispatch_get_main_queue(), ^{ // Allow the display to go to sleep now [UIApplication sharedApplication].idleTimerDisabled = NO; UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed" message:message preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"Help" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting"]]; }]]; [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ [self returnToMainFrame]; }]]; [self presentViewController:alert animated:YES completion:nil]; }); } - (void) displayMessage:(const char*)message { Log(LOG_I, @"Display message: %s", message); } - (void) displayTransientMessage:(const char*)message { Log(LOG_I, @"Display transient message: %s", message); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #if !TARGET_OS_TV // Require a confirmation when streaming to activate a system gesture - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { return UIRectEdgeAll; } - (BOOL)shouldAutorotate { return YES; } #endif @end