mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-06-17 06:11:36 +00:00
Merge branch 'pc'
This commit is contained in:
@@ -21,6 +21,10 @@ find_package(CEC)
|
|||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(EVDEV REQUIRED libevdev)
|
pkg_check_modules(EVDEV REQUIRED libevdev)
|
||||||
pkg_check_modules(UDEV REQUIRED libudev)
|
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)
|
if(CEC_FOUND)
|
||||||
include_directories(./third_party/libcec)
|
include_directories(./third_party/libcec)
|
||||||
@@ -46,6 +50,13 @@ if(FREESCALE_FOUND)
|
|||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
||||||
endif()
|
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_subdirectory(libgamestream)
|
||||||
|
|
||||||
add_executable(moonlight ${SRC_LIST})
|
add_executable(moonlight ${SRC_LIST})
|
||||||
@@ -69,6 +80,11 @@ if(FREESCALE_FOUND)
|
|||||||
target_link_libraries (moonlight PUBLIC ${FREESCALE_LIBRARIES})
|
target_link_libraries (moonlight PUBLIC ${FREESCALE_LIBRARIES})
|
||||||
endif()
|
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})
|
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_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})
|
target_link_libraries (moonlight PUBLIC ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRARY} ${UDEV_LIBRARIES} ${CMAKE_DL_LIBS})
|
||||||
|
|||||||
+4
-1
@@ -21,4 +21,7 @@
|
|||||||
|
|
||||||
extern const char* audio_device;
|
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/>.
|
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "audio.h"
|
#include "../audio.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <opus.h>
|
#include <opus.h>
|
||||||
@@ -35,7 +35,7 @@ static snd_pcm_t *handle;
|
|||||||
static OpusDecoder* decoder;
|
static OpusDecoder* decoder;
|
||||||
static short pcmBuffer[FRAME_SIZE * CHANNEL_COUNT];
|
static short pcmBuffer[FRAME_SIZE * CHANNEL_COUNT];
|
||||||
|
|
||||||
static void audio_renderer_init() {
|
static void alsa_renderer_init() {
|
||||||
int rc;
|
int rc;
|
||||||
decoder = opus_decoder_create(SAMPLE_RATE, CHANNEL_COUNT, &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));
|
CHECK_RETURN(snd_pcm_prepare(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_renderer_cleanup() {
|
static void alsa_renderer_cleanup() {
|
||||||
if (decoder != NULL)
|
if (decoder != NULL)
|
||||||
opus_decoder_destroy(decoder);
|
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);
|
int decodeLen = opus_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
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 = {
|
AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa = {
|
||||||
.init = audio_renderer_init,
|
.init = alsa_renderer_init,
|
||||||
.cleanup = audio_renderer_cleanup,
|
.cleanup = alsa_renderer_cleanup,
|
||||||
.decodeAndPlaySample = audio_renderer_decode_and_play_sample,
|
.decodeAndPlaySample = alsa_renderer_decode_and_play_sample,
|
||||||
};
|
};
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
+13
-1
@@ -49,6 +49,7 @@ static struct option long_options[] = {
|
|||||||
{"audio", required_argument, NULL, 'm'},
|
{"audio", required_argument, NULL, 'm'},
|
||||||
{"localaudio", no_argument, NULL, 'n'},
|
{"localaudio", no_argument, NULL, 'n'},
|
||||||
{"config", required_argument, NULL, 'o'},
|
{"config", required_argument, NULL, 'o'},
|
||||||
|
{"platform", required_argument, 0, 'p'},
|
||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,7 +127,13 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
|||||||
config->app = value;
|
config->app = value;
|
||||||
break;
|
break;
|
||||||
case 'j':
|
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;
|
inputAdded = true;
|
||||||
mapped = true;
|
mapped = true;
|
||||||
break;
|
break;
|
||||||
@@ -150,6 +157,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
|||||||
case 'o':
|
case 'o':
|
||||||
config_file_parse(value, config);
|
config_file_parse(value, config);
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
config->platform = optarg;
|
||||||
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (config->action == NULL)
|
if (config->action == NULL)
|
||||||
config->action = value;
|
config->action = value;
|
||||||
@@ -191,12 +201,14 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
|||||||
config->stream.bitrate = -1;
|
config->stream.bitrate = -1;
|
||||||
config->stream.packetSize = 1024;
|
config->stream.packetSize = 1024;
|
||||||
|
|
||||||
|
config->platform = "default";
|
||||||
config->app = "Steam";
|
config->app = "Steam";
|
||||||
config->action = NULL;
|
config->action = NULL;
|
||||||
config->address = NULL;
|
config->address = NULL;
|
||||||
config->sops = true;
|
config->sops = true;
|
||||||
config->localaudio = false;
|
config->localaudio = false;
|
||||||
|
|
||||||
|
config->inputsCount = 0;
|
||||||
config->mapping = get_path("mappings/default.conf");
|
config->mapping = get_path("mappings/default.conf");
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|||||||
@@ -21,14 +21,24 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define MAX_INPUTS 6
|
||||||
|
|
||||||
|
struct input_config {
|
||||||
|
char* path;
|
||||||
|
char* mapping;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct _CONFIGURATION {
|
typedef struct _CONFIGURATION {
|
||||||
STREAM_CONFIGURATION stream;
|
STREAM_CONFIGURATION stream;
|
||||||
char* app;
|
char* app;
|
||||||
char* action;
|
char* action;
|
||||||
char* address;
|
char* address;
|
||||||
char* mapping;
|
char* mapping;
|
||||||
|
char* platform;
|
||||||
bool sops;
|
bool sops;
|
||||||
bool localaudio;
|
bool localaudio;
|
||||||
|
struct input_config inputs[MAX_INPUTS];
|
||||||
|
int inputsCount;
|
||||||
} CONFIGURATION, *PCONFIGURATION;
|
} CONFIGURATION, *PCONFIGURATION;
|
||||||
|
|
||||||
bool inputAdded;
|
bool inputAdded;
|
||||||
|
|||||||
+32
-13
@@ -24,6 +24,8 @@
|
|||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "discover.h"
|
#include "discover.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "sdl.h"
|
||||||
|
|
||||||
#include "input/evdev.h"
|
#include "input/evdev.h"
|
||||||
#include "input/udev.h"
|
#include "input/udev.h"
|
||||||
@@ -72,7 +74,7 @@ static int get_app_id(PSERVER_DATA server, const char *name) {
|
|||||||
return -1;
|
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);
|
int appId = get_app_id(server, config->app);
|
||||||
if (appId<0) {
|
if (appId<0) {
|
||||||
fprintf(stderr, "Can't find app %s\n", config->app);
|
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);
|
gs_start_app(server, &config->stream, appId, config->sops, config->localaudio);
|
||||||
|
|
||||||
video_init();
|
LiStartConnection(server->address, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system), NULL, 0, server->serverMajorVersion);
|
||||||
evdev_init();
|
|
||||||
#ifdef HAVE_LIBCEC
|
|
||||||
cec_init();
|
|
||||||
#endif /* HAVE_LIBCEC */
|
|
||||||
|
|
||||||
LiStartConnection(server->address, &config->stream, &connection_callbacks, decoder_callbacks, &audio_callbacks, NULL, 0, server->serverMajorVersion);
|
if (IS_EMBEDDED(system))
|
||||||
|
|
||||||
evdev_start();
|
|
||||||
loop_main();
|
loop_main();
|
||||||
evdev_stop();
|
#ifdef HAVE_SDL
|
||||||
|
else if (system == SDL)
|
||||||
|
sdl_loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
LiStopConnection();
|
LiStopConnection();
|
||||||
}
|
}
|
||||||
@@ -138,12 +137,22 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (config.action == NULL || strcmp("help", config.action) == 0)
|
if (config.action == NULL || strcmp("help", config.action) == 0)
|
||||||
help();
|
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) {
|
if (config.address == NULL) {
|
||||||
perror("No filename for mapping");
|
perror("No filename for mapping");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
udev_init(!inputAdded, config.mapping);
|
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);
|
evdev_map(config.address);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -172,9 +181,19 @@ int main(int argc, char* argv[]) {
|
|||||||
pair_check(server);
|
pair_check(server);
|
||||||
applist(server);
|
applist(server);
|
||||||
} else if (strcmp("stream", config.action) == 0) {
|
} else if (strcmp("stream", config.action) == 0) {
|
||||||
udev_init(!inputAdded, config.mapping);
|
|
||||||
pair_check(server);
|
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) {
|
} else if (strcmp("pair", config.action) == 0) {
|
||||||
char pin[5];
|
char pin[5];
|
||||||
sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
|
sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
|
||||||
|
|||||||
@@ -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/>.
|
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include "video.h"
|
|
||||||
|
|
||||||
#include "limelight-common/Limelight.h"
|
#include "limelight-common/Limelight.h"
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.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() {
|
enum platform platform_check(char*);
|
||||||
#ifdef HAVE_FAKE
|
DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system);
|
||||||
decoder_callbacks = &decoder_callbacks_fake;
|
|
||||||
#endif
|
#ifdef HAVE_SDL
|
||||||
#ifdef HAVE_IMX
|
void sdl_loop();
|
||||||
if (dlsym(RTLD_DEFAULT, "vpu_Init") != NULL && video_imx_init()) {
|
#endif
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 */
|
||||||
@@ -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 */
|
||||||
+3
-6
@@ -19,13 +19,10 @@
|
|||||||
|
|
||||||
#include "limelight-common/Limelight.h"
|
#include "limelight-common/Limelight.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
extern DECODER_RENDERER_CALLBACKS *decoder_callbacks;
|
|
||||||
|
|
||||||
void video_init();
|
|
||||||
|
|
||||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;
|
||||||
|
#ifdef HAVE_SDL
|
||||||
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_OMX
|
#ifdef HAVE_OMX
|
||||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_omx;
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_omx;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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