mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-06-17 06:11:36 +00:00
Add support for VDPAU video acceleration
This commit is contained in:
@@ -26,9 +26,16 @@ pkg_check_modules(SDL sdl2>=2.0.4)
|
|||||||
pkg_check_modules(AVCODEC libavcodec)
|
pkg_check_modules(AVCODEC libavcodec)
|
||||||
pkg_check_modules(AVUTIL libavutil)
|
pkg_check_modules(AVUTIL libavutil)
|
||||||
pkg_check_modules(SWSCALE libswscale)
|
pkg_check_modules(SWSCALE libswscale)
|
||||||
|
pkg_check_modules(XLIB x11-xcb)
|
||||||
|
pkg_check_modules(LIBVA vdpau)
|
||||||
|
|
||||||
if(AVCODEC_FOUND AND AVUTIL_FOUND AND SWSCALE_FOUND AND SDL_FOUND)
|
if(AVCODEC_FOUND AND AVUTIL_FOUND AND SWSCALE_FOUND AND SDL_FOUND)
|
||||||
set(SOFTWARE_FOUND TRUE)
|
set(SOFTWARE_FOUND TRUE)
|
||||||
|
if(XLIB_FOUND AND LIBVA_FOUND)
|
||||||
|
set(VDPAU_FOUND TRUE)
|
||||||
|
else()
|
||||||
|
set(VDPAU_FOUND FALSE)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
set(SOFTWARE_FOUND FALSE)
|
set(SOFTWARE_FOUND FALSE)
|
||||||
endif()
|
endif()
|
||||||
@@ -46,6 +53,10 @@ endif()
|
|||||||
if (SOFTWARE_FOUND)
|
if (SOFTWARE_FOUND)
|
||||||
list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c)
|
list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL)
|
||||||
|
if(VDPAU_FOUND)
|
||||||
|
list(APPEND SRC_LIST ./src/video/ffmpeg_vdpau.c)
|
||||||
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_VDPAU)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCHES Debug)
|
if (BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCHES Debug)
|
||||||
@@ -87,6 +98,10 @@ endif()
|
|||||||
if (SOFTWARE_FOUND)
|
if (SOFTWARE_FOUND)
|
||||||
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS} ${SWSCALE_INCLUDE_DIRS})
|
target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS} ${SWSCALE_INCLUDE_DIRS})
|
||||||
target_link_libraries(moonlight ${SDL_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES} ${SWSCALE_LIBRARIES})
|
target_link_libraries(moonlight ${SDL_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES} ${SWSCALE_LIBRARIES})
|
||||||
|
if(VDPAU_FOUND)
|
||||||
|
target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(moonlight ${XLIB_LIBRARIES} ${LIBVA_LIBRARIES})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${MOONLIGHT_DEFINITIONS})
|
set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${MOONLIGHT_DEFINITIONS})
|
||||||
|
|||||||
+35
-4
@@ -19,10 +19,15 @@
|
|||||||
|
|
||||||
#include "ffmpeg.h"
|
#include "ffmpeg.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_VDPAU
|
||||||
|
#include "ffmpeg_vdpau.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
// General decoder and renderer state
|
// General decoder and renderer state
|
||||||
static AVPacket pkt;
|
static AVPacket pkt;
|
||||||
@@ -31,6 +36,9 @@ static AVCodecContext* decoder_ctx;
|
|||||||
static AVFrame* dec_frame;
|
static AVFrame* dec_frame;
|
||||||
static struct SwsContext* scaler_ctx;
|
static struct SwsContext* scaler_ctx;
|
||||||
|
|
||||||
|
enum decoders {SOFTWARE, VDPAU};
|
||||||
|
enum decoders decoder_system;
|
||||||
|
|
||||||
#define BYTES_PER_PIXEL 4
|
#define BYTES_PER_PIXEL 4
|
||||||
|
|
||||||
// This function must be called before
|
// This function must be called before
|
||||||
@@ -42,11 +50,20 @@ int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) {
|
|||||||
|
|
||||||
av_init_packet(&pkt);
|
av_init_packet(&pkt);
|
||||||
|
|
||||||
decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
|
#ifdef HAVE_VDPAU
|
||||||
|
decoder = avcodec_find_decoder_by_name("h264_vdpau");
|
||||||
|
if (decoder != NULL)
|
||||||
|
decoder_system = VDPAU;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (decoder == NULL) {
|
if (decoder == NULL) {
|
||||||
printf("Couldn't find H264 decoder");
|
decoder_system = SOFTWARE;
|
||||||
|
decoder = avcodec_find_decoder_by_name("h264");
|
||||||
|
if (decoder == NULL) {
|
||||||
|
printf("Couldn't find decoder\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
decoder_ctx = avcodec_alloc_context3(decoder);
|
decoder_ctx = avcodec_alloc_context3(decoder);
|
||||||
if (decoder_ctx == NULL) {
|
if (decoder_ctx == NULL) {
|
||||||
@@ -85,6 +102,11 @@ int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_VDPAU
|
||||||
|
if (decoder_system == VDPAU)
|
||||||
|
vdpau_init(decoder_ctx, width, height);
|
||||||
|
#endif
|
||||||
|
|
||||||
int filtering;
|
int filtering;
|
||||||
if (perf_lvl & FAST_BILINEAR_FILTERING)
|
if (perf_lvl & FAST_BILINEAR_FILTERING)
|
||||||
filtering = SWS_FAST_BILINEAR;
|
filtering = SWS_FAST_BILINEAR;
|
||||||
@@ -93,11 +115,13 @@ int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) {
|
|||||||
else
|
else
|
||||||
filtering = SWS_BICUBIC;
|
filtering = SWS_BICUBIC;
|
||||||
|
|
||||||
|
if (decoder_system == SOFTWARE) {
|
||||||
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);
|
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) {
|
if (scaler_ctx == NULL) {
|
||||||
printf("Couldn't get scaler context");
|
printf("Couldn't get scaler context\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -132,7 +156,12 @@ int ffmpeg_draw_frame(AVFrame *pict) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AVFrame* ffmpeg_get_frame() {
|
AVFrame* ffmpeg_get_frame() {
|
||||||
|
if (decoder_system == SOFTWARE)
|
||||||
return dec_frame;
|
return dec_frame;
|
||||||
|
#ifdef HAVE_VDPAU
|
||||||
|
else if (decoder_system == VDPAU)
|
||||||
|
return vdpau_get_frame(dec_frame);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// packets must be decoded in order
|
// packets must be decoded in order
|
||||||
@@ -148,7 +177,9 @@ int ffmpeg_decode(unsigned char* indata, int inlen) {
|
|||||||
got_pic = 0;
|
got_pic = 0;
|
||||||
err = avcodec_decode_video2(decoder_ctx, dec_frame, &got_pic, &pkt);
|
err = avcodec_decode_video2(decoder_ctx, dec_frame, &got_pic, &pkt);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
fprintf(stderr, "Decode failed\n");
|
char errorstring[512];
|
||||||
|
av_strerror(err, errorstring, sizeof(errorstring));
|
||||||
|
fprintf(stderr, "Decode failed - %s\n", errorstring);
|
||||||
got_pic = 0;
|
got_pic = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Moonlight Embedded.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Iwan Timmer
|
||||||
|
* Copyright (C) 2003-2014 Ulrich von Zadow
|
||||||
|
*
|
||||||
|
* Based on Libavg VDPAU 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_vdpau.h"
|
||||||
|
|
||||||
|
#include <vdpau/vdpau.h>
|
||||||
|
#include <libavutil/pixfmt.h>
|
||||||
|
#include <libavutil/imgutils.h>
|
||||||
|
#include <libavcodec/vdpau.h>
|
||||||
|
|
||||||
|
#define MAX_RENDER_STATES 32
|
||||||
|
|
||||||
|
static AVFrame* cpu_frame;
|
||||||
|
|
||||||
|
static VdpDevice vdp_device;
|
||||||
|
static VdpDecoder vdp_decoder;
|
||||||
|
static VdpVideoMixer vdp_mixer;
|
||||||
|
static struct vdpau_render_state* vdp_render_state[MAX_RENDER_STATES];
|
||||||
|
static int vdp_render_states = 0;
|
||||||
|
|
||||||
|
static VdpGetProcAddress* vdp_get_proc_address;
|
||||||
|
static VdpDecoderCreate* vdp_decoder_create;
|
||||||
|
static VdpDecoderRender* vdp_decoder_render;
|
||||||
|
static VdpVideoSurfaceGetBitsYCbCr* vdp_video_surface_get_bits_y_cb_cr;
|
||||||
|
static VdpVideoSurfaceCreate* vdp_video_surface_create;
|
||||||
|
static VdpVideoMixerCreate* vdp_video_mixer_create;
|
||||||
|
|
||||||
|
struct vdpau_render_state* vdp_get_free_render_state() {
|
||||||
|
for (unsigned i = 0; i < vdp_render_states; i++) {
|
||||||
|
struct vdpau_render_state* render_state = vdp_render_state[i];
|
||||||
|
if (!render_state->state)
|
||||||
|
return vdp_render_state[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdp_render_states == MAX_RENDER_STATES)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// No free surfaces available
|
||||||
|
struct vdpau_render_state* render_state = malloc(sizeof(struct vdpau_render_state));
|
||||||
|
vdp_render_state[vdp_render_states] = render_state;
|
||||||
|
vdp_render_states++;
|
||||||
|
memset(render_state, 0, sizeof(struct vdpau_render_state));
|
||||||
|
render_state->surface = VDP_INVALID_HANDLE;
|
||||||
|
VdpStatus status = vdp_video_surface_create(vdp_device, VDP_CHROMA_TYPE_420, 1280, 720, &render_state->surface);
|
||||||
|
return render_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vdp_get_buffer(AVCodecContext* context, AVFrame* frame) {
|
||||||
|
struct vdpau_render_state* pRenderState = vdp_get_free_render_state();
|
||||||
|
frame->data[0] = (uint8_t*) pRenderState;
|
||||||
|
frame->type = FF_BUFFER_TYPE_USER;
|
||||||
|
|
||||||
|
pRenderState->state |= FF_VDPAU_STATE_USED_FOR_RENDER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vdp_release_buffer(AVCodecContext* context, AVFrame* frame) {
|
||||||
|
struct vdpau_render_state *render_state = (struct vdpau_render_state *)frame->data[0];
|
||||||
|
render_state->state = 0;
|
||||||
|
frame->data[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum AVPixelFormat vdp_get_format(AVCodecContext* context, const enum AVPixelFormat* pixel_format) {
|
||||||
|
return PIX_FMT_VDPAU_H264;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* frame, int offset[4], int y, int type, int height) {
|
||||||
|
struct vdpau_render_state* render_state = (struct vdpau_render_state*)frame->data[0];
|
||||||
|
|
||||||
|
VdpStatus status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers);
|
||||||
|
status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) {
|
||||||
|
if (vdp_device)
|
||||||
|
return vdp_device;
|
||||||
|
|
||||||
|
Display* xdisplay = XOpenDisplay(0);
|
||||||
|
if (!xdisplay)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
VdpStatus status = vdp_device_create_x11(xdisplay, DefaultScreen(xdisplay), &vdp_device, &vdp_get_proc_address);
|
||||||
|
if (status != VDP_STATUS_OK)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**)&vdp_video_surface_get_bits_y_cb_cr);
|
||||||
|
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**)&vdp_video_surface_create);
|
||||||
|
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_RENDER, (void**)&vdp_decoder_render);
|
||||||
|
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_CREATE, (void**)&vdp_decoder_create);
|
||||||
|
vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_CREATE, (void**)&vdp_video_mixer_create);
|
||||||
|
|
||||||
|
decoder_ctx->get_buffer = vdp_get_buffer;
|
||||||
|
decoder_ctx->release_buffer = vdp_release_buffer;
|
||||||
|
decoder_ctx->draw_horiz_band = vdp_draw_horiz_band;
|
||||||
|
decoder_ctx->get_format = vdp_get_format;
|
||||||
|
decoder_ctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
|
||||||
|
|
||||||
|
cpu_frame = av_frame_alloc();
|
||||||
|
if (cpu_frame == NULL) {
|
||||||
|
printf("Couldn't allocate frame\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cpu_frame->format = PIX_FMT_YUV420P;
|
||||||
|
cpu_frame->width = width;
|
||||||
|
cpu_frame->height = height;
|
||||||
|
av_frame_get_buffer(cpu_frame, 32);
|
||||||
|
|
||||||
|
if (!vdp_device) {
|
||||||
|
printf("Can't get VDPAU device\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = vdp_decoder_create(vdp_device, VDP_DECODER_PROFILE_H264_HIGH, width, height, 16, &vdp_decoder);
|
||||||
|
if (status != VDP_STATUS_OK) {
|
||||||
|
printf("Can't create VDPAU decoder\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VdpVideoMixerFeature features[] = {
|
||||||
|
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL,
|
||||||
|
VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL,
|
||||||
|
};
|
||||||
|
VdpVideoMixerParameter params[] = {
|
||||||
|
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
|
||||||
|
VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
|
||||||
|
VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
|
||||||
|
VDP_VIDEO_MIXER_PARAMETER_LAYERS
|
||||||
|
};
|
||||||
|
VdpChromaType chroma = VDP_CHROMA_TYPE_420;
|
||||||
|
int numLayers = 0;
|
||||||
|
void const* paramValues[] = { &width, &height, &chroma, &numLayers };
|
||||||
|
|
||||||
|
status = vdp_video_mixer_create(vdp_device, 0, features, 4, params, paramValues, &vdp_mixer);
|
||||||
|
if (status != VDP_STATUS_OK) {
|
||||||
|
printf("Can't create VDPAU mixer\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vdp_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVFrame* vdpau_get_frame(AVFrame* dec_frame) {
|
||||||
|
struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0];
|
||||||
|
void *dest[3] = {
|
||||||
|
cpu_frame->data[0],
|
||||||
|
cpu_frame->data[2],
|
||||||
|
cpu_frame->data[1]
|
||||||
|
};
|
||||||
|
uint32_t pitches[3] = {
|
||||||
|
cpu_frame->linesize[0],
|
||||||
|
cpu_frame->linesize[2],
|
||||||
|
cpu_frame->linesize[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
VdpStatus status = vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches);
|
||||||
|
return cpu_frame;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
int vdpau_init(AVCodecContext* decoder_ctx, int width, int height);
|
||||||
|
AVFrame* vdpau_get_frame(AVFrame* dec_frame);
|
||||||
Reference in New Issue
Block a user