/* * This file is part of Moonlight Embedded. * * Copyright (C) 2015 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Moonlight is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Moonlight; if not, see . */ #include "client.h" #include "connection.h" #include "video.h" #include "audio.h" #include "input.h" #include "discover.h" #include "limelight-common/Limelight.h" #include #include #include #include #include #include #include #include #include #include #include PLATFORM_CALLBACKS platform_callbacks = { .threadStart = NULL, .debugPrint = NULL, }; static void applist(const char* address) { struct app_list* list = client_applist(address); for (int i = 1;list != NULL;i++) { printf("%d. %s\n", i, list->name); list = list->next; } } static void stream(STREAM_CONFIGURATION* config, const char* address, const char* app, bool sops, bool localaudio) { int appId = client_get_app_id(address, app); if (appId<0) { printf("Can't find app %s\n", app); exit(-1); } client_start_app(config, address, appId, sops, localaudio); struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; int err = getaddrinfo(address, NULL, &hints, &res); if (err<0 || res == NULL) { printf("Can't resolve host: %s\n", address); exit(-1); } struct sockaddr_in *addr = (struct sockaddr_in*)res->ai_addr; LiStartConnection(addr->sin_addr.s_addr, config, &connection_callbacks, &decoder_callbacks, &audio_callbacks, &platform_callbacks, NULL, 0, client_get_server_version()); freeaddrinfo(res); input_loop(); LiStopConnection(); } static void help() { printf("Usage: moonlight action [options] host\n\n"); printf(" Actions\n\n"); printf("\tmap\t\t\tCreate mapping file for gamepad\n"); printf("\tpair\t\t\tPair device with computer\n"); printf("\tstream\t\t\tStream computer to device\n"); printf("\tlist\t\t\tList available games and applications\n"); printf("\thelp\t\t\tShow this help\n\n"); printf(" Streaming options\n\n"); printf("\t-720\t\t\tUse 1280x720 resolution [default]\n"); printf("\t-1080\t\t\tUse 1920x1080 resolution\n"); printf("\t-width \t\tHorizontal resolution (default 1280)\n"); printf("\t-height \tVertical resolution (default 720)\n"); printf("\t-30fps\t\t\tUse 30fps\n"); printf("\t-60fps\t\t\tUse 60fps [default]\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); printf("\t-app \t\tName of app to stream\n"); printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-audio \t\tUse as ALSA audio output device (default sysdefault)\n"); printf("\t-localaudio\t\tPlay audio locally\n"); exit(0); } int main(int argc, char* argv[]) { STREAM_CONFIGURATION config; config.width = 1280; config.height = 720; config.fps = 60; config.bitrate = 8000; config.packetSize = 1024; static struct option long_options[] = { {"720", no_argument, 0, 'a'}, {"1080", no_argument, 0, 'b'}, {"width", required_argument, 0, 'c'}, {"height", required_argument, 0, 'd'}, {"30fps", no_argument, 0, 'e'}, {"60fps", no_argument, 0, 'f'}, {"bitrate", required_argument, 0, 'g'}, {"packetsize", required_argument, 0, 'h'}, {"app", required_argument, 0, 'i'}, {"input", required_argument, 0, 'j'}, {"mapping", required_argument, 0, 'k'}, {"nosops", no_argument, 0, 'l'}, {"audio", required_argument, 0, 'm'}, {"localaudio", no_argument, 0, 'n'}, {0, 0, 0, 0}, }; char* app = "Steam"; char* action = NULL; char* address = NULL; char* mapping = NULL; int option_index = 0; bool sops = true; bool localaudio = false; int c; while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:n", long_options, &option_index)) != -1) { switch (c) { case 'a': config.width = 720; config.height = 1280; break; case 'b': config.width = 1920; config.height = 1080; break; case 'c': config.width = atoi(optarg); break; case 'd': config.height = atoi(optarg); break; case 'e': config.fps = 30; break; case 'f': config.fps = 60; break; case 'g': config.bitrate = atoi(optarg); break; case 'h': config.packetSize = atoi(optarg); break; case 'i': app = optarg; break; case 'j': input_create(optarg, mapping); break; case 'k': mapping = optarg; break; case 'l': sops = false; break; case 'm': audio_device = optarg; break; case 'n': localaudio = true; break; case 1: if (action == NULL) action = optarg; else if (address == NULL) address = optarg; else { perror("Too many options"); exit(-1); } } } if (action == NULL || strcmp("help", action) == 0) help(); else if (strcmp("map", action) == 0) { if (address == NULL) { perror("No filename for mapping"); exit(-1); } input_map(address); exit(0); } if (address == NULL) { address = malloc(MAX_ADDRESS_SIZE); if (address == NULL) { perror("Not enough memory"); exit(-1); } address[0] = 0; discover_server(address); if (address[0] == 0) { perror("Can't find server"); exit(-1); } } input_init(mapping); client_init(address); if (strcmp("applist", action) == 0) applist(address); else if (strcmp("stream", action) == 0) stream(&config, address, app, sops, localaudio); else if (strcmp("pair", action) == 0) client_pair(address); else fprintf(stderr, "%s is not a valid actions\n", action); }