diff --git a/CMakeLists.txt b/CMakeLists.txt index ed0ad30..4446fb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,20 +21,26 @@ pkg_check_modules(SDL sdl2>=2.0.4) pkg_check_modules(AVCODEC libavcodec) pkg_check_modules(AVUTIL libavutil) pkg_check_modules(XLIB x11) -pkg_check_modules(LIBVA vdpau) +pkg_check_modules(VDPAU vdpau) +pkg_check_modules(LIBVA libva) +pkg_check_modules(LIBVA_X11 libva-x11) pkg_check_modules(PULSE libpulse-simple) pkg_check_modules(CEC libcec>=3.0.0) pkg_check_modules(EGL egl) pkg_check_modules(GLES glesv2) -set(VDPAU_FOUND FALSE) +set(VDPAU_ACCEL_FOUND FALSE) +set(VA_ACCEL_FOUND FALSE) set(SOFTWARE_FOUND FALSE) if(AVCODEC_FOUND AND AVUTIL_FOUND) if(EGL_FOUND AND GLES_FOUND AND XLIB_FOUND) set(X11_FOUND TRUE) - if(XLIB_FOUND AND LIBVA_FOUND) - set(VDPAU_FOUND TRUE) + if(VDPAU_FOUND) + set(VDPAU_ACCEL_FOUND TRUE) + endif() + if (LIBVA_FOUND AND LIBVA_X11_FOUND) + set(VA_ACCEL_FOUND TRUE) endif() endif() if(SDL_FOUND OR X11_FOUND) @@ -61,11 +67,16 @@ if (SOFTWARE_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_X11) list(APPEND MOONLIGHT_OPTIONS X11) endif() - if(VDPAU_FOUND) + if(VDPAU_ACCEL_FOUND) list(APPEND SRC_LIST ./src/video/ffmpeg_vdpau.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_VDPAU) list(APPEND MOONLIGHT_OPTIONS VDPAU) endif() + if(VA_ACCEL_FOUND) + list(APPEND SRC_LIST ./src/video/ffmpeg_vaapi.c) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_VAAPI) + list(APPEND MOONLIGHT_OPTIONS VAAPI) + endif() endif() if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR X11_FOUND) @@ -147,9 +158,13 @@ endif() if (SOFTWARE_FOUND) target_include_directories(moonlight PRIVATE ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS}) target_link_libraries(moonlight ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES}) - if(VDPAU_FOUND) - target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS}) - target_link_libraries(moonlight ${XLIB_LIBRARIES} ${LIBVA_LIBRARIES}) + if(VDPAU_ACCEL_FOUND) + target_include_directories(moonlight PRIVATE ${VDPAU_INCLUDE_DIRS}) + target_link_libraries(moonlight ${VDPAU_LIBRARIES}) + endif() + if(VA_ACCEL_FOUND) + target_include_directories(moonlight PRIVATE ${LIBVA_INCLUDE_DIRS} ${LIBVA_X11_INCLUDE_DIRS}) + target_link_libraries(moonlight ${LIBVA_LIBRARIES} ${LIBVA_X11_LIBRARIES}) endif() endif() diff --git a/src/platform.c b/src/platform.c index 680c1ea..5ecaaeb 100644 --- a/src/platform.c +++ b/src/platform.c @@ -60,10 +60,17 @@ enum platform platform_check(char* name) { } #endif #ifdef HAVE_X11 - if (std || strcmp(name, "x11") == 0 || strcmp(name, "x11_vdpau") == 0) { - int x11 = x11_init(strcmp(name, "x11") != 0); + bool x11 = strcmp(name, "x11") == 0; + bool vdpau = strcmp(name, "x11_vdpau") == 0; + bool vaapi = strcmp(name, "x11_vaapi") == 0; + if (std || x11 || vdpau || vaapi) { + int init = x11_init(std || vdpau, std || vaapi); + #ifdef HAVE_VAAPI + if (init == INIT_VAAPI) + return X11_VAAPI; + #endif #ifdef HAVE_VDPAU - if (strcmp(name, "x11") != 0 && x11 == 0) + if (init == INIT_VDPAU) return X11_VDPAU; #endif return X11; @@ -182,6 +189,8 @@ char* platform_name(enum platform system) { return "AMLogic VPU"; case X11: return "X Window System (software decoding)"; + case X11_VAAPI: + return "X Window System (VAAPI)"; case X11_VDPAU: return "X Window System (VDPAU)"; case SDL: diff --git a/src/platform.h b/src/platform.h index 54db256..bc3b976 100644 --- a/src/platform.h +++ b/src/platform.h @@ -26,7 +26,7 @@ #define IS_EMBEDDED(SYSTEM) SYSTEM != SDL -enum platform { NONE, SDL, X11, X11_VDPAU, PI, IMX, AML, FAKE }; +enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, IMX, AML, FAKE }; enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 00e6dda..8fcf9f8 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -22,6 +22,9 @@ #ifdef HAVE_VDPAU #include "ffmpeg_vdpau.h" #endif +#ifdef HAVE_VAAPI +#include "ffmpeg_vaapi.h" +#endif #include @@ -53,8 +56,7 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer av_init_packet(&pkt); - #ifdef HAVE_VDPAU - if (perf_lvl & HARDWARE_ACCELERATION) { + if (perf_lvl & VDPAU_ACCELERATION) { switch (videoFormat) { case VIDEO_FORMAT_H264: decoder = avcodec_find_decoder_by_name("h264_vdpau"); @@ -64,13 +66,9 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer break; } - if (decoder != NULL) - ffmpeg_decoder = VDPAU; - } - #endif - - if (decoder == NULL) { - ffmpeg_decoder = SOFTWARE; + ffmpeg_decoder = VDPAU; + } else { + ffmpeg_decoder = perf_lvl & VAAPI_ACCELERATION ? VAAPI : SOFTWARE; switch (videoFormat) { case VIDEO_FORMAT_H264: decoder = avcodec_find_decoder_by_name("h264"); @@ -79,10 +77,11 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer decoder = avcodec_find_decoder_by_name("hevc"); break; } - if (decoder == NULL) { - printf("Couldn't find decoder\n"); - return -1; - } + } + + if (decoder == NULL) { + printf("Couldn't find decoder\n"); + return -1; } decoder_ctx = avcodec_alloc_context3(decoder); @@ -131,6 +130,11 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer } } + #ifdef HAVE_VAAPI + if (ffmpeg_decoder == VAAPI) + vaapi_init(decoder_ctx); + #endif + #ifdef HAVE_VDPAU if (ffmpeg_decoder == VDPAU) vdpau_init(decoder_ctx, width, height); diff --git a/src/video/ffmpeg.h b/src/video/ffmpeg.h index 9060e5e..44708f8 100644 --- a/src/video/ffmpeg.h +++ b/src/video/ffmpeg.h @@ -34,9 +34,10 @@ // Uses a faster bilinear filtering with lower image quality #define FAST_BILINEAR_FILTERING 0x20 // Uses hardware acceleration -#define HARDWARE_ACCELERATION 0x40 +#define VDPAU_ACCELERATION 0x40 +#define VAAPI_ACCELERATION 0x80 -enum decoders {SOFTWARE, VDPAU}; +enum decoders {SOFTWARE, VDPAU, VAAPI}; extern enum decoders ffmpeg_decoder; int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count); diff --git a/src/video/ffmpeg_vaapi.c b/src/video/ffmpeg_vaapi.c new file mode 100644 index 0000000..adf1082 --- /dev/null +++ b/src/video/ffmpeg_vaapi.c @@ -0,0 +1,75 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_SURFACES 16 + +static AVBufferRef* device_ref; + +static enum AVPixelFormat va_get_format(AVCodecContext* context, const enum AVPixelFormat* pixel_format) { + AVBufferRef* hw_ctx = av_hwframe_ctx_alloc(device_ref); + if (hw_ctx == NULL) { + fprintf(stderr, "Failed to initialize Vaapi buffer\n"); + return AV_PIX_FMT_NONE; + } + + AVHWFramesContext* fr_ctx = (AVHWFramesContext*) hw_ctx->data; + fr_ctx->format = AV_PIX_FMT_VAAPI; + fr_ctx->sw_format = AV_PIX_FMT_NV12; + fr_ctx->width = context->coded_width; + fr_ctx->height = context->coded_height; + fr_ctx->initial_pool_size = MAX_SURFACES + 1; + + if (av_hwframe_ctx_init(hw_ctx) < 0) { + fprintf(stderr, "Failed to initialize VAAPI frame context"); + return AV_PIX_FMT_NONE; + } + + context->pix_fmt = AV_PIX_FMT_VAAPI; + context->hw_device_ctx = device_ref; + context->hw_frames_ctx = hw_ctx; + context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; + return AV_PIX_FMT_VAAPI; +} + +static int va_get_buffer(AVCodecContext* context, AVFrame* frame, int flags) { + return av_hwframe_get_buffer(context->hw_frames_ctx, frame, 0); +} + +int vaapi_init_lib() { + return av_hwdevice_ctx_create(&device_ref, AV_HWDEVICE_TYPE_VAAPI, ":0", NULL, 0); +} + +int vaapi_init(AVCodecContext* decoder_ctx) { + decoder_ctx->get_format = va_get_format; + decoder_ctx->get_buffer2 = va_get_buffer; +} + +void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height) { + VASurfaceID surface = (VASurfaceID)(uintptr_t)dec_frame->data[3]; + AVHWDeviceContext* device = (AVHWDeviceContext*) device_ref->data; + AVVAAPIDeviceContext *va_ctx = device->hwctx; + vaPutSurface(va_ctx->display, surface, win, 0, 0, dec_frame->width, dec_frame->height, 0, 0, width, height, NULL, 0, 0); +} diff --git a/src/video/ffmpeg_vaapi.h b/src/video/ffmpeg_vaapi.h new file mode 100644 index 0000000..bb6de04 --- /dev/null +++ b/src/video/ffmpeg_vaapi.h @@ -0,0 +1,24 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include + +int vaapi_init_lib(); +int vaapi_init(AVCodecContext* decoder_ctx); +void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height); diff --git a/src/video/video.h b/src/video/video.h index e56aeec..306215c 100644 --- a/src/video/video.h +++ b/src/video/video.h @@ -22,11 +22,19 @@ #include #define DISPLAY_FULLSCREEN 1 -#define ENABLE_HARDWARE_ACCELERATION 2 +#define ENABLE_HARDWARE_ACCELERATION_1 2 +#define ENABLE_HARDWARE_ACCELERATION_2 4 + +#define INIT_EGL 1 +#define INIT_VDPAU 2 +#define INIT_VAAPI 3 #ifdef HAVE_X11 -int x11_init(bool vdpau); +int x11_init(bool vdpau, bool vaapi); extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; +#ifdef HAVE_VAAPI +extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vaapi; +#endif #ifdef HAVE_VDPAU extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau; #endif diff --git a/src/video/x11.c b/src/video/x11.c index 95de9ee..7d5ec7b 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -23,6 +23,9 @@ #ifdef HAVE_VDPAU #include "ffmpeg_vdpau.h" #endif +#ifdef HAVE_VAAPI +#include "ffmpeg_vaapi.h" +#endif #include "../input/x11.h" #include "../loop.h" @@ -36,13 +39,19 @@ #include #define DECODER_BUFFER_SIZE 92*1024 +#define X11_VDPAU_ACCELERATION ENABLE_HARDWARE_ACCELERATION_1 +#define X11_VAAPI_ACCELERATION ENABLE_HARDWARE_ACCELERATION_2 static char* ffmpeg_buffer = NULL; static Display *display = NULL; +static Window window; static int pipefd[2]; +static int display_width; +static int display_height; + static int frame_handle(int pipefd) { AVFrame* frame = NULL; while (read(pipefd, &frame, sizeof(void*)) > 0); @@ -52,18 +61,23 @@ static int frame_handle(int pipefd) { return LOOP_OK; } -int x11_init(bool vdpau) { +int x11_init(bool vdpau, bool vaapi) { XInitThreads(); display = XOpenDisplay(NULL); if (!display) - return -1; + return 0; - #ifdef HAVE_VDPAU - if (vdpau && vdpau_init_lib(display) != 0) - return -2; + #ifdef HAVE_VAAPI + if (vaapi && vaapi_init_lib(display) == 0) + return INIT_VAAPI; #endif - return 0; + #ifdef HAVE_VDPAU + if (vdpau && vdpau_init_lib(display) == 0) + return INIT_VDPAU; + #endif + + return INIT_EGL; } int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { @@ -78,8 +92,6 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont return -1; } - int display_width; - int display_height; if (drFlags & DISPLAY_FULLSCREEN) { Screen* screen = DefaultScreenOfDisplay(display); display_width = WidthOfScreen(screen); @@ -91,7 +103,7 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont Window root = DefaultRootWindow(display); XSetWindowAttributes winattr = { .event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask }; - Window window = XCreateWindow(display, root, 0, 0, display_width, display_height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr); + window = XCreateWindow(display, root, 0, 0, display_width, display_height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr); XMapWindow(display, window); XStoreName(display, window, "Moonlight"); @@ -113,10 +125,10 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont XFlush(display); int avc_flags = SLICE_THREADING; - #ifdef HAVE_VDPAU - if (drFlags & ENABLE_HARDWARE_ACCELERATION) - avc_flags |= HARDWARE_ACCELERATION; - #endif + if (drFlags & X11_VDPAU_ACCELERATION) + avc_flags |= VDPAU_ACCELERATION; + else if (drFlags & X11_VAAPI_ACCELERATION) + avc_flags |= VAAPI_ACCELERATION; if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); @@ -143,7 +155,11 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont } int x11_setup_vdpau(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - return x11_setup(videoFormat, width, height, redrawRate, context, drFlags | ENABLE_HARDWARE_ACCELERATION); + return x11_setup(videoFormat, width, height, redrawRate, context, drFlags | X11_VDPAU_ACCELERATION); +} + +int x11_setup_vaapi(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { + return x11_setup(videoFormat, width, height, redrawRate, context, drFlags | X11_VAAPI_ACCELERATION); } void x11_cleanup() { @@ -167,6 +183,8 @@ int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { write(pipefd[1], &frame, sizeof(void*)); else if (ffmpeg_decoder == VDPAU) vdpau_queue(frame); + else if (ffmpeg_decoder == VAAPI) + vaapi_queue(frame, window, display_width, display_height); } } @@ -186,3 +204,10 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = { .submitDecodeUnit = x11_submit_decode_unit, .capabilities = CAPABILITY_DIRECT_SUBMIT, }; + +DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vaapi = { + .setup = x11_setup_vaapi, + .cleanup = x11_cleanup, + .submitDecodeUnit = x11_submit_decode_unit, + .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT, +};