mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-02-16 10:30:47 +00:00
Merge branch 'pc'
This commit is contained in:
@@ -21,6 +21,10 @@ find_package(CEC)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(EVDEV REQUIRED libevdev)
|
||||
pkg_check_modules(UDEV REQUIRED libudev)
|
||||
pkg_check_modules(SDL sdl2)
|
||||
pkg_check_modules(AVCODEC libavcodec)
|
||||
pkg_check_modules(AVUTIL libavutil)
|
||||
pkg_check_modules(SWSCALE libswscale)
|
||||
|
||||
if(CEC_FOUND)
|
||||
include_directories(./third_party/libcec)
|
||||
@@ -46,6 +50,13 @@ if(FREESCALE_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
||||
endif()
|
||||
|
||||
if (AVCODEC_FOUND AND AVUTIL_FOUND AND SWSCALE_FOUND AND SDL_FOUND)
|
||||
list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
||||
endif()
|
||||
|
||||
list(APPEND SRC_LIST ./src/audio/alsa.c)
|
||||
|
||||
add_subdirectory(libgamestream)
|
||||
|
||||
add_executable(moonlight ${SRC_LIST})
|
||||
@@ -69,6 +80,11 @@ if(FREESCALE_FOUND)
|
||||
target_link_libraries (moonlight PUBLIC ${FREESCALE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (AVCODEC_FOUND AND AVUTIL_FOUND AND SWSCALE_FOUND AND SDL_FOUND)
|
||||
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS} ${SWSCALE_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight PUBLIC ${SDL_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES} ${SWSCALE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${MOONLIGHT_DEFINITIONS})
|
||||
target_include_directories(moonlight PRIVATE ./libgamestream ./third_party/moonlight-common-c ${OPUS_INCLUDE_DIRS} ${EVDEV_INCLUDE_DIRS} ${UDEV_INCLUDE_DIRS})
|
||||
target_link_libraries (moonlight PUBLIC ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRARY} ${UDEV_LIBRARIES} ${CMAKE_DL_LIBS})
|
||||
|
||||
@@ -21,4 +21,7 @@
|
||||
|
||||
extern const char* audio_device;
|
||||
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks;
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
|
||||
#ifdef HAVE_SDL
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl;
|
||||
#endif
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "audio.h"
|
||||
#include "../audio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <opus.h>
|
||||
@@ -35,7 +35,7 @@ static snd_pcm_t *handle;
|
||||
static OpusDecoder* decoder;
|
||||
static short pcmBuffer[FRAME_SIZE * CHANNEL_COUNT];
|
||||
|
||||
static void audio_renderer_init() {
|
||||
static void alsa_renderer_init() {
|
||||
int rc;
|
||||
decoder = opus_decoder_create(SAMPLE_RATE, CHANNEL_COUNT, &rc);
|
||||
|
||||
@@ -71,7 +71,7 @@ static void audio_renderer_init() {
|
||||
CHECK_RETURN(snd_pcm_prepare(handle));
|
||||
}
|
||||
|
||||
static void audio_renderer_cleanup() {
|
||||
static void alsa_renderer_cleanup() {
|
||||
if (decoder != NULL)
|
||||
opus_decoder_destroy(decoder);
|
||||
|
||||
@@ -81,7 +81,7 @@ static void audio_renderer_cleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_renderer_decode_and_play_sample(char* data, int length) {
|
||||
static void alsa_renderer_decode_and_play_sample(char* data, int length) {
|
||||
int decodeLen = opus_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||
if (decodeLen > 0) {
|
||||
int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
||||
@@ -97,8 +97,8 @@ static void audio_renderer_decode_and_play_sample(char* data, int length) {
|
||||
}
|
||||
}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS audio_callbacks = {
|
||||
.init = audio_renderer_init,
|
||||
.cleanup = audio_renderer_cleanup,
|
||||
.decodeAndPlaySample = audio_renderer_decode_and_play_sample,
|
||||
AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa = {
|
||||
.init = alsa_renderer_init,
|
||||
.cleanup = alsa_renderer_cleanup,
|
||||
.decodeAndPlaySample = alsa_renderer_decode_and_play_sample,
|
||||
};
|
||||
79
src/audio/sdl.c
Normal file
79
src/audio/sdl.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../audio.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_audio.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <opus.h>
|
||||
|
||||
#define SAMPLE_RATE 48000
|
||||
#define CHANNEL_COUNT 2
|
||||
#define FRAME_SIZE 240
|
||||
|
||||
static OpusDecoder* decoder;
|
||||
static short pcmBuffer[FRAME_SIZE * CHANNEL_COUNT];
|
||||
static SDL_AudioDeviceID dev;
|
||||
|
||||
static void sdl_renderer_init() {
|
||||
int rc;
|
||||
decoder = opus_decoder_create(SAMPLE_RATE, CHANNEL_COUNT, &rc);
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
SDL_AudioSpec want, have;
|
||||
SDL_zero(want);
|
||||
want.freq = SAMPLE_RATE;
|
||||
want.format = AUDIO_S16LSB;
|
||||
want.channels = CHANNEL_COUNT;
|
||||
want.samples = 4096;
|
||||
|
||||
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
|
||||
if (dev == 0) {
|
||||
printf("Failed to open audio: %s\n", SDL_GetError());
|
||||
} else {
|
||||
if (have.format != want.format) // we let this one thing change.
|
||||
printf("We didn't get requested audio format.\n");
|
||||
SDL_PauseAudioDevice(dev, 0); // start audio playing.
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl_renderer_cleanup() {
|
||||
if (decoder != NULL)
|
||||
opus_decoder_destroy(decoder);
|
||||
|
||||
SDL_CloseAudioDevice(dev);
|
||||
}
|
||||
|
||||
static void sdl_renderer_decode_and_play_sample(char* data, int length) {
|
||||
int decodeLen = opus_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||
if (decodeLen > 0) {
|
||||
SDL_QueueAudio(dev, pcmBuffer, decodeLen * CHANNEL_COUNT * sizeof(short));
|
||||
} else {
|
||||
printf("Opus error from decode: %d\n", decodeLen);
|
||||
}
|
||||
}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl = {
|
||||
.init = sdl_renderer_init,
|
||||
.cleanup = sdl_renderer_cleanup,
|
||||
.decodeAndPlaySample = sdl_renderer_decode_and_play_sample,
|
||||
};
|
||||
14
src/config.c
14
src/config.c
@@ -49,6 +49,7 @@ static struct option long_options[] = {
|
||||
{"audio", required_argument, NULL, 'm'},
|
||||
{"localaudio", no_argument, NULL, 'n'},
|
||||
{"config", required_argument, NULL, 'o'},
|
||||
{"platform", required_argument, 0, 'p'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
@@ -126,7 +127,13 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
config->app = value;
|
||||
break;
|
||||
case 'j':
|
||||
evdev_create(value, config->mapping);
|
||||
if (config->inputsCount >= MAX_INPUTS) {
|
||||
perror("Too many inputs specified");
|
||||
exit(-1);
|
||||
}
|
||||
config->inputs[config->inputsCount].path = optarg;
|
||||
config->inputs[config->inputsCount].mapping = optarg;
|
||||
config->inputsCount++;
|
||||
inputAdded = true;
|
||||
mapped = true;
|
||||
break;
|
||||
@@ -150,6 +157,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
case 'o':
|
||||
config_file_parse(value, config);
|
||||
break;
|
||||
case 'p':
|
||||
config->platform = optarg;
|
||||
break;
|
||||
case 1:
|
||||
if (config->action == NULL)
|
||||
config->action = value;
|
||||
@@ -191,12 +201,14 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
config->stream.bitrate = -1;
|
||||
config->stream.packetSize = 1024;
|
||||
|
||||
config->platform = "default";
|
||||
config->app = "Steam";
|
||||
config->action = NULL;
|
||||
config->address = NULL;
|
||||
config->sops = true;
|
||||
config->localaudio = false;
|
||||
|
||||
config->inputsCount = 0;
|
||||
config->mapping = get_path("mappings/default.conf");
|
||||
|
||||
int option_index = 0;
|
||||
|
||||
10
src/config.h
10
src/config.h
@@ -21,14 +21,24 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_INPUTS 6
|
||||
|
||||
struct input_config {
|
||||
char* path;
|
||||
char* mapping;
|
||||
};
|
||||
|
||||
typedef struct _CONFIGURATION {
|
||||
STREAM_CONFIGURATION stream;
|
||||
char* app;
|
||||
char* action;
|
||||
char* address;
|
||||
char* mapping;
|
||||
char* platform;
|
||||
bool sops;
|
||||
bool localaudio;
|
||||
struct input_config inputs[MAX_INPUTS];
|
||||
int inputsCount;
|
||||
} CONFIGURATION, *PCONFIGURATION;
|
||||
|
||||
bool inputAdded;
|
||||
|
||||
47
src/main.c
47
src/main.c
@@ -24,6 +24,8 @@
|
||||
#include "audio.h"
|
||||
#include "discover.h"
|
||||
#include "config.h"
|
||||
#include "platform.h"
|
||||
#include "sdl.h"
|
||||
|
||||
#include "input/evdev.h"
|
||||
#include "input/udev.h"
|
||||
@@ -72,7 +74,7 @@ static int get_app_id(PSERVER_DATA server, const char *name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void stream(PSERVER_DATA server, PCONFIGURATION config) {
|
||||
static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform system) {
|
||||
int appId = get_app_id(server, config->app);
|
||||
if (appId<0) {
|
||||
fprintf(stderr, "Can't find app %s\n", config->app);
|
||||
@@ -81,17 +83,14 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config) {
|
||||
|
||||
gs_start_app(server, &config->stream, appId, config->sops, config->localaudio);
|
||||
|
||||
video_init();
|
||||
evdev_init();
|
||||
#ifdef HAVE_LIBCEC
|
||||
cec_init();
|
||||
#endif /* HAVE_LIBCEC */
|
||||
LiStartConnection(server->address, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system), NULL, 0, server->serverMajorVersion);
|
||||
|
||||
LiStartConnection(server->address, &config->stream, &connection_callbacks, decoder_callbacks, &audio_callbacks, NULL, 0, server->serverMajorVersion);
|
||||
|
||||
evdev_start();
|
||||
loop_main();
|
||||
evdev_stop();
|
||||
if (IS_EMBEDDED(system))
|
||||
loop_main();
|
||||
#ifdef HAVE_SDL
|
||||
else if (system == SDL)
|
||||
sdl_loop();
|
||||
#endif
|
||||
|
||||
LiStopConnection();
|
||||
}
|
||||
@@ -138,12 +137,22 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
if (config.action == NULL || strcmp("help", config.action) == 0)
|
||||
help();
|
||||
else if (strcmp("map", config.action) == 0) {
|
||||
|
||||
enum platform system = platform_check(config.platform);
|
||||
if (system != 0) {
|
||||
fprintf(stderr, "Platform '%s' not found\n", config.platform);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (strcmp("map", config.action) == 0) {
|
||||
if (config.address == NULL) {
|
||||
perror("No filename for mapping");
|
||||
exit(-1);
|
||||
}
|
||||
udev_init(!inputAdded, config.mapping);
|
||||
for (int i=0;i<config.inputsCount;i++)
|
||||
evdev_create(config.inputs[i].path, config.inputs[i].mapping);
|
||||
|
||||
evdev_map(config.address);
|
||||
exit(0);
|
||||
}
|
||||
@@ -172,9 +181,19 @@ int main(int argc, char* argv[]) {
|
||||
pair_check(server);
|
||||
applist(server);
|
||||
} else if (strcmp("stream", config.action) == 0) {
|
||||
udev_init(!inputAdded, config.mapping);
|
||||
pair_check(server);
|
||||
stream(server, &config);
|
||||
if (IS_EMBEDDED(system)) {
|
||||
for (int i=0;i<config.inputsCount;i++)
|
||||
evdev_create(config.inputs[i].path, config.inputs[i].mapping);
|
||||
|
||||
udev_init(!inputAdded, config.mapping);
|
||||
evdev_init();
|
||||
#ifdef HAVE_LIBCEC
|
||||
cec_init();
|
||||
#endif /* HAVE_LIBCEC */
|
||||
}
|
||||
|
||||
stream(server, &config, system);
|
||||
} else if (strcmp("pair", config.action) == 0) {
|
||||
char pin[5];
|
||||
sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
|
||||
|
||||
82
src/platform.c
Normal file
82
src/platform.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "platform.h"
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
enum platform platform_check(char* name) {
|
||||
bool std = strcmp(name, "default") == 0;
|
||||
#ifdef HAVE_SDL
|
||||
if (std || strcmp(name, "sdl") == 0)
|
||||
return SDL;
|
||||
#endif
|
||||
#ifdef HAVE_IMX
|
||||
if (std || strcmp(name, "imx") == 0) {
|
||||
if (dlsym(RTLD_DEFAULT, "vpu_Init") != NULL && video_imx_init())
|
||||
return IMX;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_OMX
|
||||
if (std || strcmp(name, "omx") == 0) {
|
||||
if (dlsym(RTLD_DEFAULT, "bcm_host_init") != NULL)
|
||||
return OMX;
|
||||
}
|
||||
#endif
|
||||
if (std || strcmp(name, "fake") == 0)
|
||||
return FAKE;
|
||||
}
|
||||
|
||||
DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_SDL
|
||||
case SDL:
|
||||
return &decoder_callbacks_sdl;
|
||||
#endif
|
||||
#ifdef HAVE_IMX
|
||||
case IMX:
|
||||
return &decoder_callbacks_imx;
|
||||
#endif
|
||||
#ifdef HAVE_OMX
|
||||
case OMX:
|
||||
return &decoder_callbacks_omx;
|
||||
#endif
|
||||
case FAKE:
|
||||
return &decoder_callbacks_fake;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_SDL
|
||||
case SDL:
|
||||
return &audio_callbacks_sdl;
|
||||
#endif
|
||||
default:
|
||||
return &audio_callbacks_alsa;
|
||||
}
|
||||
}
|
||||
@@ -17,36 +17,19 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "video.h"
|
||||
|
||||
#include "limelight-common/Limelight.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
DECODER_RENDERER_CALLBACKS *decoder_callbacks;
|
||||
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
||||
|
||||
static int decoder_level;
|
||||
enum platform { SDL, OMX, IMX, FAKE };
|
||||
|
||||
void video_init() {
|
||||
#ifdef HAVE_FAKE
|
||||
decoder_callbacks = &decoder_callbacks_fake;
|
||||
#endif
|
||||
#ifdef HAVE_IMX
|
||||
if (dlsym(RTLD_DEFAULT, "vpu_Init") != NULL && video_imx_init()) {
|
||||
decoder_callbacks = &decoder_callbacks_imx;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_OMX
|
||||
if (dlsym(RTLD_DEFAULT, "bcm_host_init") != NULL) {
|
||||
decoder_callbacks = &decoder_callbacks_omx;
|
||||
}
|
||||
#endif
|
||||
if (decoder_callbacks == NULL) {
|
||||
fprintf(stderr, "No video output available\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
enum platform platform_check(char*);
|
||||
DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system);
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
void sdl_loop();
|
||||
#endif
|
||||
178
src/sdl.c
Normal file
178
src/sdl.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
#include "sdl.h"
|
||||
|
||||
#include "limelight-common/Limelight.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL.h>
|
||||
|
||||
static bool done;
|
||||
|
||||
char leftTrigger, rightTrigger;
|
||||
short leftStickX, leftStickY;
|
||||
short rightStickX, rightStickY;
|
||||
int buttons;
|
||||
|
||||
void sdl_loop() {
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
|
||||
SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
if (!SDL_GameControllerOpen(i)) {
|
||||
fprintf(stderr, "Could not open gamecontroller %i: %s\n", i, SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
|
||||
while(!done && SDL_WaitEvent(&event)) {
|
||||
int button = 0;
|
||||
switch (event.type) {
|
||||
case SDL_MOUSEMOTION:
|
||||
LiSendMouseMoveEvent(event.motion.xrel, event.motion.yrel);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
LiSendScrollEvent(event.wheel.y);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
switch (event.button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
button = BUTTON_LEFT;
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
button = BUTTON_MIDDLE;
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
button = BUTTON_RIGHT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (button != 0)
|
||||
LiSendMouseButtonEvent(event.type==SDL_MOUSEBUTTONDOWN?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, button);
|
||||
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
button = event.key.keysym.sym;
|
||||
if (button >= (0x40000000 + 0x39) && button < (0x40000000 + sizeof(keyCodes)))
|
||||
button = keyCodes[button - 0x40000039];
|
||||
if (button >= 0x61)
|
||||
button -= 0x20;
|
||||
|
||||
LiSendKeyboardEvent(0x80 << 8 | button, event.type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, 0);
|
||||
break;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
switch (event.caxis.axis) {
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
leftStickX = event.caxis.value;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||
leftStickY = -event.caxis.value - 1;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||
rightStickX = event.caxis.value;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||
rightStickY = -event.caxis.value - 1;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||
leftTrigger = (event.caxis.value >> 8) + 127;
|
||||
break;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||
rightTrigger = (event.caxis.value >> 8) + 127;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
LiSendControllerEvent(0, leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY);
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
switch (event.cbutton.button) {
|
||||
case SDL_CONTROLLER_BUTTON_A:
|
||||
button = A_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_B:
|
||||
button = B_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_Y:
|
||||
button = Y_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_X:
|
||||
button = X_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP:
|
||||
button = UP_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
|
||||
button = DOWN_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
|
||||
button = RIGHT_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
|
||||
button = LEFT_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_BACK:
|
||||
button = BACK_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_START:
|
||||
button = PLAY_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_GUIDE:
|
||||
button = SPECIAL_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
|
||||
button = LS_CLK_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
|
||||
button = RS_CLK_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
|
||||
button = LB_FLAG;
|
||||
break;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
|
||||
button = RB_FLAG;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (event.type == SDL_CONTROLLERBUTTONDOWN)
|
||||
buttons |= button;
|
||||
else
|
||||
buttons &= ~button;
|
||||
|
||||
LiSendControllerEvent(buttons, leftTrigger, rightTrigger, leftStickX, leftStickY, rightStickX, rightStickY);
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_SDL */
|
||||
52
src/sdl.h
Normal file
52
src/sdl.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
|
||||
static const short keyCodes[] = {
|
||||
0x14, //SDLK_CAPSLOCK
|
||||
0x70, //SDLK_F1
|
||||
0x71, //SDLK_F2
|
||||
0x72, //SDLK_F3
|
||||
0x73, //SDLK_F4
|
||||
0x74, //SDLK_F5
|
||||
0x75, //SDLK_F6
|
||||
0x76, //SDLK_F7
|
||||
0x77, //SDLK_F8
|
||||
0x78, //SDLK_F9
|
||||
0x79, //SDLK_F10
|
||||
0x7A, //SDLK_F11
|
||||
0x7B, //SDLK_F12
|
||||
0, //SDLK_PRINTSCREEN
|
||||
0x91, //SDLK_SCROLLLOCK
|
||||
0x13, //SDLK_PAUSE
|
||||
0x9B, //SDLK_INSERT
|
||||
0x24, //SDLK_HOME
|
||||
0x21, //SDLK_PAGEUP
|
||||
0x23, //SDLK_END
|
||||
0x22, //SDLK_PAGEDOWN
|
||||
0x27, //SDLK_RIGHT
|
||||
0x25, //SDLK_LEFT
|
||||
0x28, //SDLK_DOWN
|
||||
0x26, //SDLK_UP
|
||||
};
|
||||
|
||||
void sdl_loop();
|
||||
|
||||
#endif /* HAVE_SDL */
|
||||
@@ -19,13 +19,10 @@
|
||||
|
||||
#include "limelight-common/Limelight.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
extern DECODER_RENDERER_CALLBACKS *decoder_callbacks;
|
||||
|
||||
void video_init();
|
||||
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;
|
||||
#ifdef HAVE_SDL
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl;
|
||||
#endif
|
||||
#ifdef HAVE_OMX
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_omx;
|
||||
#endif
|
||||
|
||||
165
src/video/ffmpeg.c
Normal file
165
src/video/ffmpeg.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Based on Moonlight Pc implementation
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ffmpeg.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// General decoder and renderer state
|
||||
static AVPacket pkt;
|
||||
static AVCodec* decoder;
|
||||
static AVCodecContext* decoder_ctx;
|
||||
static AVFrame* dec_frame;
|
||||
static struct SwsContext* scaler_ctx;
|
||||
|
||||
#define BYTES_PER_PIXEL 4
|
||||
|
||||
// This function must be called before
|
||||
// any other decoding functions
|
||||
int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) {
|
||||
// Initialize the avcodec library and register codecs
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
avcodec_register_all();
|
||||
|
||||
av_init_packet(&pkt);
|
||||
|
||||
decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
if (decoder == NULL) {
|
||||
printf("Couldn't find H264 decoder");
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder_ctx = avcodec_alloc_context3(decoder);
|
||||
if (decoder_ctx == NULL) {
|
||||
printf("Couldn't allocate context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_lvl & DISABLE_LOOP_FILTER)
|
||||
// Skip the loop filter for performance reasons
|
||||
decoder_ctx->skip_loop_filter = AVDISCARD_ALL;
|
||||
|
||||
if (perf_lvl & LOW_LATENCY_DECODE)
|
||||
// Use low delay single threaded encoding
|
||||
decoder_ctx->flags |= CODEC_FLAG_LOW_DELAY;
|
||||
|
||||
if (perf_lvl & SLICE_THREADING)
|
||||
decoder_ctx->thread_type = FF_THREAD_SLICE;
|
||||
else
|
||||
decoder_ctx->thread_type = FF_THREAD_FRAME;
|
||||
|
||||
decoder_ctx->thread_count = thread_count;
|
||||
|
||||
decoder_ctx->width = width;
|
||||
decoder_ctx->height = height;
|
||||
decoder_ctx->pix_fmt = PIX_FMT_YUV420P;
|
||||
|
||||
int err = avcodec_open2(decoder_ctx, decoder, NULL);
|
||||
if (err < 0) {
|
||||
printf("Couldn't open codec");
|
||||
return err;
|
||||
}
|
||||
|
||||
dec_frame = av_frame_alloc();
|
||||
if (dec_frame == NULL) {
|
||||
printf("Couldn't allocate frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int filtering;
|
||||
if (perf_lvl & FAST_BILINEAR_FILTERING)
|
||||
filtering = SWS_FAST_BILINEAR;
|
||||
else if (perf_lvl & BILINEAR_FILTERING)
|
||||
filtering = SWS_BILINEAR;
|
||||
else
|
||||
filtering = SWS_BICUBIC;
|
||||
|
||||
scaler_ctx = sws_getContext(decoder_ctx->width, decoder_ctx->height, decoder_ctx->pix_fmt, decoder_ctx->width, decoder_ctx->height, PIX_FMT_YUV420P, filtering, NULL, NULL, NULL);
|
||||
if (scaler_ctx == NULL) {
|
||||
printf("Couldn't get scaler context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function must be called after
|
||||
// decoding is finished
|
||||
void ffmpeg_destroy(void) {
|
||||
if (decoder_ctx) {
|
||||
avcodec_close(decoder_ctx);
|
||||
av_free(decoder_ctx);
|
||||
decoder_ctx = NULL;
|
||||
}
|
||||
if (scaler_ctx) {
|
||||
sws_freeContext(scaler_ctx);
|
||||
scaler_ctx = NULL;
|
||||
}
|
||||
if (dec_frame) {
|
||||
av_frame_free(&dec_frame);
|
||||
dec_frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int ffmpeg_draw_frame(AVFrame *pict) {
|
||||
int err = sws_scale(scaler_ctx, (const uint8_t* const*) dec_frame->data, dec_frame->linesize, 0, decoder_ctx->height, pict->data, pict->linesize);
|
||||
|
||||
if (err != decoder_ctx->height) {
|
||||
fprintf(stderr, "Scaling failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
AVFrame* ffmpeg_get_frame() {
|
||||
return dec_frame;
|
||||
}
|
||||
|
||||
// packets must be decoded in order
|
||||
// indata must be inlen + FF_INPUT_BUFFER_PADDING_SIZE in length
|
||||
int ffmpeg_decode(unsigned char* indata, int inlen) {
|
||||
int err;
|
||||
int got_pic = 0;
|
||||
|
||||
pkt.data = indata;
|
||||
pkt.size = inlen;
|
||||
|
||||
while (pkt.size > 0) {
|
||||
got_pic = 0;
|
||||
err = avcodec_decode_video2(decoder_ctx, dec_frame, &got_pic, &pkt);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "Decode failed\n");
|
||||
got_pic = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pkt.size -= err;
|
||||
pkt.data += err;
|
||||
}
|
||||
|
||||
if (got_pic) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
40
src/video/ffmpeg.h
Normal file
40
src/video/ffmpeg.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Based on Moonlight Pc implementation
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
// Disables the deblocking filter at the cost of image quality
|
||||
#define DISABLE_LOOP_FILTER 0x1
|
||||
// Uses the low latency decode flag (disables multithreading)
|
||||
#define LOW_LATENCY_DECODE 0x2
|
||||
// Threads process each slice, rather than each frame
|
||||
#define SLICE_THREADING 0x4
|
||||
// Uses nonstandard speedup tricks
|
||||
#define FAST_DECODE 0x8
|
||||
// Uses bilinear filtering instead of bicubic
|
||||
#define BILINEAR_FILTERING 0x10
|
||||
// Uses a faster bilinear filtering with lower image quality
|
||||
#define FAST_BILINEAR_FILTERING 0x20
|
||||
|
||||
int ffmpeg_init(int width, int height, int perf_lvl, int thread_count);
|
||||
void ffmpeg_destroy(void);
|
||||
|
||||
int ffmpeg_draw_frame(AVFrame *pict);
|
||||
AVFrame* ffmpeg_get_frame();
|
||||
int ffmpeg_decode(unsigned char* indata, int inlen);
|
||||
112
src/video/sdl.c
Normal file
112
src/video/sdl.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ffmpeg.h"
|
||||
|
||||
#include "limelight-common/Limelight.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
|
||||
#define DECODER_BUFFER_SIZE 92*1024
|
||||
|
||||
static SDL_Window *window;
|
||||
static SDL_Renderer *renderer;
|
||||
static SDL_Texture *bmp = NULL;
|
||||
static int screen_width, screen_height;
|
||||
static char* ffmpeg_buffer;
|
||||
|
||||
static void sdl_setup(int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
int avc_flags = FAST_BILINEAR_FILTERING;
|
||||
if (ffmpeg_init(width, height, 2, avc_flags) < 0) {
|
||||
fprintf(stderr, "Couldn't initialize video decoding\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (ffmpeg_buffer == NULL) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
screen_width = width;
|
||||
screen_height = height;
|
||||
}
|
||||
|
||||
static void sdl_cleanup() {
|
||||
ffmpeg_destroy();
|
||||
}
|
||||
|
||||
static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
if (window == NULL) {
|
||||
if(SDL_Init(SDL_INIT_VIDEO)) {
|
||||
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("Moonlight", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, 0);
|
||||
if(!window) {
|
||||
fprintf(stderr, "SDL: could not create window - exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_TARGETTEXTURE);
|
||||
if (!renderer) {
|
||||
fprintf(stderr, "SDL: could not create renderer - exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bmp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_TARGET, screen_width, screen_height);
|
||||
if (!bmp) {
|
||||
fprintf(stderr, "SDL: could not create texture - exiting\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
int length = 0;
|
||||
while (entry != NULL) {
|
||||
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
||||
length += entry->length;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
int ret = ffmpeg_decode(ffmpeg_buffer, length);
|
||||
if (ret == 1) {
|
||||
AVFrame* frame = ffmpeg_get_frame();
|
||||
|
||||
SDL_UpdateYUVTexture(bmp, NULL, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, bmp, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Video decode buffer too small");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return DR_OK;
|
||||
}
|
||||
|
||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = {
|
||||
.setup = sdl_setup,
|
||||
.cleanup = sdl_cleanup,
|
||||
.submitDecodeUnit = sdl_submit_decode_unit,
|
||||
};
|
||||
Reference in New Issue
Block a user