From 10edbfdb204584d7d863ba801986fea9921929ab Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 5 Jun 2017 00:12:06 +0200 Subject: [PATCH] Use VDPAU video output in X11 --- src/video/ffmpeg.c | 19 ++++++++-------- src/video/ffmpeg.h | 9 ++++++-- src/video/ffmpeg_vdpau.c | 48 +++++++++++++++++++++++++++++++++------- src/video/ffmpeg_vdpau.h | 5 ++++- src/video/sdl.c | 4 ++-- src/video/x11.c | 26 ++++++++++++++-------- 6 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 7e0d9b9..653676a 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -40,14 +40,13 @@ static AVFrame** dec_frames; static int dec_frames_cnt; static int current_frame, next_frame; -enum decoders {SOFTWARE, VDPAU}; -enum decoders decoder_system; +enum decoders ffmpeg_decoder; #define BYTES_PER_PIXEL 4 // This function must be called before // any other decoding functions -int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count) { +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count, void* context) { // Initialize the avcodec library and register codecs av_log_set_level(AV_LOG_QUIET); avcodec_register_all(); @@ -66,12 +65,12 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer } if (decoder != NULL) - decoder_system = VDPAU; + ffmpeg_decoder = VDPAU; } #endif if (decoder == NULL) { - decoder_system = SOFTWARE; + ffmpeg_decoder = SOFTWARE; switch (videoFormat) { case VIDEO_FORMAT_H264: decoder = avcodec_find_decoder_by_name("h264"); @@ -133,8 +132,8 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer } #ifdef HAVE_VDPAU - if (decoder_system == VDPAU) - vdpau_init(decoder_ctx, width, height); + if (ffmpeg_decoder == VDPAU) + vdpau_init(decoder_ctx, (Display*) context, width, height); #endif return 0; @@ -156,16 +155,16 @@ void ffmpeg_destroy(void) { } } -AVFrame* ffmpeg_get_frame() { +AVFrame* ffmpeg_get_frame(bool native_frame) { int err = avcodec_receive_frame(decoder_ctx, dec_frames[next_frame]); if (err == 0) { current_frame = next_frame; next_frame = (current_frame+1) % dec_frames_cnt; - if (decoder_system == SOFTWARE) + if (ffmpeg_decoder == SOFTWARE || native_frame) return dec_frames[current_frame]; #ifdef HAVE_VDPAU - else if (decoder_system == VDPAU) + else if (ffmpeg_decoder == VDPAU) return vdpau_get_frame(dec_frames[current_frame]); #endif } else if (err != AVERROR(EAGAIN)) { diff --git a/src/video/ffmpeg.h b/src/video/ffmpeg.h index e65f585..d5449b4 100644 --- a/src/video/ffmpeg.h +++ b/src/video/ffmpeg.h @@ -17,6 +17,8 @@ * along with Moonlight; if not, see . */ +#include + #include // Disables the deblocking filter at the cost of image quality @@ -34,9 +36,12 @@ // Uses hardware acceleration #define HARDWARE_ACCELERATION 0x40 -int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count); +enum decoders {SOFTWARE, VDPAU}; +extern enum decoders ffmpeg_decoder; + +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count, void* context); void ffmpeg_destroy(void); int ffmpeg_draw_frame(AVFrame *pict); -AVFrame* ffmpeg_get_frame(); +AVFrame* ffmpeg_get_frame(bool native_frame); int ffmpeg_decode(unsigned char* indata, int inlen); diff --git a/src/video/ffmpeg_vdpau.c b/src/video/ffmpeg_vdpau.c index ae2b119..eddd024 100644 --- a/src/video/ffmpeg_vdpau.c +++ b/src/video/ffmpeg_vdpau.c @@ -34,6 +34,9 @@ static AVFrame* cpu_frame; static VdpDevice vdp_device; static VdpDecoder vdp_decoder; static VdpVideoMixer vdp_mixer; +static VdpPresentationQueue vdp_queue; +static VdpPresentationQueueTarget vdp_queue_target; +static VdpOutputSurface vdp_output; static struct vdpau_render_state* vdp_render_state[MAX_RENDER_STATES]; static int vdp_render_states = 0; @@ -43,8 +46,13 @@ 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; +static VdpVideoMixerRender* vdp_video_mixer_render; +static VdpOutputSurfaceCreate* vdp_output_surface_create; +static VdpPresentationQueueCreate* vdp_presentation_queue_create; +static VdpPresentationQueueDisplay* vdp_presentation_queue_display; +static VdpPresentationQueueTargetCreateX11* vdp_presentation_queue_target_create_x11; -struct vdpau_render_state* vdp_get_free_render_state() { +struct vdpau_render_state* vdp_get_free_render_state(int width, int height) { for (unsigned i = 0; i < vdp_render_states; i++) { struct vdpau_render_state* render_state = vdp_render_state[i]; if (!render_state->state) @@ -60,7 +68,7 @@ struct vdpau_render_state* vdp_get_free_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); + VdpStatus status = vdp_video_surface_create(vdp_device, VDP_CHROMA_TYPE_420, width, height, &render_state->surface); return render_state; } @@ -70,7 +78,7 @@ static void vdp_release_buffer(void* opaque, uint8_t *data) { } static int vdp_get_buffer(AVCodecContext* context, AVFrame* frame, int flags) { - struct vdpau_render_state* pRenderState = vdp_get_free_render_state(); + struct vdpau_render_state* pRenderState = vdp_get_free_render_state(frame->width, frame->height); frame->data[0] = (uint8_t*) pRenderState; frame->buf[0] = av_buffer_create(frame->data[0], 0, vdp_release_buffer, NULL, 0); @@ -89,23 +97,27 @@ static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* f 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) { +int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height) { if (vdp_device) return vdp_device; - Display* xdisplay = XOpenDisplay(0); - if (!xdisplay) - return -1; + if (!display) + display = XOpenDisplay(NULL); - VdpStatus status = vdp_device_create_x11(xdisplay, DefaultScreen(xdisplay), &vdp_device, &vdp_get_proc_address); + VdpStatus status = vdp_device_create_x11(display, DefaultScreen(display), &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_OUTPUT_SURFACE_CREATE, (void**)&vdp_output_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); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_RENDER, (void**)&vdp_video_mixer_render); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, (void**)&vdp_presentation_queue_create); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11, (void**)&vdp_presentation_queue_target_create_x11); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, (void**)&vdp_presentation_queue_display); decoder_ctx->get_buffer2 = vdp_get_buffer; decoder_ctx->draw_horiz_band = vdp_draw_horiz_band; @@ -153,6 +165,8 @@ int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) { return -1; } + vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, width, height, &vdp_output); + return vdp_device; } @@ -172,3 +186,21 @@ AVFrame* vdpau_get_frame(AVFrame* dec_frame) { VdpStatus status = vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches); return cpu_frame; } + +int vdpau_init_queue(Drawable win) { + if(vdp_presentation_queue_target_create_x11(vdp_device, win, &vdp_queue_target) != VDP_STATUS_OK) + return -1; + + if(vdp_presentation_queue_create(vdp_device, vdp_queue_target, &vdp_queue) != VDP_STATUS_OK) + return -1; + + return 0; +} + +void vdpau_queue(AVFrame* dec_frame) { + struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0]; + int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; + vdp_video_mixer_render(vdp_mixer, VDP_INVALID_HANDLE, 0, field, 0, (VdpVideoSurface*)VDP_INVALID_HANDLE, render_state->surface, 0,(VdpVideoSurface*)VDP_INVALID_HANDLE, NULL, vdp_output, NULL, NULL, 0, NULL); + + vdp_presentation_queue_display(vdp_queue, vdp_output, 0, 0, 0); +} diff --git a/src/video/ffmpeg_vdpau.h b/src/video/ffmpeg_vdpau.h index 13f2b69..27d0554 100644 --- a/src/video/ffmpeg_vdpau.h +++ b/src/video/ffmpeg_vdpau.h @@ -17,7 +17,10 @@ * along with Moonlight; if not, see . */ +#include #include -int vdpau_init(AVCodecContext* decoder_ctx, int width, int height); +int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height); AVFrame* vdpau_get_frame(AVFrame* dec_frame); +int vdpau_init_queue(Drawable win); +void vdpau_queue(AVFrame* dec_frame); diff --git a/src/video/sdl.c b/src/video/sdl.c index 69f9a23..459f3a1 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -37,7 +37,7 @@ static int sdl_setup(int videoFormat, int width, int height, int redrawRate, voi if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; - if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) { + if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN), NULL) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); return -1; } @@ -68,7 +68,7 @@ static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) { ffmpeg_decode(ffmpeg_buffer, length); if (SDL_LockMutex(mutex) == 0) { - AVFrame* frame = ffmpeg_get_frame(); + AVFrame* frame = ffmpeg_get_frame(false); if (frame != NULL) { sdlNextFrame++; diff --git a/src/video/x11.c b/src/video/x11.c index 96e8f92..1aa5be3 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -20,6 +20,7 @@ #include "video.h" #include "egl.h" #include "ffmpeg.h" +#include "ffmpeg_vdpau.h" #include "../input/x11.h" #include "../loop.h" @@ -54,11 +55,6 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; - if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) { - fprintf(stderr, "Couldn't initialize video decoding\n"); - return -1; - } - ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); if (ffmpeg_buffer == NULL) { fprintf(stderr, "Not enough memory\n"); @@ -95,7 +91,16 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } - egl_init(display, window, width, height); + if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2, display) < 0) { + fprintf(stderr, "Couldn't initialize video decoding\n"); + return -1; + } + + if (ffmpeg_decoder == SOFTWARE) + egl_init(display, window, width, height); + else + vdpau_init_queue(window); + x11_input_init(display, window); if (pipe(pipefd) == -1) { @@ -123,10 +128,13 @@ int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { entry = entry->next; } ffmpeg_decode(ffmpeg_buffer, length); - AVFrame* frame = ffmpeg_get_frame(); + AVFrame* frame = ffmpeg_get_frame(true); if (frame != NULL) { - void* pointer = frame->data; - write(pipefd[1], &pointer, sizeof(void*)); + if (ffmpeg_decoder == SOFTWARE) { + void* pointer = frame->data; + write(pipefd[1], &pointer, sizeof(void*)); + } else + vdpau_queue(frame); } }