/// <summary>
        /// When the user presses "Start Streaming Steam", first check that they are paired
        /// </summary>
        public static async Task<StreamContext> StartStreaming(CoreDispatcher uiDispatcher, Computer computer, MoonlightStreamConfiguration streamConfig)
        {
            PairingManager p = new PairingManager(computer); 

            // If we can't get the pair state, return   
            bool? pairState = await p.QueryPairState();
            if (!pairState.HasValue)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "Pair state query failed", "Failed to start streaming");
                return null;
            }

            // If we're not paired, return
            if (pairState == false)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "Device not paired", "Failed to start streaming");
                return null;
            }

            // Lookup the desired app in the app list
            // NOTE: This will go away when we have a proper app list
            int appId = await LookupAppIdForApp(uiDispatcher, new NvHttp(computer.IpAddress), "Steam");
            if (appId == 0)
            {
                // LookupAppIdForApp() handles displaying a failure dialog
                return null;
            }

            return new StreamContext(computer, appId, streamConfig);
        }
        /// <summary>
        /// Pair with the hostname in the textbox
        /// </summary>
        public async Task Pair(CoreDispatcher uiDispatcher, Computer c)
        {
            Debug.WriteLine("Pairing...");

            // Get the pair state.
            bool? pairState = await QueryPairState(); 
            if (pairState == true)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "This device is already paired to the host PC", "Already Paired");
                return;
            }
            // pairstate = null. We've encountered an error
            else if (!pairState.HasValue)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "Failed to query pair state", "Pairing failed");
                return;
            }

            bool challenge = await PairingCryptoHelpers.PerformPairingHandshake(uiDispatcher, new WindowsCryptoProvider(), nv, nv.GetUniqueId());
            if (!challenge)
            {
                Debug.WriteLine("Challenges failed");
                return; 
            } 

            // Otherwise, everything was successful
            MainPage.SaveComputer(c);

            // FIXME: We can't have two dialogs open at once.
            // DialogUtils.DisplayDialog(uiDispatcher, "Pairing successful", "Success");
        }
