Merge branch 'pc'

This commit is contained in:
Iwan Timmer
2015-07-28 12:23:03 +02:00
15 changed files with 803 additions and 55 deletions

View File

@@ -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

View File

@@ -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
View 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,
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
View 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;
}
}

View File

@@ -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
View 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
View 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 */

View File

@@ -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
View 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
View 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
View 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,
};