Example #3
0
 /// <summary>
 /// Event handler for clicking the add PC manually button
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void Add_Button_Click(object sender, RoutedEventArgs e)
 {
     if (String.IsNullOrWhiteSpace(ip_textbox.Text) || String.IsNullOrWhiteSpace(nickname_textbox.Text))
     {
         DialogUtils.DisplayDialog(this.Dispatcher, "Please fill out both text boxes", "Add PC Failed");
         return;
     }
     else
     {
         Computer toAdd = new Computer(nickname_textbox.Text, ip_textbox.Text);                
         this.Frame.Navigate(typeof(MainPage), toAdd);
     }
 }
 public StreamContext(Computer computer, int appId, MoonlightStreamConfiguration streamConfig)
 {
     this.computer = computer;
     this.appId = appId;
     this.streamConfig = streamConfig;
 }
        /// <summary>
        /// Uses mDNS to enumerate the machines on the network eligible to stream from
        /// </summary>
        /// <returns></returns>
        private async Task EnumerateEligibleMachines()
        {
            // Make a local copy of the computer list
            // The UI thread will populate the listbox with computerList whenever it pleases, so we don't want it to take the one we're modifying
            List<Computer> computerListLocal = new List<Computer>(computerList);
            bool modifiedList = false;

            // Make sure we have the manually added PCs in here
            foreach (var pc in addedPCs)
            {
                if (!computerListLocal.Exists(x => x.IpAddress == pc.IpAddress))
                {
                    computerListLocal.Add(pc);
                    modifiedList = true;
                }
            }

            Debug.WriteLine("Enumerating machines...");

            // If there's no network, save time and don't do the time-consuming mDNS 
            if (!InternetAvailable)
            {
                if (computerListLocal.Count == 0 && !computerListLocal.Contains(noNetwork))
                {
                    computerListLocal.Add(noNetwork);
                    modifiedList = true;
                }
                Debug.WriteLine("Network not available - skipping mDNS");
            }
            else
            {
                // Remove the placeholder
                if (computerListLocal.Contains(noNetwork))
                {
                    Debug.WriteLine("Removing \"no network\" placeholder");
                    computerListLocal.Remove(noNetwork);
                    modifiedList = true;
                }

                // Let Zeroconf do its magic and find everything it can with mDNS
                ILookup<string, string> domains = null;
                try
                {
                    domains = await ZeroconfResolver.BrowseDomainsAsync();
                }
                catch (Exception e)
                {
                    Debug.WriteLine("Browse Domains Async threw exception: " + e.Message);
                }

                IReadOnlyList<IZeroconfHost> responses = null;
                if (domains != null)
                {
                    try
                    {
                        responses = await ZeroconfResolver.ResolveAsync(domains.Select(g => g.Key));

                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("Exception in ZeroconfResolver.ResolverAsyc (Expected if BrowseDomainsAsync excepted): " + e.Message);
                    }
                }

                if (responses != null)
                {
                    // Remove the "not found" placeholder
                    if (computerList.Contains(notFound))
                    {
                        Debug.WriteLine("Removing \"not found\" placeholder");
                        computerList.Remove(notFound);
                        modifiedList = true;
                    }

                    // Go through every response we received and grab only the ones running nvstream
                    foreach (var resp in responses)
                    {
                        if (resp.Services.ContainsKey("_nvstream._tcp.local."))
                        {
                            Computer toAdd = new Computer(resp.DisplayName, resp.IPAddress);

                            // If we don't have the computer already, add it
                            if (!computerListLocal.Exists(x => x.IpAddress == resp.IPAddress))
                            {
                                computerListLocal.Add(toAdd);
                                Debug.WriteLine(resp);
                                modifiedList = true;
                            }
                        }
                    }
                }

                // We're done messing with the list - it's okay for the UI thread to update it now
                Computer last = LoadComputer();
                if (last != null)
                {
                    // If we don't have the computer already, add it
                    if (!computerListLocal.Exists(x => x.IpAddress == last.IpAddress))
                    {
                        modifiedList = true;
                        computerListLocal.Add(last);
                    }
                }

                // If no computers at all, say none are found.
                if (computerListLocal.Count == 0)
                {
                    Debug.WriteLine("Not Found");
                    modifiedList = true;
                    computerListLocal.Add(notFound);
                }
            }

            if (modifiedList)
            {
                computerList = computerListLocal;

                if (computerPicker.SelectedIndex == -1)
                {
                    computerPicker.ItemsSource = computerList;
                }
            }
        }
 /// <summary>
 /// Once we freshly pair to a computer, save it
 /// </summary>
 /// <param name="c">Computer we've paired to</param>
 public static void SaveComputer(Computer c)
 {
     var settings = ApplicationData.Current.RoamingSettings;
     settings.Values["computerName"] = c.Name;
     settings.Values["computerIP"] = c.IpAddress;
 }
 /// <summary>
 /// Constructor that sets nv 
 /// </summary>
 /// <param name="nv">The NvHttp Object</param>
 public PairingManager(Computer computer)
 {
     this.nv = new NvHttp(computer.IpAddress); 
 }
        /// <summary>
        /// Executed when the user presses "Start Streaming Steam!"
        /// </summary>
        private async Task StreamButton_Click_Common()
        {
            Debug.WriteLine("Start Streaming button pressed");

            selected = (Computer)computerPicker.SelectedItem;

            // User hasn't selected a machine or selected a placeholder
            if (selected == null || String.IsNullOrWhiteSpace(selected.IpAddress))
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "No machine selected", "Streaming Failed");
            }
            else
            {
                // Stop enumerating machines while we're trying to check pair state
                mDnsTimer.Stop();

                byte[] aesKey = PairingCryptoHelpers.GenerateRandomBytes(16);

                // GameStream only uses 4 bytes of a 16 byte IV. Go figure.
                byte[] aesRiIndex = PairingCryptoHelpers.GenerateRandomBytes(4);
                byte[] aesIv = new byte[16];
                Array.ConstrainedCopy(aesRiIndex, 0, aesIv, 0, aesRiIndex.Length);
                SettingsPage s = new SettingsPage();
                MoonlightStreamConfiguration config = new MoonlightStreamConfiguration(
                    s.GetStreamWidth(),
                    s.GetStreamHeight(),
                    s.GetStreamFps(),
                    10000, // FIXME: Scale by resolution
                    1024,
                    aesKey, aesIv);

                StreamContext context = await ConnectionManager.StartStreaming(this.Dispatcher, selected, config);
                if (context != null)
                {
                    this.Frame.Navigate(typeof(StreamFrame), context);
                }
            }
        }