diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | include/xine.h | 1 | ||||
-rw-r--r-- | include/xine/video_out.h | 1 | ||||
-rw-r--r-- | m4/summary.m4 | 1 | ||||
-rw-r--r-- | m4/video_out.m4 | 13 | ||||
-rw-r--r-- | src/combined/ffmpeg/ff_video_decoder.c | 318 | ||||
-rw-r--r-- | src/video_out/Makefile.am | 9 | ||||
-rw-r--r-- | src/video_out/video_out_vaapi.c | 4014 | ||||
-rw-r--r-- | src/xine-engine/accel_vaapi.h | 135 |
9 files changed, 4473 insertions, 20 deletions
@@ -1,4 +1,5 @@ xine-lib (1.2.3) ????-??-?? + * Add VAAPI HW decoding support * Add OpenGL 2.0 output plugin * Add crop support to XShm output plugins * Add color matrix and full range support to OpenGL/Xv/XShm output plugins diff --git a/include/xine.h b/include/xine.h index 073a9facd..b6112e97b 100644 --- a/include/xine.h +++ b/include/xine.h @@ -458,6 +458,7 @@ int xine_get_current_frame_data (xine_stream_t *stream, #define XINE_IMGFMT_XVMC (('C'<<24)|('M'<<16)|('v'<<8)|'X') #define XINE_IMGFMT_XXMC (('C'<<24)|('M'<<16)|('x'<<8)|'X') #define XINE_IMGFMT_VDPAU (('A'<<24)|('P'<<16)|('D'<<8)|'V') +#define XINE_IMGFMT_VAAPI (('P'<<24)|('A'<<16)|('A'<<8)|'V') /* get current xine's virtual presentation timestamp (1/90000 sec) * note: this is mostly internal data. diff --git a/include/xine/video_out.h b/include/xine/video_out.h index de126baea..fae62b3d1 100644 --- a/include/xine/video_out.h +++ b/include/xine/video_out.h @@ -310,6 +310,7 @@ struct xine_video_port_s { #define VO_CAP_VDPAU_MPEG12 0x00000100 /* driver can use VDPAU for mpeg1/2 */ #define VO_CAP_VDPAU_VC1 0x00000200 /* driver can use VDPAU for VC1 */ #define VO_CAP_VDPAU_MPEG4 0x00000400 /* driver can use VDPAU for mpeg4-part2 */ +#define VO_CAP_VAAPI 0x00000800 /* driver can use VAAPI */ #define VO_CAP_COLOR_MATRIX 0x00004000 /* driver can use alternative yuv->rgb matrices */ #define VO_CAP_FULLRANGE 0x00008000 /* driver handles fullrange yuv */ #define VO_CAP_HUE 0x00010000 diff --git a/m4/summary.m4 b/m4/summary.m4 index 15fccdbf4..8e084e635 100644 --- a/m4/summary.m4 +++ b/m4/summary.m4 @@ -211,6 +211,7 @@ AC_DEFUN([XINE_LIB_SUMMARY], [ if test x"$have_opengl2" = x"yes"; then echo " - OpenGL 2.0 (with bicubic scaling)" fi + test x"$have_vaapi" = x"yes" && echo " - vaapi (Video Acceleration (VA) API for Linux)" test x"$have_vdpau" = x"yes" && echo " - vdpau (X11 Video Decode and Presentation API for Unix)" if test x"$have_sunfb" = x"yes"; then if test x"$have_sundga" = x"yes"; then diff --git a/m4/video_out.m4 b/m4/video_out.m4 index 17630758a..dcb502b0b 100644 --- a/m4/video_out.m4 +++ b/m4/video_out.m4 @@ -27,6 +27,7 @@ AC_DEFUN([XINE_VIDEO_OUT_PLUGINS], [ default_enable_xinerama=yes default_enable_xvmc=yes default_enable_vdpau=no + default_enable_vaapi=no default_with_caca=yes default_with_libstk=no @@ -56,6 +57,7 @@ AC_DEFUN([XINE_VIDEO_OUT_PLUGINS], [ default_enable_fb=yes default_enable_vidix=yes default_enable_vdpau=yes + default_enable_vaapi=yes enable_linux=yes ;; esac @@ -517,5 +519,14 @@ AC_DEFUN([XINE_VIDEO_OUT_PLUGINS], [ fi fi AM_CONDITIONAL([ENABLE_VDPAU], test x"$have_vdpau" = x"yes") - + + dnl VAAPI + XINE_ARG_ENABLE([vaapi], [Disable VAAPI output plugin]) + if test x"$no_x" != x"yes" && test x"$enable_vaapi" != x"no"; then + PKG_CHECK_MODULES([LIBVA], [libva], [have_vaapi=yes], [have_vaapi=no]) + AC_CHECK_HEADERS([va/va.h], , [have_vaapi=no]) + AC_CHECK_HEADERS([va/va_x11.h], , [have_vaapi=no]) + fi + AM_CONDITIONAL([ENABLE_VAAPI], test x"$have_vaapi" = x"yes") + ])dnl XINE_VIDEO_OUT_PLUGIN diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c index e231b4b2c..ef91961d6 100644 --- a/src/combined/ffmpeg/ff_video_decoder.c +++ b/src/combined/ffmpeg/ff_video_decoder.c @@ -50,6 +50,12 @@ # include <libpostproc/postprocess.h> #endif +#ifdef HAVE_VA_VA_X11_H +#include <libavcodec/vaapi.h> +#include "accel_vaapi.h" +#define ENABLE_VAAPI 1 +#endif + #include "ffmpeg_compat.h" #define VIDEOBUFSIZE (128*1024) @@ -70,6 +76,9 @@ typedef struct ff_video_class_s { int8_t choose_speed_over_accuracy; uint8_t enable_dri; + uint8_t enable_vaapi; + uint8_t vaapi_mpeg_softdec; + xine_t *xine; } ff_video_class_t; @@ -140,6 +149,12 @@ struct ff_video_decoder_s { #endif uint8_t set_stream_info; + +#ifdef ENABLE_VAAPI + struct vaapi_context vaapi_context; + vaapi_accel_t *accel; + vo_frame_t *accel_img; +#endif }; /* import color matrix names */ @@ -204,6 +219,7 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ vo_frame_t *img; int width = context->width; int height = context->height; + int guarded_render = 0; ff_check_colorspace (this); @@ -221,8 +237,70 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ avcodec_align_dimensions(context, &width, &height); - if (this->full2mpeg || (this->context->pix_fmt != PIX_FMT_YUV420P && - this->context->pix_fmt != PIX_FMT_YUVJ420P)) { +#ifdef ENABLE_VAAPI + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + + av_frame->opaque = NULL; + av_frame->data[0] = NULL; + av_frame->data[1] = NULL; + av_frame->data[2] = NULL; + av_frame->data[3] = NULL; + av_frame->type = FF_BUFFER_TYPE_USER; +#ifdef AVFRAMEAGE + av_frame->age = 1; +#endif + av_frame->reordered_opaque = context->reordered_opaque; + + if(!this->accel->guarded_render(this->accel_img)) { + img = this->stream->video_out->get_frame (this->stream->video_out, + width, + height, + this->aspect_ratio, + this->output_format, + VO_BOTH_FIELDS|this->frame_flags); + + av_frame->opaque = img; + xine_list_push_back(this->dr1_frames, av_frame); + + vaapi_accel_t *accel = (vaapi_accel_t*)img->accel_data; + ff_vaapi_surface_t *va_surface = accel->get_vaapi_surface(img); + + if(va_surface) { + av_frame->data[0] = (void *)va_surface;//(void *)(uintptr_t)va_surface->va_surface_id; + av_frame->data[3] = (void *)(uintptr_t)va_surface->va_surface_id; + } + } else { + ff_vaapi_surface_t *va_surface = this->accel->get_vaapi_surface(this->accel_img); + + if(va_surface) { + av_frame->data[0] = (void *)va_surface;//(void *)(uintptr_t)va_surface->va_surface_id; + av_frame->data[3] = (void *)(uintptr_t)va_surface->va_surface_id; + } + } + + lprintf("1: 0x%08x\n", av_frame->data[3]); + + av_frame->linesize[0] = 0; + av_frame->linesize[1] = 0; + av_frame->linesize[2] = 0; + av_frame->linesize[3] = 0; + + this->is_direct_rendering_disabled = 1; + + return 0; + } + + /* on vaapi out do not use direct rendeing */ + if(this->class->enable_vaapi) { + this->output_format = XINE_IMGFMT_YV12; + } + + if(this->accel) + guarded_render = this->accel->guarded_render(this->accel_img); +#endif /* ENABLE_VAAPI */ + + if ((this->full2mpeg || (this->context->pix_fmt != PIX_FMT_YUV420P && + this->context->pix_fmt != PIX_FMT_YUVJ420P)) || guarded_render) { if (!this->is_direct_rendering_disabled) { xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: unsupported frame format, DR1 disabled.\n")); @@ -293,6 +371,18 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ static void release_buffer(struct AVCodecContext *context, AVFrame *av_frame){ ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; +#ifdef ENABLE_VAAPI + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD ) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)av_frame->data[0]; + if(va_surface != NULL) { + this->accel->release_vaapi_surface(this->accel_img, va_surface); + lprintf("release_buffer: va_surface_id 0x%08x\n", (unsigned int)av_frame->data[3]); + } + } + } +#endif + if (av_frame->type == FF_BUFFER_TYPE_USER) { if ( av_frame->opaque ) { vo_frame_t *img = (vo_frame_t *)av_frame->opaque; @@ -339,6 +429,53 @@ static const int skip_loop_filter_enum_values[] = { AVDISCARD_ALL }; +#ifdef ENABLE_VAAPI +static enum PixelFormat get_format(struct AVCodecContext *context, const enum PixelFormat *fmt) +{ + int i, profile; + ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; + + if(!this->class->enable_vaapi || !this->accel_img) + return PIX_FMT_YUV420P; + + vaapi_accel_t *accel = (vaapi_accel_t*)this->accel_img->accel_data; + + for (i = 0; fmt[i] != PIX_FMT_NONE; i++) { + if (fmt[i] != PIX_FMT_VAAPI_VLD) + continue; + + profile = accel->profile_from_imgfmt(this->accel_img, fmt[i], context->codec_id, this->class->vaapi_mpeg_softdec); + + if (profile >= 0) { + VAStatus status; + + status = accel->vaapi_init(this->accel_img, profile, context->width, context->height, 0); + + if( status == VA_STATUS_SUCCESS ) { + ff_vaapi_context_t *va_context = accel->get_context(this->accel_img); + + if(!va_context) + return PIX_FMT_YUV420P; + + context->draw_horiz_band = NULL; + context->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; + context->dsp_mask = 0; + + this->vaapi_context.config_id = va_context->va_config_id; + this->vaapi_context.context_id = va_context->va_context_id; + this->vaapi_context.display = va_context->va_display; + + context->hwaccel_context = &this->vaapi_context; + this->pts = 0; + + return fmt[i]; + } + } + } + return PIX_FMT_YUV420P; +} +#endif + static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) { size_t i; @@ -391,6 +528,40 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) } #endif + if(this->class->enable_vaapi) { + this->class->thread_count = this->context->thread_count = 1; + + this->context->skip_loop_filter = AVDISCARD_DEFAULT; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: force AVDISCARD_DEFAULT for VAAPI\n")); + } else { + this->context->skip_loop_filter = skip_loop_filter_enum_values[this->class->skip_loop_filter_enum]; + } + + /* enable direct rendering by default */ + this->output_format = XINE_IMGFMT_YV12; +#ifdef ENABLE_DIRECT_RENDERING + if( this->codec->capabilities & CODEC_CAP_DR1 && this->class->enable_dri ) { + this->context->get_buffer = get_buffer; + this->context->release_buffer = release_buffer; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: direct rendering enabled\n")); + } +#endif + +#ifdef ENABLE_VAAPI + if( this->class->enable_vaapi ) { + this->class->enable_dri = 1; + this->output_format = XINE_IMGFMT_VAAPI; + this->context->get_buffer = get_buffer; + this->context->reget_buffer = get_buffer; + this->context->release_buffer = release_buffer; + this->context->get_format = get_format; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_video_dec: direct rendering enabled\n")); + } +#endif /* ENABLE_VAAPI */ + pthread_mutex_lock(&ffmpeg_lock); if (avcodec_open (this->context, this->codec) < 0) { pthread_mutex_unlock(&ffmpeg_lock); @@ -426,8 +597,6 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) } #endif - this->context->skip_loop_filter = skip_loop_filter_enum_values[this->class->skip_loop_filter_enum]; - pthread_mutex_unlock(&ffmpeg_lock); lprintf("lavc decoder opened\n"); @@ -450,21 +619,11 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) this->skipframes = 0; - /* enable direct rendering by default */ - this->output_format = XINE_IMGFMT_YV12; -#ifdef ENABLE_DIRECT_RENDERING - if( this->codec->capabilities & CODEC_CAP_DR1 && this->class->enable_dri ) { - this->context->get_buffer = get_buffer; - this->context->release_buffer = release_buffer; - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_video_dec: direct rendering enabled\n")); - } -#endif - /* flag for interlaced streams */ this->frame_flags = 0; /* FIXME: which codecs can be interlaced? FIXME: check interlaced DCT and other codec specific info. */ + if(!this->class->enable_vaapi) { switch( codec_type ) { case BUF_VIDEO_DV: this->frame_flags |= VO_INTERLACED_FLAG; @@ -482,6 +641,7 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) this->frame_flags |= VO_INTERLACED_FLAG; break; } + } #ifdef AVCODEC_HAS_REORDERED_OPAQUE /* dont want initial AV_NOPTS_VALUE here */ @@ -489,6 +649,20 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type) #endif } +#ifdef ENABLE_VAAPI +static void vaapi_enable_vaapi(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->enable_vaapi = entry->num_value; +} + +static void vaapi_mpeg_softdec_func(void *user_data, xine_cfg_entry_t *entry) { + ff_video_class_t *class = (ff_video_class_t *) user_data; + + class->vaapi_mpeg_softdec = entry->num_value; +} +#endif /* ENABLE_VAAPI */ + static void choose_speed_over_accuracy_cb(void *user_data, xine_cfg_entry_t *entry) { ff_video_class_t *class = (ff_video_class_t *) user_data; @@ -1299,12 +1473,28 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu avpkt.data = (uint8_t *)this->mpeg_parser->chunk_buffer; avpkt.size = this->mpeg_parser->buffer_size; avpkt.flags = AV_PKT_FLAG_KEY; +# if ENABLE_VAAPI + if (this->accel) { + len = this->accel->avcodec_decode_video2 ( this->accel_img, this->context, this->av_frame, + &got_picture, &avpkt); + } else +# endif + { len = avcodec_decode_video2 (this->context, this->av_frame, &got_picture, &avpkt); + } #else +# if ENABLE_VAAPI + if(this->accel) { + len = this->accel->avcodec_decode_video ( this->accel_img, this->context, this->av_frame, + &got_picture, this->mpeg_parser->chunk_buffer, + this->mpeg_parser->buffer_size); + } else +# endif len = avcodec_decode_video (this->context, this->av_frame, &got_picture, this->mpeg_parser->chunk_buffer, this->mpeg_parser->buffer_size); + } #endif lprintf("avcodec_decode_video: decoded_size=%d, got_picture=%d\n", len, got_picture); @@ -1322,6 +1512,11 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu offset += len; } + if (got_picture && this->class->enable_vaapi) { + this->bih.biWidth = this->context->width; + this->bih.biHeight = this->context->height; + } + if( this->set_stream_info) { set_stream_info(this); this->set_stream_info = 0; @@ -1362,6 +1557,16 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu img->crop_right = this->crop_right; img->crop_bottom = this->crop_bottom; +#ifdef ENABLE_VAAPI + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)this->av_frame->data[0]; + this->accel->render_vaapi_surface(img, va_surface); + lprintf("handle_mpeg12_buffer: render_vaapi_surface va_surface_id 0x%08x\n", this->av_frame->data[0]); + } + } +#endif /* ENABLE_VAAPi */ + this->skipframes = img->draw(img, this->stream); if(free_img) @@ -1486,12 +1691,29 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { avpkt.data = (uint8_t *)&chunk_buf[offset]; avpkt.size = this->size; avpkt.flags = AV_PKT_FLAG_KEY; +# if ENABLE_VAAPI + if(this->accel) { + len = this->accel->avcodec_decode_video2 ( this->accel_img, this->context, this->av_frame, + &got_picture, &avpkt); + } else +# endif + { len = avcodec_decode_video2 (this->context, this->av_frame, &got_picture, &avpkt); + } #else +# if ENABLE_VAAPI + if(this->accel) { + len = this->accel->avcodec_decode_video ( this->accel_img, this->context, this->av_frame, + &got_picture, &chunk_buf[offset], + this->size); + } else +# endif + { len = avcodec_decode_video (this->context, this->av_frame, &got_picture, &chunk_buf[offset], this->size); + } #endif /* reset consumed pts value */ this->context->reordered_opaque = ff_tag_pts(this, 0); @@ -1555,7 +1777,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { /* indirect rendering */ /* initialize the colorspace converter */ - if (!this->cs_convert_init) { + if (!this->cs_convert_init && !this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { if ((this->context->pix_fmt == PIX_FMT_RGB32) || (this->context->pix_fmt == PIX_FMT_RGB565) || (this->context->pix_fmt == PIX_FMT_RGB555) || @@ -1591,10 +1813,10 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { } /* post processing */ - if(this->pp_quality != this->class->pp_quality) + if(this->pp_quality != this->class->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) pp_change_quality(this); - if(this->pp_available && this->pp_quality) { + if(this->pp_available && this->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) { if(this->av_frame->opaque) { /* DR1 */ @@ -1616,7 +1838,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { } else if (!this->av_frame->opaque) { /* colorspace conversion or copy */ - ff_convert_frame(this, img, this->av_frame); + if( this->context->pix_fmt != PIX_FMT_VAAPI_VLD) + ff_convert_frame(this, img, this->av_frame); } img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque); @@ -1645,6 +1868,17 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) { img->progressive_frame = !this->av_frame->interlaced_frame; img->top_field_first = this->av_frame->top_field_first; +#ifdef ENABLE_VAAPI + if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) { + if(this->accel->guarded_render(this->accel_img)) { + ff_vaapi_surface_t *va_surface = (ff_vaapi_surface_t *)this->av_frame->data[0]; + this->accel->render_vaapi_surface(img, va_surface); + if(va_surface) + lprintf("handle_buffer: render_vaapi_surface va_surface_id 0x%08x\n", this->av_frame->data[0]); + } + } +#endif /* ENABLE_VAAPI */ + this->skipframes = img->draw(img, this->stream); if(free_img) @@ -1868,6 +2102,11 @@ static void ff_dispose (video_decoder_t *this_gen) { xine_list_delete(this->dr1_frames); +#ifdef ENABLE_VAAPI + if(this->accel_img) + this->accel_img->free(this->accel_img); +#endif + free (this_gen); } @@ -1917,6 +2156,35 @@ static video_decoder_t *ff_video_open_plugin (video_decoder_class_t *class_gen, this->debug_fmt = -1; #endif +#ifdef ENABLE_VAAPI + memset(&this->vaapi_context, 0x0 ,sizeof(struct vaapi_context)); + + this->accel = NULL; + this->accel_img = NULL; +#endif + + +#ifdef ENABLE_VAAPI + if(this->class->enable_vaapi && (stream->video_driver->get_capabilities(stream->video_driver) & VO_CAP_VAAPI)) { + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: vaapi_mpeg_softdec %d\n"), + this->class->vaapi_mpeg_softdec ); + + this->accel_img = stream->video_out->get_frame( stream->video_out, 1920, 1080, 1, XINE_IMGFMT_VAAPI, VO_BOTH_FIELDS ); + + if( this->accel_img ) { + this->accel = (vaapi_accel_t*)this->accel_img->accel_data; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled in config.\n")); + } else { + this->class->enable_vaapi = 0; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled disabled by driver.\n")); + } + } else { + this->class->enable_vaapi = 0; + this->class->vaapi_mpeg_softdec = 0; + xprintf(this->class->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: VAAPI Enabled disabled by driver.\n")); + } +#endif + return &this->video_decoder; } @@ -1982,6 +2250,18 @@ void *init_video_plugin (xine_t *xine, void *data) { "streams with lot of reference frames."), 10, dri_cb, this); +#ifdef ENABLE_VAAPI + this->vaapi_mpeg_softdec = xine->config->register_bool(config, "video.processing.vaapi_mpeg_softdec", 0, + _("VAAPI Mpeg2 softdecoding"), + _("If the machine freezes on mpeg2 decoding use mpeg2 software decoding."), + 10, vaapi_mpeg_softdec_func, this); + + this->enable_vaapi = xine->config->register_bool(config, "video.processing.ffmpeg_enable_vaapi", 1, + _("Enable VAAPI"), + _("Enable or disable usage of vaapi"), + 10, vaapi_enable_vaapi, this); +#endif /* ENABLE_VAAPI */ + return this; } diff --git a/src/video_out/Makefile.am b/src/video_out/Makefile.am index 18b2ef498..9a88b80d8 100644 --- a/src/video_out/Makefile.am +++ b/src/video_out/Makefile.am @@ -46,6 +46,10 @@ if ENABLE_VDPAU vdpau_module = xineplug_vo_out_vdpau.la endif +if ENABLE_VAAPI +vaapi_module = xineplug_vo_out_vaapi.la +endif + if ENABLE_XCB XCBOSD = xcbosd.c if ENABLE_XCBSHM @@ -121,6 +125,7 @@ xineplug_LTLIBRARIES = $(xshm_module) $(xv_module) $(xvmc_module) \ $(xcbshm_module) \ $(xcbxv_module) \ $(vdpau_module) \ + $(vaapi_module) \ xineplug_vo_out_raw.la \ xineplug_vo_out_none.la @@ -128,6 +133,10 @@ xineplug_vo_out_vdpau_la_SOURCES = video_out_vdpau.c xineplug_vo_out_vdpau_la_LIBADD = $(XINE_LIB) $(MLIB_LIBS) $(AVUTIL_LIBS) $(PTHREAD_LIBS) $(X_LIBS) $(LTLIBINTL) $(VDPAU_LIBS) -lm xineplug_vo_out_vdpau_la_CFLAGS = $(VISIBILITY_FLAG) $(MLIB_CFLAGS) $(X_CFLAGS) $(VDPAU_CFLAGS) $(AVUTIL_CFLAGS) -fno-strict-aliasing +xineplug_vo_out_vaapi_la_SOURCES = $(X11OSD) video_out_vaapi.c +xineplug_vo_out_vaapi_la_LIBADD = $(YUV_LIBS) $(XINE_LIB) $(OPENGL_LIBS) $(FFMPEG_LIBS) $(AVUTIL_LIBS) $(X_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL) -ldl -lGLU -lva-glx -lva-x11 -lva +xineplug_vo_out_vaapi_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) $(XV_CFLAGS) -fno-strict-aliasing + xineplug_vo_out_xcbshm_la_SOURCES = video_out_xcbshm.c $(XCBOSD) xineplug_vo_out_xcbshm_la_LIBADD = $(YUV_LIBS) $(PTHREAD_LIBS) $(XCB_LIBS) $(XCBSHM_LIBS) $(LTLIBINTL) xineplug_vo_out_xcbshm_la_CFLAGS = $(AM_CFLAGS) $(XCB_CFLAGS) $(XCBSHM_CFLAGS) $(AVUTIL_CFLAGS) -fno-strict-aliasing diff --git a/src/video_out/video_out_vaapi.c b/src/video_out/video_out_vaapi.c new file mode 100644 index 000000000..0e0dc3306 --- /dev/null +++ b/src/video_out/video_out_vaapi.c @@ -0,0 +1,4014 @@ +/* + * Copyright (C) 2000-2004, 2008 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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 2 of the License, or + * (at your option) any later version. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * video_out_vaapi.c, VAAPI video extension interface for xine + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <math.h> + +#include <sys/types.h> +#if defined(__FreeBSD__) +#include <machine/param.h> +#endif +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> +#include <time.h> +#include <unistd.h> +#include "yuv2rgb.h" + +#define LOG_MODULE "video_out_vaapi" +#define LOG_VERBOSE +/* +#define LOG +*/ +/* +#define DEBUG_SURFACE +*/ +#include "xine.h" +#include <xine/video_out.h> +#include <xine/xine_internal.h> +#include <xine/xineutils.h> +#include <xine/vo_scale.h> + +#include <GL/glu.h> +#include <GL/glx.h> +#include <GL/glext.h> +#include <GL/gl.h> +#include <dlfcn.h> + +#include <va/va_x11.h> +#include <va/va_glx.h> + +#include "accel_vaapi.h" + +#ifndef VA_SURFACE_ATTRIB_SETTABLE +#define vaCreateSurfaces(d, f, w, h, s, ns, a, na) \ + vaCreateSurfaces(d, w, h, f, ns, s) +#endif + +#define RENDER_SURFACES 50 +#define SOFT_SURFACES 3 +#define SW_WIDTH 1920 +#define SW_HEIGHT 1080 +#define STABLE_FRAME_COUNTER 4 +#define SW_CONTEXT_INIT_FORMAT -1 //VAProfileH264Main + +#if defined VA_SRC_BT601 && defined VA_SRC_BT709 +# define USE_VAAPI_COLORSPACE 1 +#else +# define USE_VAAPI_COLORSPACE 0 +#endif + +#define IMGFMT_VAAPI 0x56410000 /* 'VA'00 */ +#define IMGFMT_VAAPI_MASK 0xFFFF0000 +#define IMGFMT_IS_VAAPI(fmt) (((fmt) & IMGFMT_VAAPI_MASK) == IMGFMT_VAAPI) +#define IMGFMT_VAAPI_CODEC_MASK 0x000000F0 +#define IMGFMT_VAAPI_CODEC(fmt) ((fmt) & IMGFMT_VAAPI_CODEC_MASK) +#define IMGFMT_VAAPI_CODEC_MPEG2 (0x10) +#define IMGFMT_VAAPI_CODEC_MPEG4 (0x20) +#define IMGFMT_VAAPI_CODEC_H264 (0x30) +#define IMGFMT_VAAPI_CODEC_VC1 (0x40) +#define IMGFMT_VAAPI_MPEG2 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2) +#define IMGFMT_VAAPI_MPEG2_IDCT (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|1) +#define IMGFMT_VAAPI_MPEG2_MOCO (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG2|2) +#define IMGFMT_VAAPI_MPEG4 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4) +#define IMGFMT_VAAPI_H263 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_MPEG4|1) +#define IMGFMT_VAAPI_H264 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_H264) +#define IMGFMT_VAAPI_VC1 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1) +#define IMGFMT_VAAPI_WMV3 (IMGFMT_VAAPI|IMGFMT_VAAPI_CODEC_VC1|1) + +#define FOVY 60.0f +#define ASPECT 1.0f +#define Z_NEAR 0.1f +#define Z_FAR 100.0f +#define Z_CAMERA 0.869f + +#ifndef GLAPIENTRY +#ifdef APIENTRY +#define GLAPIENTRY APIENTRY +#else +#define GLAPIENTRY +#endif +#endif + +#if defined(__linux__) +// Linux select() changes its timeout parameter upon return to contain +// the remaining time. Most other unixen leave it unchanged or undefined. +#define SELECT_SETS_REMAINING +#elif defined(__FreeBSD__) || defined(__sun__) || (defined(__MACH__) && defined(__APPLE__)) +#define USE_NANOSLEEP +#elif defined(HAVE_PTHREADS) && defined(sgi) +// SGI pthreads has a bug when using pthreads+signals+nanosleep, +// so instead of using nanosleep, wait on a CV which is never signalled. +#include <pthread.h> +#define USE_COND_TIMEDWAIT +#endif + +#define LOCKDISPLAY + +#ifdef LOCKDISPLAY +#define DO_LOCKDISPLAY XLockDisplay(guarded_display) +#define DO_UNLOCKDISPLAY XUnlockDisplay(guarded_display) +static Display *guarded_display; +#else +#define DO_LOCKDISPLAY +#define DO_UNLOCKDISPLAY +#endif + +#define RECT_IS_EQ(a, b) ((a).x1 == (b).x1 && (a).y1 == (b).y1 && (a).x2 == (b).x2 && (a).y2 == (b).y2) + +static const char *const scaling_level_enum_names[] = { + "default", /* VA_FILTER_SCALING_DEFAULT */ + "fast", /* VA_FILTER_SCALING_FAST */ + "hq", /* VA_FILTER_SCALING_HQ */ + "nla", /* VA_FILTER_SCALING_NL_ANAMORPHIC */ + NULL +}; + +static const int scaling_level_enum_values[] = { + VA_FILTER_SCALING_DEFAULT, + VA_FILTER_SCALING_FAST, + VA_FILTER_SCALING_HQ, + VA_FILTER_SCALING_NL_ANAMORPHIC +}; + +typedef struct vaapi_driver_s vaapi_driver_t; + +typedef struct { + int x0, y0; + int x1, y1, x2, y2; +} vaapi_rect_t; + +typedef struct { + vo_frame_t vo_frame; + + int width, height, format, flags; + double ratio; + + vaapi_accel_t vaapi_accel_data; +} vaapi_frame_t; + +typedef struct { + VADisplayAttribType type; + int value; + int min; + int max; + int atom; + + cfg_entry_t *entry; + + vaapi_driver_t *this; + +} va_property_t; + +struct vaapi_driver_s { + + vo_driver_t vo_driver; + + config_values_t *config; + + /* X11 related stuff */ + Display *display; + int screen; + Drawable drawable; + XColor black; + Window window; + + uint32_t capabilities; + + int ovl_changed; + vo_overlay_t *overlays[XINE_VORAW_MAX_OVL]; + uint32_t *overlay_bitmap; + int overlay_bitmap_size; + uint32_t overlay_bitmap_width; + uint32_t overlay_bitmap_height; + vaapi_rect_t overlay_bitmap_src; + vaapi_rect_t overlay_bitmap_dst; + + uint32_t vdr_osd_width; + uint32_t vdr_osd_height; + + uint32_t overlay_output_width; + uint32_t overlay_output_height; + vaapi_rect_t overlay_dirty_rect; + int has_overlay; + + uint32_t overlay_unscaled_width; + uint32_t overlay_unscaled_height; + vaapi_rect_t overlay_unscaled_dirty_rect; + + yuv2rgb_factory_t *yuv2rgb_factory; + yuv2rgb_t *ovl_yuv2rgb; + + /* all scaling information goes here */ + vo_scale_t sc; + + xine_t *xine; + + unsigned int deinterlace; + + int valid_opengl_context; + int opengl_render; + int opengl_use_tfp; + int query_va_status; + + GLuint gl_texture; + GLXContext gl_context; + XVisualInfo *gl_vinfo; + Pixmap gl_pixmap; + Pixmap gl_image_pixmap; + + ff_vaapi_context_t *va_context; + + int num_frame_buffers; + vaapi_frame_t *frames[RENDER_SURFACES]; + + pthread_mutex_t vaapi_lock; + + unsigned int init_opengl_render; + unsigned int guarded_render; + unsigned int scaling_level_enum; + unsigned int scaling_level; + va_property_t props[VO_NUM_PROPERTIES]; + unsigned int swap_uv_planes; +}; + +ff_vaapi_surface_t *va_render_surfaces = NULL; +VASurfaceID *va_surface_ids = NULL; +VASurfaceID *va_soft_surface_ids = NULL; +VAImage *va_soft_images = NULL; + +static void vaapi_destroy_subpicture(vo_driver_t *this_gen); +static void vaapi_destroy_image(vo_driver_t *this_gen, VAImage *va_image); +static int vaapi_ovl_associate(vo_driver_t *this_gen, int format, int bShow); +static VAStatus vaapi_destroy_soft_surfaces(vo_driver_t *this_gen); +static VAStatus vaapi_destroy_render_surfaces(vo_driver_t *this_gen); +static const char *vaapi_profile_to_string(VAProfile profile); +static int vaapi_set_property (vo_driver_t *this_gen, int property, int value); +static void vaapi_show_display_props(vo_driver_t *this_gen); + +static void nv12_to_yv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *uv_src, int uv_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *u_dst, int u_dst_pitch, + uint8_t *v_dst, int v_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int src_data_size); + +static void yv12_to_nv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *u_src, int u_src_pitch, + const uint8_t *v_src, int v_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size); + +void (GLAPIENTRY *mpglGenTextures)(GLsizei, GLuint *); +void (GLAPIENTRY *mpglBindTexture)(GLenum, GLuint); +void (GLAPIENTRY *mpglXBindTexImage)(Display *, GLXDrawable, int, const int *); +void (GLAPIENTRY *mpglXReleaseTexImage)(Display *, GLXDrawable, int); +GLXPixmap (GLAPIENTRY *mpglXCreatePixmap)(Display *, GLXFBConfig, Pixmap, const int *); +void (GLAPIENTRY *mpglXDestroyPixmap)(Display *, GLXPixmap); +const GLubyte *(GLAPIENTRY *mpglGetString)(GLenum); +void (GLAPIENTRY *mpglGenPrograms)(GLsizei, GLuint *); + +#ifdef LOG +static const char *string_of_VAImageFormat(VAImageFormat *imgfmt) +{ + static char str[5]; + str[0] = imgfmt->fourcc; + str[1] = imgfmt->fourcc >> 8; + str[2] = imgfmt->fourcc >> 16; + str[3] = imgfmt->fourcc >> 24; + str[4] = '\0'; + return str; +} +#endif + +static int vaapi_check_status(vo_driver_t *this_gen, VAStatus vaStatus, const char *msg) +{ + + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if (vaStatus != VA_STATUS_SUCCESS) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " Error : %s: %s\n", msg, vaErrorStr(vaStatus)); + return 0; + } + return 1; +} + +/* Wrapper for ffmpeg avcodec_decode_video2 */ +#if AVVIDEO > 1 +static int guarded_avcodec_decode_video2(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, AVPacket *avpkt) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + int len = 0; + + + if(this->guarded_render) { + lprintf("guarded_avcodec_decode_video2 enter\n"); + pthread_mutex_lock(&this->vaapi_lock); + //DO_LOCKDISPLAY; + } + + len = avcodec_decode_video2 (avctx, picture, got_picture_ptr, avpkt); + + if(this->guarded_render) { + //DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + lprintf("guarded_avcodec_decode_video2 exit\n"); + } + + + return len; +} +#else +static int guarded_avcodec_decode_video(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + int len = 0; + + + if(this->guarded_render) { + lprintf("guarded_avcodec_decode_video enter\n"); + pthread_mutex_lock(&this->vaapi_lock); + //DO_LOCKDISPLAY; + } + + len = avcodec_decode_video (avctx, picture, got_picture_ptr, buf, buf_size); + + if(this->guarded_render) { + //DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + lprintf("guarded_avcodec_decode_video exit\n"); + } + + + return len; +} +#endif + +static int guarded_render(vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + return this->guarded_render; +} + +static ff_vaapi_surface_t *get_vaapi_surface(vo_frame_t *frame_gen) { + + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + ff_vaapi_surface_t *va_surface = NULL; + VAStatus vaStatus; + + lprintf("get_vaapi_surface\n"); + + if(!va_render_surfaces) + return NULL; + + if(this->guarded_render) { + /* Get next VAAPI surface marked as SURFACE_FREE */ + for(;;) { + int old_head = va_context->va_head; + va_context->va_head = (va_context->va_head + 1) % ((RENDER_SURFACES)); + + va_surface = &va_render_surfaces[old_head]; + + if( va_surface->status == SURFACE_FREE ) { + + VASurfaceStatus surf_status = 0; + + if(this->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface->va_surface_id, &surf_status); + vaapi_check_status(va_context->driver, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status == VASurfaceReady) { + + va_surface->status = SURFACE_ALOC; + +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface 0x%08x\n", va_surface->va_surface_id); +#endif + + return &va_render_surfaces[old_head]; + } else { +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface busy\n"); +#endif + } + } +#ifdef DEBUG_SURFACE + printf("get_vaapi_surface miss\n"); +#endif + } + } else { + va_surface = &va_render_surfaces[frame->vaapi_accel_data.index]; + } + + return va_surface; +} + +/* Set VAAPI surface status to render */ +static void render_vaapi_surface(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_accel_t *accel = (vaapi_accel_t*)frame_gen->accel_data; + + lprintf("render_vaapi_surface\n"); + + if(!this->guarded_render || !accel || !va_surface) + return; + + pthread_mutex_lock(&this->vaapi_lock); + //DO_LOCKDISPLAY; + + accel->index = va_surface->index; + + va_surface->status = SURFACE_RENDER; +#ifdef DEBUG_SURFACE + printf("render_vaapi_surface 0x%08x\n", va_surface->va_surface_id); +#endif + + //DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); +} + +/* Set VAAPI surface status to free */ +static void release_vaapi_surface(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + lprintf("release_vaapi_surface\n"); + + if(va_surface == NULL || !this->guarded_render) { + return; + } + + if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RENDER_RELEASE; + } else if (va_surface->status != SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface 0x%08x\n", va_surface->va_surface_id); +#endif + } +} + +static VADisplay vaapi_get_display(Display *display, int opengl_render) +{ + VADisplay ret; + + if(opengl_render) { + ret = vaGetDisplayGLX(display); + } else { + ret = vaGetDisplay(display); + } + + if(vaDisplayIsValid(ret)) + return ret; + else + return 0; +} + +typedef struct { + void *funcptr; + const char *extstr; + const char *funcnames[7]; + void *fallback; +} extfunc_desc_t; + +#define DEF_FUNC_DESC(name) {&mpgl##name, NULL, {"gl"#name, NULL}, gl ##name} +static const extfunc_desc_t extfuncs[] = { + DEF_FUNC_DESC(GenTextures), + + {&mpglBindTexture, NULL, {"glBindTexture", "glBindTextureARB", "glBindTextureEXT", NULL}}, + {&mpglXBindTexImage, "GLX_EXT_texture_from_pixmap", {"glXBindTexImageEXT", NULL}}, + {&mpglXReleaseTexImage, "GLX_EXT_texture_from_pixmap", {"glXReleaseTexImageEXT", NULL}}, + {&mpglXCreatePixmap, "GLX_EXT_texture_from_pixmap", {"glXCreatePixmap", NULL}}, + {&mpglXDestroyPixmap, "GLX_EXT_texture_from_pixmap", {"glXDestroyPixmap", NULL}}, + {&mpglGenPrograms, "_program", {"glGenProgramsARB", NULL}}, + {NULL} +}; + +typedef struct { + video_driver_class_t driver_class; + + config_values_t *config; + xine_t *xine; +} vaapi_class_t; + +static int gl_visual_attr[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GL_NONE +}; + +static void delay_usec(unsigned int usec) +{ + int was_error; + +#if defined(USE_NANOSLEEP) + struct timespec elapsed, tv; +#elif defined(USE_COND_TIMEDWAIT) + // Use a local mutex and cv, so threads remain independent + pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER; + struct timespec elapsed; + uint64_t future; +#else + struct timeval tv; +#ifndef SELECT_SETS_REMAINING + uint64_t then, now, elapsed; +#endif +#endif + + // Set the timeout interval - Linux only needs to do this once +#if defined(SELECT_SETS_REMAINING) + tv.tv_sec = 0; + tv.tv_usec = usec; +#elif defined(USE_NANOSLEEP) + elapsed.tv_sec = 0; + elapsed.tv_nsec = usec * 1000; +#elif defined(USE_COND_TIMEDWAIT) + future = get_ticks_usec() + usec; + elapsed.tv_sec = future / 1000000; + elapsed.tv_nsec = (future % 1000000) * 1000; +#else + then = get_ticks_usec(); +#endif + + do { + errno = 0; +#if defined(USE_NANOSLEEP) + tv.tv_sec = elapsed.tv_sec; + tv.tv_nsec = elapsed.tv_nsec; + was_error = nanosleep(&tv, &elapsed); +#elif defined(USE_COND_TIMEDWAIT) + was_error = pthread_mutex_lock(&delay_mutex); + was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed); + was_error = pthread_mutex_unlock(&delay_mutex); +#else +#ifndef SELECT_SETS_REMAINING + // Calculate the time interval left (in case of interrupt) + now = get_ticks_usec(); + elapsed = now - then; + then = now; + if (elapsed >= usec) + break; + usec -= elapsed; + tv.tv_sec = 0; + tv.tv_usec = usec; +#endif + was_error = select(0, NULL, NULL, NULL, &tv); +#endif + } while (was_error && (errno == EINTR)); +} + +static void vaapi_x11_wait_event(Display *dpy, Window w, int type) +{ + XEvent e; + while (!XCheckTypedWindowEvent(dpy, w, type, &e)) + delay_usec(10); +} + +/* X11 Error handler and error functions */ +static int vaapi_x11_error_code = 0; +static int (*vaapi_x11_old_error_handler)(Display *, XErrorEvent *); + +static int vaapi_x11_error_handler(Display *dpy, XErrorEvent *error) +{ + vaapi_x11_error_code = error->error_code; + return 0; +} + +static void vaapi_x11_trap_errors(void) +{ + vaapi_x11_error_code = 0; + vaapi_x11_old_error_handler = XSetErrorHandler(vaapi_x11_error_handler); +} + +static int vaapi_x11_untrap_errors(void) +{ + XSetErrorHandler(vaapi_x11_old_error_handler); + return vaapi_x11_error_code; +} + +static void vaapi_appendstr(char **dst, const char *str) +{ + int newsize; + char *newstr; + if (!str) + return; + newsize = strlen(*dst) + 1 + strlen(str) + 1; + newstr = realloc(*dst, newsize); + if (!newstr) + return; + *dst = newstr; + strcat(*dst, " "); + strcat(*dst, str); +} + +/* Return the address of a linked function */ +static void *vaapi_getdladdr (const char *s) { + void *ret = NULL; + void *handle = dlopen(NULL, RTLD_LAZY); + if (!handle) + return NULL; + ret = dlsym(handle, s); + dlclose(handle); + + return ret; +} + +/* Resolve opengl functions. */ +static void vaapi_get_functions(vo_driver_t *this_gen, void *(*getProcAddress)(const GLubyte *), + const char *ext2) { + const extfunc_desc_t *dsc; + const char *extensions; + char *allexts; + + if (!getProcAddress) + getProcAddress = (void *)vaapi_getdladdr; + + /* special case, we need glGetString before starting to find the other functions */ + mpglGetString = getProcAddress("glGetString"); + if (!mpglGetString) + mpglGetString = glGetString; + + extensions = (const char *)mpglGetString(GL_EXTENSIONS); + if (!extensions) extensions = ""; + if (!ext2) ext2 = ""; + allexts = malloc(strlen(extensions) + strlen(ext2) + 2); + strcpy(allexts, extensions); + strcat(allexts, " "); + strcat(allexts, ext2); + lprintf("vaapi_get_functions: OpenGL extensions string:\n%s\n", allexts); + for (dsc = extfuncs; dsc->funcptr; dsc++) { + void *ptr = NULL; + int i; + if (!dsc->extstr || strstr(allexts, dsc->extstr)) { + for (i = 0; !ptr && dsc->funcnames[i]; i++) + ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); + } + if (!ptr) + ptr = dsc->fallback; + *(void **)dsc->funcptr = ptr; + } + lprintf("\n"); + free(allexts); +} + +/* Check if opengl indirect/software rendering is used */ +static int vaapi_opengl_verify_direct (x11_visual_t *vis) { + Window root, win; + XVisualInfo *visinfo; + GLXContext ctx; + XSetWindowAttributes xattr; + int ret = 0; + + if (!vis || !vis->display || ! (root = RootWindow (vis->display, vis->screen))) { + lprintf ("vaapi_opengl_verify_direct: Don't have a root window to verify\n"); + return 0; + } + + if (! (visinfo = glXChooseVisual (vis->display, vis->screen, gl_visual_attr))) + return 0; + + if (! (ctx = glXCreateContext (vis->display, visinfo, NULL, 1))) + return 0; + + memset (&xattr, 0, sizeof (xattr)); + xattr.colormap = XCreateColormap(vis->display, root, visinfo->visual, AllocNone); + xattr.event_mask = StructureNotifyMask | ExposureMask; + + if ( (win = XCreateWindow (vis->display, root, 0, 0, 1, 1, 0, visinfo->depth, + InputOutput, visinfo->visual, + CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, + &xattr))) { + if (glXMakeCurrent (vis->display, win, ctx)) { + const char *renderer = (const char *) glGetString(GL_RENDERER); + if (glXIsDirect (vis->display, ctx) && + ! strstr (renderer, "Software") && + ! strstr (renderer, "Indirect")) + ret = 1; + glXMakeCurrent (vis->display, None, NULL); + } + XDestroyWindow (vis->display, win); + } + glXDestroyContext (vis->display, ctx); + XFreeColormap (vis->display, xattr.colormap); + + return ret; +} + +static int vaapi_glx_bind_texture(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + glEnable(GL_TEXTURE_2D); + mpglBindTexture(GL_TEXTURE_2D, this->gl_texture); + + if (this->opengl_use_tfp) { + vaapi_x11_trap_errors(); + mpglXBindTexImage(this->display, this->gl_pixmap, GLX_FRONT_LEFT_EXT, NULL); + XSync(this->display, False); + if (vaapi_x11_untrap_errors()) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_bind_texture : Update bind_tex_image failed\n"); + } + + return 0; +} + +static int vaapi_glx_unbind_texture(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if (this->opengl_use_tfp) { + vaapi_x11_trap_errors(); + mpglXReleaseTexImage(this->display, this->gl_pixmap, GLX_FRONT_LEFT_EXT); + if (vaapi_x11_untrap_errors()) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_unbind_texture : Failed to release?\n"); + } + + mpglBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + return 0; +} + +static void vaapi_glx_render_frame(vo_frame_t *frame_gen, int left, int top, int right, int bottom) +{ + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + int x1, x2, y1, y2; + float tx, ty; + + if (vaapi_glx_bind_texture(frame_gen->driver) < 0) + return; + + /* Calc texture/rectangle coords */ + x1 = this->sc.output_xoffset; + y1 = this->sc.output_yoffset; + x2 = x1 + this->sc.output_width; + y2 = y1 + this->sc.output_height; + tx = (float) frame->width / va_context->width; + ty = (float) frame->height / va_context->height; + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + /* Draw quad */ + glBegin (GL_QUADS); + + glTexCoord2f (tx, ty); glVertex2i (x2, y2); + glTexCoord2f (0, ty); glVertex2i (x1, y2); + glTexCoord2f (0, 0); glVertex2i (x1, y1); + glTexCoord2f (tx, 0); glVertex2i (x2, y1); + lprintf("render_frame left %d top %d right %d bottom %d\n", x1, y1, x2, y2); + + glEnd (); + + if (vaapi_glx_unbind_texture(frame_gen->driver) < 0) + return; +} + +static void vaapi_glx_flip_page(vo_frame_t *frame_gen, int left, int top, int right, int bottom) +{ + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + glClear(GL_COLOR_BUFFER_BIT); + + vaapi_glx_render_frame(frame_gen, left, top, right, bottom); + + //if (gl_finish) + // glFinish(); + + glXSwapBuffers(this->display, this->window); + +} + +static void destroy_glx(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if(!this->opengl_render || !va_context->valid_context) + return; + + //if (gl_finish) + // glFinish(); + + if(va_context->gl_surface) { + VAStatus vaStatus = vaDestroySurfaceGLX(va_context->va_display, va_context->gl_surface); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaceGLX()"); + va_context->gl_surface = NULL; + } + + if(this->gl_context) + glXMakeCurrent(this->display, None, NULL); + + if(this->gl_pixmap) { + vaapi_x11_trap_errors(); + mpglXDestroyPixmap(this->display, this->gl_pixmap); + XSync(this->display, False); + vaapi_x11_untrap_errors(); + this->gl_pixmap = None; + } + + if(this->gl_image_pixmap) { + XFreePixmap(this->display, this->gl_image_pixmap); + this->gl_image_pixmap = None; + } + + if(this->gl_texture) { + glDeleteTextures(1, &this->gl_texture); + this->gl_texture = GL_NONE; + } + + if(this->gl_context) { + glXDestroyContext(this->display, this->gl_context); + this->gl_context = 0; + } + + if(this->gl_vinfo) { + XFree(this->gl_vinfo); + this->gl_vinfo = NULL; + } + + this->valid_opengl_context = 0; +} + +static GLXFBConfig *get_fbconfig_for_depth(vo_driver_t *this_gen, int depth) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + GLXFBConfig *fbconfigs, *ret = NULL; + int n_elements, i, found; + int db, stencil, alpha, rgba, value; + + static GLXFBConfig *cached_config = NULL; + static int have_cached_config = 0; + + if (have_cached_config) + return cached_config; + + fbconfigs = glXGetFBConfigs(this->display, this->screen, &n_elements); + + db = SHRT_MAX; + stencil = SHRT_MAX; + rgba = 0; + + found = n_elements; + + for (i = 0; i < n_elements; i++) { + XVisualInfo *vi; + int visual_depth; + + vi = glXGetVisualFromFBConfig(this->display, fbconfigs[i]); + if (!vi) + continue; + + visual_depth = vi->depth; + XFree(vi); + + if (visual_depth != depth) + continue; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_ALPHA_SIZE, &alpha); + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_BUFFER_SIZE, &value); + if (value != depth && (value - alpha) != depth) + continue; + + value = 0; + if (depth == 32) { + glXGetFBConfigAttrib(this->display, fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); + if (value) + rgba = 1; + } + + if (!value) { + if (rgba) + continue; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], + GLX_BIND_TO_TEXTURE_RGB_EXT, &value); + if (!value) + continue; + } + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_DOUBLEBUFFER, &value); + if (value > db) + continue; + db = value; + + glXGetFBConfigAttrib(this->display, fbconfigs[i], GLX_STENCIL_SIZE, &value); + if (value > stencil) + continue; + stencil = value; + + found = i; + } + + if (found != n_elements) { + ret = malloc(sizeof(*ret)); + *ret = fbconfigs[found]; + } + + if (n_elements) + XFree(fbconfigs); + + have_cached_config = 1; + cached_config = ret; + return ret; +} + +static int vaapi_glx_config_tfp(vo_driver_t *this_gen, unsigned int width, unsigned int height) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + GLXFBConfig *fbconfig; + int attribs[7], i = 0; + const int depth = 24; + + if (!mpglXBindTexImage || !mpglXReleaseTexImage) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : No GLX texture-from-pixmap extension available\n"); + return 0; + } + + if (depth != 24 && depth != 32) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : color depth wrong.\n"); + return 0; + } + + this->gl_image_pixmap = XCreatePixmap(this->display, this->window, width, height, depth); + if (!this->gl_image_pixmap) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not create X11 pixmap\n"); + return 0; + } + + fbconfig = get_fbconfig_for_depth(this_gen, depth); + if (!fbconfig) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not find an FBConfig for 32-bit pixmap\n"); + return 0; + } + + attribs[i++] = GLX_TEXTURE_TARGET_EXT; + attribs[i++] = GLX_TEXTURE_2D_EXT; + attribs[i++] = GLX_TEXTURE_FORMAT_EXT; + if (depth == 24) + attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; + else if (depth == 32) + attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; + attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; + attribs[i++] = GL_FALSE; + attribs[i++] = None; + + vaapi_x11_trap_errors(); + this->gl_pixmap = mpglXCreatePixmap(this->display, *fbconfig, this->gl_image_pixmap, attribs); + XSync(this->display, False); + if (vaapi_x11_untrap_errors()) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_tfp : Could not create GLX pixmap\n"); + return 0; + } + + return 1; +} + +static int vaapi_glx_config_glx(vo_driver_t *this_gen, unsigned int width, unsigned int height) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + this->gl_vinfo = glXChooseVisual(this->display, this->screen, gl_visual_attr); + if(!this->gl_vinfo) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXChooseVisual\n"); + this->opengl_render = 0; + } + + glXMakeCurrent(this->display, None, NULL); + this->gl_context = glXCreateContext (this->display, this->gl_vinfo, NULL, True); + if (this->gl_context) { + if(!glXMakeCurrent (this->display, this->window, this->gl_context)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXMakeCurrent\n"); + goto error; + } + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : error glXCreateContext\n"); + goto error; + } + + void *(*getProcAddress)(const GLubyte *); + const char *(*glXExtStr)(Display *, int); + char *glxstr = strdup(" "); + + getProcAddress = vaapi_getdladdr("glXGetProcAddress"); + if (!getProcAddress) + getProcAddress = vaapi_getdladdr("glXGetProcAddressARB"); + glXExtStr = vaapi_getdladdr("glXQueryExtensionsString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, this->screen)); + glXExtStr = vaapi_getdladdr("glXGetClientString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, GLX_EXTENSIONS)); + glXExtStr = vaapi_getdladdr("glXGetServerString"); + if (glXExtStr) + vaapi_appendstr(&glxstr, glXExtStr(this->display, GLX_EXTENSIONS)); + + vaapi_get_functions(this_gen, getProcAddress, glxstr); + if (!mpglGenPrograms && mpglGetString && + getProcAddress && + strstr(mpglGetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : Broken glXGetProcAddress detected, trying workaround\n"); + vaapi_get_functions(this_gen, NULL, glxstr); + } + free(glxstr); + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glDrawBuffer(GL_BACK); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* Create TFP resources */ + if(this->opengl_use_tfp && vaapi_glx_config_tfp(this_gen, width, height)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : Using GLX texture-from-pixmap extension\n"); + } else { + this->opengl_use_tfp = 0; + } + + /* Create OpenGL texture */ + /* XXX: assume GL_ARB_texture_non_power_of_two is available */ + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &this->gl_texture); + mpglBindTexture(GL_TEXTURE_2D, this->gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (!this->opengl_use_tfp) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + mpglBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + if(!this->gl_texture) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_glx_config_glx : gl_texture NULL\n"); + goto error; + } + + if(!this->opengl_use_tfp) { + VAStatus vaStatus = vaCreateSurfaceGLX(va_context->va_display, GL_TEXTURE_2D, this->gl_texture, &va_context->gl_surface); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaceGLX()")) { + va_context->gl_surface = NULL; + goto error; + } + } else { + va_context->gl_surface = NULL; + } + + lprintf("vaapi_glx_config_glx : GL setup done\n"); + + this->valid_opengl_context = 1; + return 1; + +error: + destroy_glx(this_gen); + this->valid_opengl_context = 0; + return 0; +} + +static uint32_t vaapi_get_capabilities (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + return this->capabilities; +} + +static const struct { + int fmt; + enum PixelFormat pix_fmt; + enum CodecID codec_id; +} conversion_map[] = { + {IMGFMT_VAAPI_MPEG2, PIX_FMT_VAAPI_VLD, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG2_IDCT,PIX_FMT_VAAPI_IDCT, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG2_MOCO,PIX_FMT_VAAPI_MOCO, CODEC_ID_MPEG2VIDEO}, + {IMGFMT_VAAPI_MPEG4, PIX_FMT_VAAPI_VLD, CODEC_ID_MPEG4}, + {IMGFMT_VAAPI_H263, PIX_FMT_VAAPI_VLD, CODEC_ID_H263}, + {IMGFMT_VAAPI_H264, PIX_FMT_VAAPI_VLD, CODEC_ID_H264}, + {IMGFMT_VAAPI_WMV3, PIX_FMT_VAAPI_VLD, CODEC_ID_WMV3}, + {IMGFMT_VAAPI_VC1, PIX_FMT_VAAPI_VLD, CODEC_ID_VC1}, + {0, PIX_FMT_NONE} +}; + +static int vaapi_pixfmt2imgfmt(enum PixelFormat pix_fmt, int codec_id) +{ + int i; + int fmt; + for (i = 0; conversion_map[i].pix_fmt != PIX_FMT_NONE; i++) { + if (conversion_map[i].pix_fmt == pix_fmt && + (conversion_map[i].codec_id == 0 || + conversion_map[i].codec_id == codec_id)) { + break; + } + } + fmt = conversion_map[i].fmt; + return fmt; +} + +static int vaapi_has_profile(VAProfile *va_profiles, int va_num_profiles, VAProfile profile) +{ + if (va_profiles && va_num_profiles > 0) { + int i; + for (i = 0; i < va_num_profiles; i++) { + if (va_profiles[i] == profile) + return 1; + } + } + return 0; +} + +static int profile_from_imgfmt(vo_frame_t *frame_gen, enum PixelFormat pix_fmt, int codec_id, int vaapi_mpeg_sofdec) +{ + vo_driver_t *this_gen = (vo_driver_t *) frame_gen->driver; + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + int profile = -1; + int maj, min; + int i; + int va_num_profiles; + int max_profiles; + VAProfile *va_profiles = NULL; + int inited = 0; + + if(va_context->va_display == NULL) { + lprintf("profile_from_imgfmt vaInitialize\n"); + inited = 1; + va_context->va_display = vaapi_get_display(this->display, this->opengl_render); + if(!va_context->va_display) + goto out; + + vaStatus = vaInitialize(va_context->va_display, &maj, &min); + if(!vaapi_check_status(this_gen, vaStatus, "vaInitialize()")) + goto out; + + } + + max_profiles = vaMaxNumProfiles(va_context->va_display); + va_profiles = calloc(max_profiles, sizeof(*va_profiles)); + if (!va_profiles) + goto out; + + vaStatus = vaQueryConfigProfiles(va_context->va_display, va_profiles, &va_num_profiles); + if(!vaapi_check_status(this_gen, vaStatus, "vaQueryConfigProfiles()")) + goto out; + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " VAAPI Supported Profiles : "); + for (i = 0; i < va_num_profiles; i++) { + printf("%s ", vaapi_profile_to_string(va_profiles[i])); + } + printf("\n"); + + uint32_t format = vaapi_pixfmt2imgfmt(pix_fmt, codec_id); + + static const int mpeg2_profiles[] = { VAProfileMPEG2Main, VAProfileMPEG2Simple, -1 }; + static const int mpeg4_profiles[] = { VAProfileMPEG4Main, VAProfileMPEG4AdvancedSimple, VAProfileMPEG4Simple, -1 }; + static const int h264_profiles[] = { VAProfileH264High, VAProfileH264Main, VAProfileH264Baseline, -1 }; + static const int wmv3_profiles[] = { VAProfileVC1Main, VAProfileVC1Simple, -1 }; + static const int vc1_profiles[] = { VAProfileVC1Advanced, -1 }; + + const int *profiles = NULL; + switch (IMGFMT_VAAPI_CODEC(format)) + { + case IMGFMT_VAAPI_CODEC_MPEG2: + if(!vaapi_mpeg_sofdec) { + profiles = mpeg2_profiles; + } + break; + case IMGFMT_VAAPI_CODEC_MPEG4: + profiles = mpeg4_profiles; + break; + case IMGFMT_VAAPI_CODEC_H264: + profiles = h264_profiles; + break; + case IMGFMT_VAAPI_CODEC_VC1: + switch (format) { + case IMGFMT_VAAPI_WMV3: + profiles = wmv3_profiles; + break; + case IMGFMT_VAAPI_VC1: + profiles = vc1_profiles; + break; + } + break; + } + + if (profiles) { + int i; + for (i = 0; profiles[i] != -1; i++) { + if (vaapi_has_profile(va_profiles, va_num_profiles, profiles[i])) { + profile = profiles[i]; + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " VAAPI Profile %s supported by your hardware\n", vaapi_profile_to_string(profiles[i])); + break; + } + } + } + +out: + if(va_profiles) + free(va_profiles); + if(inited) { + vaStatus = vaTerminate(va_context->va_display); + vaapi_check_status(this_gen, vaStatus, "vaTerminate()"); + } + return profile; +} + + +static const char *vaapi_profile_to_string(VAProfile profile) +{ + switch(profile) { +#define PROFILE(profile) \ + case VAProfile##profile: return "VAProfile" #profile + PROFILE(MPEG2Simple); + PROFILE(MPEG2Main); + PROFILE(MPEG4Simple); + PROFILE(MPEG4AdvancedSimple); + PROFILE(MPEG4Main); + PROFILE(H264Baseline); + PROFILE(H264Main); + PROFILE(H264High); + PROFILE(VC1Simple); + PROFILE(VC1Main); + PROFILE(VC1Advanced); +#undef PROFILE + default: break; + } + return "<unknown>"; +} + +static const char *vaapi_entrypoint_to_string(VAEntrypoint entrypoint) +{ + switch(entrypoint) + { +#define ENTRYPOINT(entrypoint) \ + case VAEntrypoint##entrypoint: return "VAEntrypoint" #entrypoint + ENTRYPOINT(VLD); + ENTRYPOINT(IZZ); + ENTRYPOINT(IDCT); + ENTRYPOINT(MoComp); + ENTRYPOINT(Deblocking); +#undef ENTRYPOINT + default: break; + } + return "<unknown>"; +} + +/* Init subpicture */ +static void vaapi_init_subpicture(vaapi_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + va_context->va_subpic_width = 0; + va_context->va_subpic_height = 0; + va_context->va_subpic_id = VA_INVALID_ID; + va_context->va_subpic_image.image_id = VA_INVALID_ID; + + this->overlay_output_width = this->overlay_output_height = 0; + this->overlay_unscaled_width = this->overlay_unscaled_height = 0; + this->ovl_changed = 0; + this->has_overlay = 0; + this->overlay_bitmap = NULL; + this->overlay_bitmap_size = 0; + +} + +/* Init vaapi context */ +static void vaapi_init_va_context(vaapi_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + + va_context->va_config_id = VA_INVALID_ID; + va_context->va_context_id = VA_INVALID_ID; + va_context->va_profile = 0; + va_context->va_colorspace = 1; + va_context->is_bound = 0; + va_context->gl_surface = NULL; + va_context->soft_head = 0; + va_context->valid_context = 0; + va_context->va_head = 0; + va_context->va_soft_head = 0; + + for(i = 0; i < RENDER_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = VA_INVALID_SURFACE; + + va_surface_ids[i] = VA_INVALID_SURFACE; + } + + for(i = 0; i < SOFT_SURFACES; i++) { + va_soft_surface_ids[i] = VA_INVALID_SURFACE; + va_soft_images[i].image_id = VA_INVALID_ID; + } + + va_context->va_image_formats = NULL; + va_context->va_num_image_formats = 0; + + va_context->va_subpic_formats = NULL; + va_context->va_num_subpic_formats = 0; +} + +/* Close vaapi */ +static void vaapi_close(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context || !va_context->va_display || !va_context->valid_context) + return; + + vaapi_ovl_associate(this_gen, 0, 0); + + destroy_glx((vo_driver_t *)this); + + if(va_context->va_context_id != VA_INVALID_ID) { + vaStatus = vaDestroyContext(va_context->va_display, va_context->va_context_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyContext()"); + va_context->va_context_id = VA_INVALID_ID; + } + + vaapi_destroy_subpicture(this_gen); + vaapi_destroy_soft_surfaces(this_gen); + vaapi_destroy_render_surfaces(this_gen); + + if(va_context->va_config_id != VA_INVALID_ID) { + vaStatus = vaDestroyConfig(va_context->va_display, va_context->va_config_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyConfig()"); + va_context->va_config_id = VA_INVALID_ID; + } + + vaStatus = vaTerminate(va_context->va_display); + vaapi_check_status(this_gen, vaStatus, "vaTerminate()"); + va_context->va_display = NULL; + + if(va_context->va_image_formats) { + free(va_context->va_image_formats); + va_context->va_image_formats = NULL; + va_context->va_num_image_formats = 0; + } + if(va_context->va_subpic_formats) { + free(va_context->va_subpic_formats); + va_context->va_subpic_formats = NULL; + va_context->va_num_subpic_formats = 0; + } + + va_context->valid_context = 0; +} + +/* Returns internal VAAPI context */ +static ff_vaapi_context_t *get_context(vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) frame_gen->driver; + + return this->va_context; +} + +/* Free allocated VAAPI image */ +static void vaapi_destroy_image(vo_driver_t *this_gen, VAImage *va_image) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(va_image->image_id != VA_INVALID_ID) { + lprintf("vaapi_destroy_image 0x%08x\n", va_image->image_id); + vaStatus = vaDestroyImage(va_context->va_display, va_image->image_id); + vaapi_check_status(this_gen, vaStatus, "vaDestroyImage()"); + } + va_image->image_id = VA_INVALID_ID; + va_image->width = 0; + va_image->height = 0; +} + +/* Allocated VAAPI image */ +static VAStatus vaapi_create_image(vo_driver_t *this_gen, VASurfaceID va_surface_id, VAImage *va_image, int width, int height, int clear) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int i = 0; + VAStatus vaStatus; + + if(!va_context->valid_context || va_context->va_image_formats == NULL || va_context->va_num_image_formats == 0) + return VA_STATUS_ERROR_UNKNOWN; + + va_context->is_bound = 0; + + vaStatus = vaDeriveImage(va_context->va_display, va_surface_id, va_image); + if(vaStatus == VA_STATUS_SUCCESS) { + if (va_image->image_id != VA_INVALID_ID && va_image->buf != VA_INVALID_ID) { + va_context->is_bound = 1; + } + } + + if(!va_context->is_bound) { + for (i = 0; i < va_context->va_num_image_formats; i++) { + if (va_context->va_image_formats[i].fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_context->va_image_formats[i].fourcc == VA_FOURCC( 'I', '4', '2', '0' ) /*|| + va_context->va_image_formats[i].fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) */) { + vaStatus = vaCreateImage( va_context->va_display, &va_context->va_image_formats[i], width, height, va_image ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateImage()")) + goto error; + break; + } + } + } + + void *p_base = NULL; + + vaStatus = vaMapBuffer( va_context->va_display, va_image->buf, &p_base ); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + goto error; + + if(clear) { + if(va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' )) { + memset((uint8_t*)p_base + va_image->offsets[0], 0, va_image->pitches[0] * va_image->height); + memset((uint8_t*)p_base + va_image->offsets[1], 128, va_image->pitches[1] * (va_image->height/2)); + memset((uint8_t*)p_base + va_image->offsets[2], 128, va_image->pitches[2] * (va_image->height/2)); + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { + memset((uint8_t*)p_base + va_image->offsets[0], 0, va_image->pitches[0] * va_image->height); + memset((uint8_t*)p_base + va_image->offsets[1], 128, va_image->pitches[1] * (va_image->height/2)); + } + } + + vaStatus = vaUnmapBuffer( va_context->va_display, va_image->buf ); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + lprintf("vaapi_create_image 0x%08x width %d height %d format %s\n", va_image->image_id, va_image->width, va_image->height, + string_of_VAImageFormat(&va_image->format)); + + return VA_STATUS_SUCCESS; + +error: + /* house keeping */ + vaapi_destroy_image(this_gen, va_image); + return VA_STATUS_ERROR_UNKNOWN; +} + +/* Deassociate and free subpicture */ +static void vaapi_destroy_subpicture(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + lprintf("destroy sub 0x%08x 0x%08x 0x%08x\n", va_context->va_subpic_id, + va_context->va_subpic_image.image_id, va_context->va_subpic_image.buf); + + if(va_context->va_subpic_id != VA_INVALID_ID) { + vaStatus = vaDestroySubpicture(va_context->va_display, va_context->va_subpic_id); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } + va_context->va_subpic_id = VA_INVALID_ID; + + vaapi_destroy_image(this_gen, &va_context->va_subpic_image); + +} + +/* Create VAAPI subpicture */ +static VAStatus vaapi_create_subpicture(vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + int i = 0; + + if(!va_context->valid_context || !va_context->va_subpic_formats || va_context->va_num_subpic_formats == 0) + return VA_STATUS_ERROR_UNKNOWN; + + for (i = 0; i < va_context->va_num_subpic_formats; i++) { + if ( va_context->va_subpic_formats[i].fourcc == VA_FOURCC('B','G','R','A')) { + + vaStatus = vaCreateImage( va_context->va_display, &va_context->va_subpic_formats[i], width, height, &va_context->va_subpic_image ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateImage()")) + goto error; + + vaStatus = vaCreateSubpicture(va_context->va_display, va_context->va_subpic_image.image_id, &va_context->va_subpic_id ); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSubpicture()")) + goto error; + } + } + + if(va_context->va_subpic_image.image_id == VA_INVALID_ID || va_context->va_subpic_id == VA_INVALID_ID) + goto error; + + void *p_base = NULL; + + lprintf("create sub 0x%08x 0x%08x 0x%08x\n", va_context->va_subpic_id, + va_context->va_subpic_image.image_id, va_context->va_subpic_image.buf); + + vaStatus = vaMapBuffer(va_context->va_display, va_context->va_subpic_image.buf, &p_base); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + goto error; + + memset((uint32_t *)p_base, 0x0, va_context->va_subpic_image.data_size); + vaStatus = vaUnmapBuffer(va_context->va_display, va_context->va_subpic_image.buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + this->overlay_output_width = width; + this->overlay_output_height = height; + + lprintf("vaapi_create_subpicture 0x%08x format %s\n", va_context->va_subpic_image.image_id, + string_of_VAImageFormat(&va_context->va_subpic_image.format)); + + return VA_STATUS_SUCCESS; + +error: + /* house keeping */ + if(va_context->va_subpic_id != VA_INVALID_ID) + vaapi_destroy_subpicture(this_gen); + va_context->va_subpic_id = VA_INVALID_ID; + + vaapi_destroy_image(this_gen, &va_context->va_subpic_image); + + this->overlay_output_width = 0; + this->overlay_output_height = 0; + + return VA_STATUS_ERROR_UNKNOWN; +} + +static inline int vaapi_get_colorspace_flags(vo_driver_t *this_gen) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if(!va_context) + return 0; + + int colorspace = 0; +#if USE_VAAPI_COLORSPACE + switch (va_context->va_colorspace) { + case 0: + colorspace = ((va_context->sw_width >= 1280 || va_context->sw_height > 576) ? + VA_SRC_BT709 : VA_SRC_BT601); + break; + case 1: + colorspace = VA_SRC_BT601; + break; + case 2: + colorspace = VA_SRC_BT709; + break; + case 3: + colorspace = VA_SRC_SMPTE_240; + break; + default: + colorspace = VA_SRC_BT601; + break; + } +#endif + return colorspace; +} + +static void vaapi_property_callback (void *property_gen, xine_cfg_entry_t *entry) { + va_property_t *property = (va_property_t *) property_gen; + vaapi_driver_t *this = property->this; + ff_vaapi_context_t *va_context = this->va_context; + + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + VADisplayAttribute attr; + + attr.type = property->type; + attr.value = entry->num_value; + + lprintf("vaapi_property_callback property=%d, value=%d\n", property->type, entry->num_value ); + + VAStatus vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + //vaapi_check_status((vo_driver_t *)this, vaStatus, "vaSetDisplayAttributes()"); + + vaapi_show_display_props((vo_driver_t*)this); + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); +} + +/* called xlocked */ +static void vaapi_check_capability (vaapi_driver_t *this, + int property, VADisplayAttribute attr, + const char *config_name, + const char *config_desc, + const char *config_help) { + int int_default = 0; + cfg_entry_t *entry; + + this->props[property].type = attr.type; + this->props[property].min = attr.min_value; + this->props[property].max = attr.max_value; + int_default = attr.value; + this->props[property].atom = 1; + + if (config_name) { + /* is this a boolean property ? */ + if ((attr.min_value == 0) && (attr.max_value == 1)) { + this->config->register_bool (this->config, config_name, int_default, + config_desc, + config_help, 20, vaapi_property_callback, &this->props[property]); + + } else { + this->config->register_range (this->config, config_name, int_default, + this->props[property].min, this->props[property].max, + config_desc, + config_help, 20, vaapi_property_callback, &this->props[property]); + } + + entry = this->config->lookup_entry (this->config, config_name); + if((entry->num_value < this->props[property].min) || + (entry->num_value > this->props[property].max)) { + + this->config->update_num(this->config, config_name, + ((this->props[property].min + this->props[property].max) >> 1)); + + entry = this->config->lookup_entry (this->config, config_name); + } + + this->props[property].entry = entry; + + vaapi_set_property(&this->vo_driver, property, entry->num_value); + } else { + this->props[property].value = int_default; + } +} + +static void vaapi_show_display_props(vo_driver_t *this_gen) { + /* + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if(this->capabilities & VO_CAP_BRIGHTNESS) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : brightness : %d\n", this->props[VO_PROP_BRIGHTNESS].value); + if(this->capabilities & VO_CAP_CONTRAST) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : contrast : %d\n", this->props[VO_PROP_CONTRAST].value); + if(this->capabilities & VO_CAP_HUE) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : hue : %d\n", this->props[VO_PROP_HUE].value); + if(this->capabilities & VO_CAP_SATURATION) + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : saturation : %d\n", this->props[VO_PROP_SATURATION].value); + */ +} + +/* VAAPI display attributes. */ +static void vaapi_display_attribs(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int num_display_attrs, max_display_attrs; + VAStatus vaStatus; + VADisplayAttribute *display_attrs; + int i; + + max_display_attrs = vaMaxNumDisplayAttributes(va_context->va_display); + display_attrs = calloc(max_display_attrs, sizeof(*display_attrs)); + + if (display_attrs) { + num_display_attrs = 0; + vaStatus = vaQueryDisplayAttributes(va_context->va_display, + display_attrs, &num_display_attrs); + if(vaapi_check_status(this_gen, vaStatus, "vaQueryDisplayAttributes()")) { + for (i = 0; i < num_display_attrs; i++) { + switch (display_attrs[i].type) { + case VADisplayAttribBrightness: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_BRIGHTNESS; + vaapi_check_capability(this, VO_PROP_BRIGHTNESS, display_attrs[i], "video.output.vaapi_brightness", "Brightness setting", "Brightness setting"); + } + break; + case VADisplayAttribContrast: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_CONTRAST; + vaapi_check_capability(this, VO_PROP_CONTRAST, display_attrs[i], "video.output.vaapi_contrast", "Contrast setting", "Contrast setting"); + } + break; + case VADisplayAttribHue: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_HUE; + vaapi_check_capability(this, VO_PROP_HUE, display_attrs[i], "video.output.vaapi_hue", "Hue setting", "Hue setting"); + } + break; + case VADisplayAttribSaturation: + if( ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_GETTABLE ) && + ( display_attrs[i].flags & VA_DISPLAY_ATTRIB_SETTABLE ) ) { + this->capabilities |= VO_CAP_SATURATION; + vaapi_check_capability(this, VO_PROP_SATURATION, display_attrs[i], "video.output.vaapi_saturation", "Saturation setting", "Saturation setting"); + } + break; + default: + break; + } + } + } + free(display_attrs); + } + vaapi_show_display_props(this_gen); +} + +static void vaapi_set_background_color(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context->valid_context) + return; + + VADisplayAttribute attr; + memset( &attr, 0, sizeof(attr) ); + + attr.type = VADisplayAttribBackgroundColor; + attr.value = 0x000000; + + vaStatus = vaSetDisplayAttributes(va_context->va_display, &attr, 1); + //vaapi_check_status(this_gen, vaStatus, "vaSetDisplayAttributes()"); +} + +static VAStatus vaapi_destroy_render_surfaces(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + VAStatus vaStatus; + + for(i = 0; i < RENDER_SURFACES; i++) { + if(va_surface_ids[i] != VA_INVALID_SURFACE) { + vaStatus = vaSyncSurface(va_context->va_display, va_surface_ids[i]); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + vaStatus = vaDestroySurfaces(va_context->va_display, &va_surface_ids[i], 1); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaces()"); + va_surface_ids[i] = VA_INVALID_SURFACE; + + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = va_surface_ids[i]; + } + } + + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_destroy_soft_surfaces(vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + int i; + VAStatus vaStatus; + + + for(i = 0; i < SOFT_SURFACES; i++) { + if(va_soft_images[i].image_id != VA_INVALID_ID) + vaapi_destroy_image((vo_driver_t *)this, &va_soft_images[i]); + va_soft_images[i].image_id = VA_INVALID_ID; + + if(va_soft_surface_ids[i] != VA_INVALID_SURFACE) { +#ifdef DEBUG_SURFACE + printf("vaapi_close destroy render surface 0x%08x\n", va_soft_surface_ids[i]); +#endif + vaStatus = vaSyncSurface(va_context->va_display, va_soft_surface_ids[i]); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + vaStatus = vaDestroySurfaces(va_context->va_display, &va_soft_surface_ids[i], 1); + vaapi_check_status(this_gen, vaStatus, "vaDestroySurfaces()"); + va_soft_surface_ids[i] = VA_INVALID_SURFACE; + } + } + + va_context->sw_width = 0; + va_context->sw_height = 0; + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_init_soft_surfaces(vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + int i; + + vaapi_destroy_soft_surfaces(this_gen); + + vaStatus = vaCreateSurfaces(va_context->va_display, VA_RT_FORMAT_YUV420, width, height, va_soft_surface_ids, SOFT_SURFACES, NULL, 0); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaces()")) + goto error; + + /* allocate software surfaces */ + for(i = 0; i < SOFT_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + + vaStatus = vaapi_create_image((vo_driver_t *)this, va_soft_surface_ids[i], &va_soft_images[i], width, height, 1); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_create_image()")) { + va_soft_images[i].image_id = VA_INVALID_ID; + goto error; + } + + va_surface->index = i; + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_soft_surface_ids[i], va_soft_images[i].image_id, + 0, 0, va_soft_images[i].width, va_soft_images[i].height, + 0, 0, va_soft_images[i].width, va_soft_images[i].height); + vaapi_check_status(this_gen, vaStatus, "vaPutImage()"); + } +#ifdef DEBUG_SURFACE + printf("vaapi_init_soft_surfaces 0x%08x\n", va_soft_surface_ids[i]); +#endif + } + + va_context->sw_width = width; + va_context->sw_height = height; + return VA_STATUS_SUCCESS; + +error: + va_context->sw_width = 0; + va_context->sw_height = 0; + vaapi_destroy_soft_surfaces(this_gen); + return VA_STATUS_ERROR_UNKNOWN; +} + +static VAStatus vaapi_init_internal(vo_driver_t *this_gen, int va_profile, int width, int height, int softrender) { + vaapi_driver_t *this = (vaapi_driver_t *)this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAConfigAttrib va_attrib; + int maj, min, i; + VAStatus vaStatus; + + vaapi_close(this_gen); + vaapi_init_va_context(this); + + this->va_context->va_display = vaapi_get_display(this->display, this->opengl_render); + + if(!this->va_context->va_display) + goto error; + + vaStatus = vaInitialize(this->va_context->va_display, &maj, &min); + if(!vaapi_check_status((vo_driver_t *)this, vaStatus, "vaInitialize()")) + goto error; + + lprintf("libva: %d.%d\n", maj, min); + + va_context->valid_context = 1; + + int fmt_count = 0; + fmt_count = vaMaxNumImageFormats( va_context->va_display ); + va_context->va_image_formats = calloc( fmt_count, sizeof(*va_context->va_image_formats) ); + + vaStatus = vaQueryImageFormats(va_context->va_display, va_context->va_image_formats, &va_context->va_num_image_formats); + if(!vaapi_check_status(this_gen, vaStatus, "vaQueryImageFormats()")) + goto error; + + fmt_count = vaMaxNumSubpictureFormats( va_context->va_display ); + va_context->va_subpic_formats = calloc( fmt_count, sizeof(*va_context->va_subpic_formats) ); + + vaStatus = vaQuerySubpictureFormats( va_context->va_display , va_context->va_subpic_formats, 0, &va_context->va_num_subpic_formats ); + if(!vaapi_check_status(this_gen, vaStatus, "vaQuerySubpictureFormats()")) + goto error; + + const char *vendor = vaQueryVendorString(va_context->va_display); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Vendor : %s\n", vendor); + + this->query_va_status = 1; + char *p = (char *)vendor; + for(i = 0; i < strlen(vendor); i++, p++) { + if(strncmp(p, "VDPAU", strlen("VDPAU")) == 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Enable Splitted-Desktop Systems VDPAU-VIDEO workarounds.\n"); + this->query_va_status = 0; + this->opengl_use_tfp = 0; + break; + } + } + + vaapi_set_background_color(this_gen); + vaapi_display_attribs((vo_driver_t *)this); + + va_context->width = width; + va_context->height = height; + va_context->va_profile = va_profile; + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : Context width %d height %d\n", va_context->width, va_context->height); + + /* allocate decoding surfaces */ + vaStatus = vaCreateSurfaces(va_context->va_display, VA_RT_FORMAT_YUV420, va_context->width, va_context->height, va_surface_ids, RENDER_SURFACES, NULL, 0); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateSurfaces()")) + goto error; + + /* hardware decoding needs more setup */ + if(!softrender && va_profile >= 0) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : Profile: %d (%s) Entrypoint %d (%s) Surfaces %d\n", va_context->va_profile, vaapi_profile_to_string(va_context->va_profile), VAEntrypointVLD, vaapi_entrypoint_to_string(VAEntrypointVLD), RENDER_SURFACES); + + memset( &va_attrib, 0, sizeof(va_attrib) ); + va_attrib.type = VAConfigAttribRTFormat; + + vaStatus = vaGetConfigAttributes(va_context->va_display, va_context->va_profile, VAEntrypointVLD, &va_attrib, 1); + if(!vaapi_check_status(this_gen, vaStatus, "vaGetConfigAttributes()")) + goto error; + + if( (va_attrib.value & VA_RT_FORMAT_YUV420) == 0 ) + goto error; + + vaStatus = vaCreateConfig(va_context->va_display, va_context->va_profile, VAEntrypointVLD, &va_attrib, 1, &va_context->va_config_id); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateConfig()")) { + va_context->va_config_id = VA_INVALID_ID; + goto error; + } + + vaStatus = vaCreateContext(va_context->va_display, va_context->va_config_id, va_context->width, va_context->height, + VA_PROGRESSIVE, va_surface_ids, RENDER_SURFACES, &va_context->va_context_id); + if(!vaapi_check_status(this_gen, vaStatus, "vaCreateContext()")) { + va_context->va_context_id = VA_INVALID_ID; + goto error; + } + } + + /* xine was told to allocate RENDER_SURFACES frames. assign the frames the rendering surfaces. */ + for(i = 0; i < RENDER_SURFACES; i++) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[i]; + va_surface->index = i; + va_surface->status = SURFACE_FREE; + va_surface->va_surface_id = va_surface_ids[i]; + + if(this->frames[i]) { + vaapi_frame_t *frame = this->frames[i]; + frame->vaapi_accel_data.index = i; + + VAImage va_image; + vaStatus = vaapi_create_image(va_context->driver, va_surface_ids[i], &va_image, width, height, 1); + if(vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()") && !va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_ids[i], va_image.image_id, + 0, 0, va_image.width, va_image.height, + 0, 0, va_image.width, va_image.height); + vaapi_destroy_image(va_context->driver, &va_image); + } + } +#ifdef DEBUG_SURFACE + printf("vaapi_init_internal 0x%08x\n", va_surface_ids[i]); +#endif + } + + vaStatus = vaapi_init_soft_surfaces(this_gen, width, height); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_init_soft_surfaces()")) { + vaapi_destroy_soft_surfaces(this_gen); + goto error; + } + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : guarded render : %d\n", this->guarded_render); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : glxrender : %d\n", this->opengl_render); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : glxrender tfp : %d\n", this->opengl_use_tfp); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : is_bound : %d\n", va_context->is_bound); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : scaling level : name %s value 0x%08x\n", scaling_level_enum_names[this->scaling_level_enum], this->scaling_level); + + this->init_opengl_render = 1; + + return VA_STATUS_SUCCESS; + +error: + vaapi_close(this_gen); + vaapi_init_va_context(this); + va_context->valid_context = 0; + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_init : error init vaapi\n"); + + return VA_STATUS_ERROR_UNKNOWN; +} + +/* + * Init VAAPI. This function is called from the decoder side. + * When the decoder uses software decoding vaapi_init is not called. + * Therefore we do it in vaapi_display_frame to get a valid VAAPI context + */ +static VAStatus vaapi_init(vo_frame_t *frame_gen, int va_profile, int width, int height, int softrender) { + if(!frame_gen) + return VA_STATUS_ERROR_UNKNOWN; + + vo_driver_t *this_gen = (vo_driver_t *) frame_gen->driver; + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + VAStatus vaStatus; + + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + if(!this->guarded_render) { + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + } + + vaStatus = vaapi_init_internal(this_gen, va_profile, width, height, softrender); + + if(!this->guarded_render) { + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + } + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + return vaStatus; +} + +static void vaapi_frame_proc_slice (vo_frame_t *vo_img, uint8_t **src) +{ + vo_img->proc_called = 1; +} + +static void vaapi_frame_field (vo_frame_t *vo_img, int which_field) +{ +} + +static void vaapi_frame_dispose (vo_frame_t *vo_img) { + vaapi_driver_t *this = (vaapi_driver_t *) vo_img->driver; + vaapi_frame_t *frame = (vaapi_frame_t *) vo_img ; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + + lprintf("vaapi_frame_dispose\n"); + + av_free (frame->vo_frame.base[0]); + av_free (frame->vo_frame.base[1]); + av_free (frame->vo_frame.base[2]); + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + va_surface->status = SURFACE_FREE; + } + + free (frame); +} + +static vo_frame_t *vaapi_alloc_frame (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame; + + frame = (vaapi_frame_t *) calloc(1, sizeof(vaapi_frame_t)); + + if (!frame) + return NULL; + + this->frames[this->num_frame_buffers++] = frame; + + frame->vo_frame.base[0] = frame->vo_frame.base[1] = frame->vo_frame.base[2] = NULL; + frame->width = frame->height = frame->format = frame->flags = 0; + + frame->vo_frame.accel_data = &frame->vaapi_accel_data; + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + /* + * supply required functions + */ + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + frame->vo_frame.proc_slice = vaapi_frame_proc_slice; + frame->vo_frame.proc_frame = NULL; + frame->vo_frame.field = vaapi_frame_field; + frame->vo_frame.dispose = vaapi_frame_dispose; + frame->vo_frame.driver = this_gen; + + frame->vaapi_accel_data.vo_frame = &frame->vo_frame; + frame->vaapi_accel_data.vaapi_init = &vaapi_init; + frame->vaapi_accel_data.profile_from_imgfmt = &profile_from_imgfmt; + frame->vaapi_accel_data.get_context = &get_context; + +#if AVVIDEO > 1 + frame->vaapi_accel_data.avcodec_decode_video2 = &guarded_avcodec_decode_video2; +#else + frame->vaapi_accel_data.avcodec_decode_video = &guarded_avcodec_decode_video; +#endif + + frame->vaapi_accel_data.get_vaapi_surface = &get_vaapi_surface; + frame->vaapi_accel_data.render_vaapi_surface = &render_vaapi_surface; + frame->vaapi_accel_data.release_vaapi_surface = &release_vaapi_surface; + frame->vaapi_accel_data.guarded_render = &guarded_render; + + lprintf("alloc frame\n"); + + return (vo_frame_t *) frame; +} + + +/* Display OSD */ +static int vaapi_ovl_associate(vo_driver_t *this_gen, int format, int bShow) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus; + + if(!va_context->valid_context) + return 0; + + if(va_context->last_sub_image_fmt && !bShow) { + if(va_context->va_subpic_id != VA_INVALID_ID) { + if(va_context->last_sub_image_fmt == XINE_IMGFMT_VAAPI) { + vaStatus = vaDeassociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_surface_ids, RENDER_SURFACES); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } else if(va_context->last_sub_image_fmt == XINE_IMGFMT_YV12 || + va_context->last_sub_image_fmt == XINE_IMGFMT_YUY2) { + vaStatus = vaDeassociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_soft_surface_ids, SOFT_SURFACES); + vaapi_check_status(this_gen, vaStatus, "vaDeassociateSubpicture()"); + } + } + va_context->last_sub_image_fmt = 0; + return 1; + } + + if(!va_context->last_sub_image_fmt && bShow) { + unsigned int flags = 0; + unsigned int output_width = va_context->width; + unsigned int output_height = va_context->height; + void *p_base = NULL; + + VAStatus vaStatus; + + vaapi_destroy_subpicture(this_gen); + vaStatus = vaapi_create_subpicture(this_gen, this->overlay_bitmap_width, this->overlay_bitmap_height); + if(!vaapi_check_status(this_gen, vaStatus, "vaapi_create_subpicture()")) + return 0; + + vaStatus = vaMapBuffer(va_context->va_display, va_context->va_subpic_image.buf, &p_base); + if(!vaapi_check_status(this_gen, vaStatus, "vaMapBuffer()")) + return 0; + + xine_fast_memcpy((uint32_t *)p_base, this->overlay_bitmap, this->overlay_bitmap_width * this->overlay_bitmap_height * sizeof(uint32_t)); + + vaStatus = vaUnmapBuffer(va_context->va_display, va_context->va_subpic_image.buf); + vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()"); + + lprintf( "vaapi_ovl_associate: overlay_width=%d overlay_height=%d unscaled %d va_subpic_id 0x%08x ovl_changed %d has_overlay %d bShow %d overlay_bitmap_width %d overlay_bitmap_height %d va_context->width %d va_context->height %d\n", + this->overlay_output_width, this->overlay_output_height, this->has_overlay, + va_context->va_subpic_id, this->ovl_changed, this->has_overlay, bShow, + this->overlay_bitmap_width, this->overlay_bitmap_height, + va_context->width, va_context->height); + + if(format == XINE_IMGFMT_VAAPI) { + lprintf("vaapi_ovl_associate hw\n"); + vaStatus = vaAssociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_surface_ids, RENDER_SURFACES, + 0, 0, va_context->va_subpic_image.width, va_context->va_subpic_image.height, + 0, 0, output_width, output_height, flags); + } else { + lprintf("vaapi_ovl_associate sw\n"); + vaStatus = vaAssociateSubpicture(va_context->va_display, va_context->va_subpic_id, + va_soft_surface_ids, SOFT_SURFACES, + 0, 0, va_context->va_subpic_image.width, va_context->va_subpic_image.height, + 0, 0, va_soft_images[0].width, va_soft_images[0].height, flags); + } + + if(vaapi_check_status(this_gen, vaStatus, "vaAssociateSubpicture()")) { + va_context->last_sub_image_fmt = format; + return 1; + } + } + return 0; +} + +static void vaapi_overlay_clut_yuv2rgb(vaapi_driver_t *this, vo_overlay_t *overlay, vaapi_frame_t *frame) +{ + int i; + clut_t* clut = (clut_t*) overlay->color; + + if (!overlay->rgb_clut) { + for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++ ) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->rgb_clut++; + } + if (!overlay->hili_rgb_clut) { + clut = (clut_t*) overlay->hili_color; + for ( i=0; i<sizeof(overlay->color)/sizeof(overlay->color[0]); i++) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->hili_rgb_clut++; + } +} + +static void vaapi_overlay_begin (vo_driver_t *this_gen, + vo_frame_t *frame_gen, int changed) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + if ( !changed ) + return; + + this->has_overlay = 0; + ++this->ovl_changed; + + /* Apply OSD layer. */ + if(va_context->valid_context) { + lprintf("vaapi_overlay_begin chaned %d\n", changed); + + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + } +} + +static void vaapi_overlay_blend (vo_driver_t *this_gen, + vo_frame_t *frame_gen, vo_overlay_t *overlay) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + int i = this->ovl_changed; + + if (!i) + return; + + if (--i >= XINE_VORAW_MAX_OVL) + return; + + if (overlay->width <= 0 || overlay->height <= 0 || (!overlay->rle && (!overlay->argb_layer || !overlay->argb_layer->buffer))) + return; + + if (overlay->rle) + lprintf("overlay[%d] rle %s%s %dx%d@%d,%d hili rect %d,%d-%d,%d\n", i, + overlay->unscaled ? " unscaled ": " scaled ", + (overlay->rgb_clut > 0 || overlay->hili_rgb_clut > 0) ? " rgb ": " ycbcr ", + overlay->width, overlay->height, overlay->x, overlay->y, + overlay->hili_left, overlay->hili_top, + overlay->hili_right, overlay->hili_bottom); + if (overlay->argb_layer && overlay->argb_layer->buffer) + lprintf("overlay[%d] argb %s %dx%d@%d,%d dirty rect %d,%d-%d,%d\n", i, + overlay->unscaled ? " unscaled ": " scaled ", + overlay->width, overlay->height, overlay->x, overlay->y, + overlay->argb_layer->x1, overlay->argb_layer->y1, + overlay->argb_layer->x2, overlay->argb_layer->y2); + + + this->overlays[i] = overlay; + + ++this->ovl_changed; +} + +static void vaapi_overlay_end (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + + int novls = this->ovl_changed; + if (novls < 2) { + this->ovl_changed = 0; + return; + } + --novls; + + uint32_t output_width = frame->width; + uint32_t output_height = frame->height; + uint32_t unscaled_width = 0, unscaled_height = 0; + vo_overlay_t *first_scaled = NULL, *first_unscaled = NULL; + vaapi_rect_t dirty_rect, unscaled_dirty_rect; + int has_rle = 0; + + int i; + for (i = 0; i < novls; ++i) { + vo_overlay_t *ovl = this->overlays[i]; + + if (ovl->rle) + has_rle = 1; + + if (ovl->unscaled) { + if (first_unscaled) { + if (ovl->x < unscaled_dirty_rect.x1) + unscaled_dirty_rect.x1 = ovl->x; + if (ovl->y < unscaled_dirty_rect.y1) + unscaled_dirty_rect.y1 = ovl->y; + if ((ovl->x + ovl->width) > unscaled_dirty_rect.x2) + unscaled_dirty_rect.x2 = ovl->x + ovl->width; + if ((ovl->y + ovl->height) > unscaled_dirty_rect.y2) + unscaled_dirty_rect.y2 = ovl->y + ovl->height; + } else { + first_unscaled = ovl; + unscaled_dirty_rect.x1 = ovl->x; + unscaled_dirty_rect.y1 = ovl->y; + unscaled_dirty_rect.x2 = ovl->x + ovl->width; + unscaled_dirty_rect.y2 = ovl->y + ovl->height; + } + + unscaled_width = unscaled_dirty_rect.x2; + unscaled_height = unscaled_dirty_rect.y2; + } else { + if (first_scaled) { + if (ovl->x < dirty_rect.x1) + dirty_rect.x1 = ovl->x; + if (ovl->y < dirty_rect.y1) + dirty_rect.y1 = ovl->y; + if ((ovl->x + ovl->width) > dirty_rect.x2) + dirty_rect.x2 = ovl->x + ovl->width; + if ((ovl->y + ovl->height) > dirty_rect.y2) + dirty_rect.y2 = ovl->y + ovl->height; + } else { + first_scaled = ovl; + dirty_rect.x1 = ovl->x; + dirty_rect.y1 = ovl->y; + dirty_rect.x2 = ovl->x + ovl->width; + dirty_rect.y2 = ovl->y + ovl->height; + } + + if (dirty_rect.x2 > output_width) + output_width = dirty_rect.x2; + if (dirty_rect.y2 > output_height) + output_height = dirty_rect.y2; + + } + } + + int need_init = 0; + + lprintf("dirty_rect.x0 %d dirty_rect.y0 %d dirty_rect.x2 %d dirty_rect.y2 %d output_width %d output_height %d\n", + dirty_rect.x0, dirty_rect.y0, dirty_rect.x2, dirty_rect.y2, output_width, output_height); + + if (first_scaled) { + vaapi_rect_t dest; + dest.x1 = first_scaled->x; + dest.y1 = first_scaled->y; + dest.x2 = first_scaled->x + first_scaled->width; + dest.y2 = first_scaled->y + first_scaled->height; + if (!RECT_IS_EQ(dest, dirty_rect)) + need_init = 1; + } + + int need_unscaled_init = (first_unscaled && + (first_unscaled->x != unscaled_dirty_rect.x1 || + first_unscaled->y != unscaled_dirty_rect.y1 || + (first_unscaled->x + first_unscaled->width) != unscaled_dirty_rect.x2 || + (first_unscaled->y + first_unscaled->height) != unscaled_dirty_rect.y2)); + + if (first_scaled) { + this->overlay_output_width = output_width; + this->overlay_output_height = output_height; + + need_init = 1; + + this->overlay_dirty_rect = dirty_rect; + } + + if (first_unscaled) { + this->overlay_unscaled_width = unscaled_width; + this->overlay_unscaled_height = unscaled_height; + + need_unscaled_init = 1; + this->overlay_unscaled_dirty_rect = unscaled_dirty_rect; + } + + if (has_rle || need_init || need_unscaled_init) { + lprintf("has_rle %d need_init %d need_unscaled_init %d unscaled_width %d unscaled_height %d output_width %d output_height %d\n", + has_rle, need_init, need_unscaled_init, unscaled_width, unscaled_height, output_width, output_height); + if (need_init) { + this->overlay_bitmap_width = output_width; + this->overlay_bitmap_height = output_height; + } + if (need_unscaled_init) { + + if(this->vdr_osd_width) + this->overlay_bitmap_width = (this->vdr_osd_width > this->sc.gui_width) ? this->vdr_osd_width : this->sc.gui_width; + else + this->overlay_bitmap_width = (unscaled_width > this->sc.gui_width) ? unscaled_width : this->sc.gui_width; + + if(this->vdr_osd_height) + this->overlay_bitmap_height = (this->vdr_osd_height > this->sc.gui_height) ? this->vdr_osd_height : this->sc.gui_height; + else + this->overlay_bitmap_height = (unscaled_height > this->sc.gui_height) ? unscaled_height : this->sc.gui_height; + + } else if (need_init) { + + if(this->vdr_osd_width) + this->overlay_bitmap_width = (this->vdr_osd_width > this->sc.gui_width) ? this->vdr_osd_width : this->sc.gui_width; + else + this->overlay_bitmap_width = (output_width > this->sc.gui_width) ? output_width : this->sc.gui_width; + + if(this->vdr_osd_height) + this->overlay_bitmap_height = (this->vdr_osd_height > this->sc.gui_height) ? this->vdr_osd_height : this->sc.gui_height; + else + this->overlay_bitmap_height = (output_height > this->sc.gui_height) ? output_height : this->sc.gui_height; + + } + } + + if ((this->overlay_bitmap_width * this->overlay_bitmap_height) > this->overlay_bitmap_size) { + this->overlay_bitmap_size = this->overlay_bitmap_width * this->overlay_bitmap_height; + free(this->overlay_bitmap); + this->overlay_bitmap = calloc( this->overlay_bitmap_size, sizeof(uint32_t)); + } else { + memset(this->overlay_bitmap, 0x0, this->overlay_bitmap_size * sizeof(uint32_t)); + } + + for (i = 0; i < novls; ++i) { + vo_overlay_t *ovl = this->overlays[i]; + uint32_t *bitmap = NULL; + uint32_t *rgba = NULL; + + if (ovl->rle) { + if(ovl->width<=0 || ovl->height<=0) + continue; + + if (!ovl->rgb_clut || !ovl->hili_rgb_clut) + vaapi_overlay_clut_yuv2rgb (this, ovl, frame); + + bitmap = rgba = calloc(ovl->width * ovl->height * 4, sizeof(uint32_t)); + + int num_rle = ovl->num_rle; + rle_elem_t *rle = ovl->rle; + uint32_t red, green, blue, alpha; + clut_t *low_colors = (clut_t*)ovl->color; + clut_t *hili_colors = (clut_t*)ovl->hili_color; + uint8_t *low_trans = ovl->trans; + uint8_t *hili_trans = ovl->hili_trans; + clut_t *colors; + uint8_t *trans; + int rlelen = 0; + uint8_t clr = 0; + int i, pos=0, x, y; + + while (num_rle > 0) { + x = pos % ovl->width; + y = pos / ovl->width; + + if ( (x>=ovl->hili_left && x<=ovl->hili_right) && (y>=ovl->hili_top && y<=ovl->hili_bottom) ) { + colors = hili_colors; + trans = hili_trans; + } + else { + colors = low_colors; + trans = low_trans; + } + rlelen = rle->len; + clr = rle->color; + for ( i=0; i<rlelen; ++i ) { + if ( trans[clr] == 0 ) { + alpha = red = green = blue = 0; + } + else { + red = colors[clr].y; // red + green = colors[clr].cr; // green + blue = colors[clr].cb; // blue + alpha = trans[clr]*255/15; + } + *rgba = (alpha<<24) | (red<<16) | (green<<8) | blue; + rgba++; + ++pos; + } + ++rle; + --num_rle; + } + lprintf("width %d height %d pos %d %d\n", ovl->width, ovl->height, pos, ovl->width * ovl->height); + } else { + pthread_mutex_lock(&ovl->argb_layer->mutex); + bitmap = ovl->argb_layer->buffer; + } + + /* Blit overlay to destination */ + uint32_t pitch = ovl->width * sizeof(uint32_t); + uint32_t *copy_dst = this->overlay_bitmap; + uint32_t *copy_src = NULL; + uint32_t height = 0; + + copy_src = bitmap; + + copy_dst += ovl->y * this->overlay_bitmap_width; + + lprintf("overlay_bitmap_width %d overlay_bitmap_height %d ovl->x %d ovl->y %d ovl->width %d ovl->height %d width %d height %d\n", + this->overlay_bitmap_width, this->overlay_bitmap_height, ovl->x, ovl->y, ovl->width, ovl->height, this->overlay_bitmap_width, this->overlay_bitmap_height); + + for(height = 0; height < ovl->height; height++) { + if((height + ovl->y) >= this->overlay_bitmap_height) + break; + + xine_fast_memcpy(copy_dst + ovl->x, copy_src, pitch); + copy_dst += this->overlay_bitmap_width; + copy_src += ovl->width; + } + + if (ovl->rle) { + if(bitmap) { + free(bitmap); + bitmap = NULL; + } + } + + if (!ovl->rle) + pthread_mutex_unlock(&ovl->argb_layer->mutex); + + } + + this->ovl_changed = 0; + this->has_overlay = (first_scaled != NULL) | (first_unscaled != NULL); + + lprintf("this->has_overlay %d\n", this->has_overlay); + /* Apply OSD layer. */ + if(va_context->valid_context) { + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + } +} + +static void vaapi_resize_glx_window (vo_driver_t *this_gen, int width, int height) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + if(this->valid_opengl_context) { + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(FOVY, ASPECT, Z_NEAR, Z_FAR); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(-0.5f, -0.5f, -Z_CAMERA); + glScalef(1.0f / (GLfloat)width, + -1.0f / (GLfloat)height, + 1.0f / (GLfloat)width); + glTranslatef(0.0f, -1.0f * (GLfloat)height, 0.0f); + } +} + +static int vaapi_redraw_needed (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + int ret = 0; + + _x_vo_scale_compute_ideal_size( &this->sc ); + + if ( _x_vo_scale_redraw_needed( &this->sc ) ) { + _x_vo_scale_compute_output_size( &this->sc ); + + XMoveResizeWindow(this->display, this->window, + 0, 0, this->sc.gui_width, this->sc.gui_height); + + vaapi_resize_glx_window(this_gen, this->sc.gui_width, this->sc.gui_height); + + ret = 1; + } + + return ret; +} + +static void vaapi_provide_standard_frame_data (vo_frame_t *orig, xine_current_frame_data_t *data) +{ + vaapi_driver_t *driver = (vaapi_driver_t *) orig->driver; + ff_vaapi_context_t *va_context = driver->va_context; + + vaapi_accel_t *accel = (vaapi_accel_t *) orig->accel_data; + vo_frame_t *this = accel->vo_frame; + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + uint32_t pitches[3]; + uint8_t *base[3]; + + if(driver == NULL) { + return; + } + + if (this->format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_provide_standard_frame_data: unexpected frame format 0x%08x!\n", this->format); + return; + } + + if( !accel || va_surface->va_surface_id == VA_INVALID_SURFACE ) + return; + + lprintf("vaapi_provide_standard_frame_data %s 0x%08x width %d height %d\n", + (this->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((this->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + va_surface->va_surface_id, this->width, this->height); + + pthread_mutex_lock(&driver->vaapi_lock); + DO_LOCKDISPLAY; + + int width = va_context->width; + int height = va_context->height; + + data->format = XINE_IMGFMT_YV12; + data->img_size = width * height + + ((width + 1) / 2) * ((height + 1) / 2) + + ((width + 1) / 2) * ((height + 1) / 2); + if (data->img) { + pitches[0] = width; + pitches[2] = width / 2; + pitches[1] = width / 2; + base[0] = data->img; + base[2] = data->img + width * height; + base[1] = data->img + width * height + width * this->height / 4; + + VAImage va_image; + VAStatus vaStatus; + void *p_base; + + vaStatus = vaSyncSurface(va_context->va_display, va_surface->va_surface_id); + vaapi_check_status(va_context->driver, vaStatus, "vaSyncSurface()"); + + VASurfaceStatus surf_status = 0; + + if(driver->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface->va_surface_id, &surf_status); + vaapi_check_status(va_context->driver, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status != VASurfaceReady) + goto error; + + vaStatus = vaapi_create_image(va_context->driver, va_surface->va_surface_id, &va_image, width, height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) + goto error; + + lprintf("vaapi_provide_standard_frame_data accel->va_surface_id 0x%08x va_image.image_id 0x%08x va_context->width %d va_context->height %d va_image.width %d va_image.height %d width %d height %d size1 %d size2 %d %d %d %d status %d num_planes %d\n", + va_surface->va_surface_id, va_image.image_id, va_context->width, va_context->height, va_image.width, va_image.height, width, height, va_image.data_size, data->img_size, + va_image.pitches[0], va_image.pitches[1], va_image.pitches[2], surf_status, va_image.num_planes); + + if(va_image.image_id == VA_INVALID_ID) + goto error; + + if(!va_context->is_bound) { + vaStatus = vaGetImage(va_context->va_display, va_surface->va_surface_id, 0, 0, + va_image.width, va_image.height, va_image.image_id); + } else { + vaStatus = VA_STATUS_SUCCESS; + } + + if(vaapi_check_status(va_context->driver, vaStatus, "vaGetImage()")) { + vaStatus = vaMapBuffer( va_context->va_display, va_image.buf, &p_base ) ; + if(vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) { + + /* + uint8_t *src[3] = { NULL, }; + src[0] = (uint8_t *)p_base + va_image.offsets[0]; + src[1] = (uint8_t *)p_base + va_image.offsets[1]; + src[2] = (uint8_t *)p_base + va_image.offsets[2]; + */ + + if( va_image.format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image.format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("VAAPI YV12 image\n"); + + yv12_to_yv12( + (uint8_t*)p_base + va_image.offsets[0], va_image.pitches[0], + base[0], pitches[0], + (uint8_t*)p_base + va_image.offsets[1], va_image.pitches[1], + base[1], pitches[1], + (uint8_t*)p_base + va_image.offsets[2], va_image.pitches[2], + base[2], pitches[2], + va_image.width, va_image.height); + + } else if( va_image.format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' ) ) { + lprintf("VAAPI NV12 image\n"); + + lprintf("va_image.offsets[0] %d va_image.offsets[1] %d va_image.offsets[2] %d size %d size %d size %d width %d height %d width %d height %d\n", + va_image.offsets[0], va_image.offsets[1], va_image.offsets[2], va_image.data_size, va_image.width * va_image.height, + data->img_size, width, height, va_image.width, va_image.height); + + base[0] = data->img; + base[1] = data->img + width * height; + base[2] = data->img + width * height + width * height / 4; + + nv12_to_yv12((uint8_t *)p_base + va_image.offsets[0], va_image.pitches[0], + (uint8_t *)p_base + va_image.offsets[1], va_image.pitches[1], + base[0], pitches[0], + base[1], pitches[1], + base[2], pitches[2], + va_image.width, va_image.height, + width, height, + va_image.data_size); + + } else { + printf("vaapi_provide_standard_frame_data unsupported image format\n"); + } + + vaStatus = vaUnmapBuffer(va_context->va_display, va_image.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + vaapi_destroy_image(va_context->driver, &va_image); + } + } + } + +error: + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&driver->vaapi_lock); +} + +static void vaapi_duplicate_frame_data (vo_frame_t *this_gen, vo_frame_t *original) +{ + vaapi_driver_t *driver = (vaapi_driver_t *) original->driver; + ff_vaapi_context_t *va_context = driver->va_context; + + vaapi_frame_t *this = (vaapi_frame_t *)this_gen; + vaapi_frame_t *orig = (vaapi_frame_t *)original; + + vaapi_accel_t *accel_this = &this->vaapi_accel_data; + vaapi_accel_t *accel_orig = &orig->vaapi_accel_data; + + ff_vaapi_surface_t *va_surface_this = &va_render_surfaces[accel_this->index]; + ff_vaapi_surface_t *va_surface_orig = &va_render_surfaces[accel_orig->index]; + + lprintf("vaapi_duplicate_frame_data %s %s 0x%08x 0x%08x\n", + (this_gen->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((this_gen->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + (original->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((original->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2"), + va_surface_this->va_surface_id, va_surface_orig->va_surface_id); + + if (orig->vo_frame.format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_duplicate_frame_data: unexpected frame format 0x%08x!\n", orig->format); + return; + } + + if (this->vo_frame.format != XINE_IMGFMT_VAAPI) { + xprintf(driver->xine, XINE_VERBOSITY_LOG, LOG_MODULE "vaapi_duplicate_frame_data: unexpected frame format 0x%08x!\n", this->format); + return; + } + + pthread_mutex_lock(&driver->vaapi_lock); + DO_LOCKDISPLAY; + + VAImage va_image_orig; + VAImage va_image_this; + VAStatus vaStatus; + void *p_base_orig = NULL; + void *p_base_this = NULL; + + vaStatus = vaSyncSurface(va_context->va_display, va_surface_orig->va_surface_id); + vaapi_check_status(va_context->driver, vaStatus, "vaSyncSurface()"); + + int this_width = va_context->width; + int this_height = va_context->height; + int orig_width = va_context->width; + int orig_height = va_context->height; + + vaStatus = vaapi_create_image(va_context->driver, va_surface_orig->va_surface_id, &va_image_orig, orig_width, orig_height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + va_image_orig.image_id = VA_INVALID_ID; + goto error; + } + + vaStatus = vaapi_create_image(va_context->driver, va_surface_this->va_surface_id, &va_image_this, this_width, this_height, 0); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaapi_create_image()")) { + va_image_this.image_id = VA_INVALID_ID; + goto error; + } + + if(va_image_orig.image_id == VA_INVALID_ID || va_image_this.image_id == VA_INVALID_ID) { + printf("vaapi_duplicate_frame_data invalid image\n"); + goto error; + } + + lprintf("vaapi_duplicate_frame_data va_image_orig.image_id 0x%08x va_image_orig.width %d va_image_orig.height %d width %d height %d size %d %d %d %d\n", + va_image_orig.image_id, va_image_orig.width, va_image_orig.height, this->width, this->height, va_image_orig.data_size, + va_image_orig.pitches[0], va_image_orig.pitches[1], va_image_orig.pitches[2]); + + if(!va_context->is_bound) { + vaStatus = vaGetImage(va_context->va_display, va_surface_orig->va_surface_id, 0, 0, + va_image_orig.width, va_image_orig.height, va_image_orig.image_id); + } else { + vaStatus = VA_STATUS_SUCCESS; + } + + if(vaapi_check_status(va_context->driver, vaStatus, "vaGetImage()")) { + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_this->va_surface_id, va_image_orig.image_id, + 0, 0, va_image_orig.width, va_image_orig.height, + 0, 0, va_image_this.width, va_image_this.height); + vaapi_check_status(va_context->driver, vaStatus, "vaPutImage()"); + } else { + vaStatus = vaMapBuffer( va_context->va_display, va_image_orig.buf, &p_base_orig ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + goto error; + + vaStatus = vaMapBuffer( va_context->va_display, va_image_this.buf, &p_base_this ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + goto error; + + int size = (va_image_orig.data_size > va_image_this.data_size) ? va_image_this.data_size : va_image_orig.data_size; + xine_fast_memcpy((uint8_t *) p_base_this, (uint8_t *) p_base_orig, size); + + } + } + +error: + if(p_base_orig) { + vaStatus = vaUnmapBuffer(va_context->va_display, va_image_orig.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + } + if(p_base_this) { + vaStatus = vaUnmapBuffer(va_context->va_display, va_image_this.buf); + vaapi_check_status(va_context->driver, vaStatus, "vaUnmapBuffer()"); + } + + vaapi_destroy_image(va_context->driver, &va_image_orig); + vaapi_destroy_image(va_context->driver, &va_image_this); + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&driver->vaapi_lock); +} + +static void vaapi_update_frame_format (vo_driver_t *this_gen, + vo_frame_t *frame_gen, + uint32_t width, uint32_t height, + double ratio, int format, int flags) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t*)frame_gen; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + + lprintf("vaapi_update_frame_format\n"); + + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + lprintf("vaapi_update_frame_format %s %s width %d height %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + (format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + width, height); + + frame->vo_frame.width = width; + frame->vo_frame.height = height; + + if ((frame->width != width) + || (frame->height != height) + || (frame->format != format)) { + + // (re-) allocate render space + av_freep (&frame->vo_frame.base[0]); + av_freep (&frame->vo_frame.base[1]); + av_freep (&frame->vo_frame.base[2]); + + /* set init_vaapi on frame formats XINE_IMGFMT_YV12/XINE_IMGFMT_YUY2 only. + * for XINE_IMGFMT_VAAPI the init was already done. + */ + if (format == XINE_IMGFMT_YV12) { + frame->vo_frame.pitches[0] = 8*((width + 7) / 8); + frame->vo_frame.pitches[1] = 8*((width + 15) / 16); + frame->vo_frame.pitches[2] = 8*((width + 15) / 16); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.base[1] = av_mallocz (frame->vo_frame.pitches[1] * ((height+1)/2) + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.base[2] = av_mallocz (frame->vo_frame.pitches[2] * ((height+1)/2) + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + lprintf("XINE_IMGFMT_YV12 width %d height %d\n", width, height); + } else if (format == XINE_IMGFMT_YUY2){ + frame->vo_frame.pitches[0] = 8*((width + 3) / 4); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height + FF_INPUT_BUFFER_PADDING_SIZE); + frame->vo_frame.proc_duplicate_frame_data = NULL; + frame->vo_frame.proc_provide_standard_frame_data = NULL; + lprintf("XINE_IMGFMT_YUY2 width %d height %d\n", width, height); + } else if (format == XINE_IMGFMT_VAAPI) { + frame->vo_frame.proc_duplicate_frame_data = vaapi_duplicate_frame_data; + frame->vo_frame.proc_provide_standard_frame_data = vaapi_provide_standard_frame_data; + lprintf("XINE_IMGFMT_VAAPI width %d height %d\n", width, height); + } + + frame->width = width; + frame->height = height; + frame->format = format; + frame->flags = flags; + vaapi_frame_field ((vo_frame_t *)frame, flags); + } + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + if(va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_update_frame_format 0x%08x\n", va_surface->va_surface_id); +#endif + } else if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RELEASE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_update_frame_format 0x%08x\n", va_surface->va_surface_id); +#endif + } + } + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + + frame->ratio = ratio; + frame->vo_frame.future_frame = NULL; +} + +static inline uint8_t clip_uint8_vlc( int32_t a ) +{ + if( a&(~255) ) return (-a)>>31; + else return a; +} + + +static void nv12_to_yv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *uv_src, int uv_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *u_dst, int u_dst_pitch, + uint8_t *v_dst, int v_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int src_data_size) { + + int y_src_size = src_height * y_src_pitch; + int y, x; + + int uv_src_size = src_height * uv_src_pitch / 2; + if((y_src_size + uv_src_size) != (src_data_size)) + printf("nv12_to_yv12 strange %d\n", (y_src_size + uv_src_size) - (src_data_size)); + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + for(y = 0; y < height; y++) { + xine_fast_memcpy(y_dst, y_src, width); + y_src += y_src_pitch; + y_dst += y_dst_pitch; + } + + for(y = 0; y < height; y++) { + const uint8_t *uv_src_tmp = uv_src; + for(x = 0; x < u_dst_pitch; x++) { + if(((y * uv_src_pitch) + x) < uv_src_size) { + *(u_dst + x) = *(uv_src_tmp ); + *(v_dst + x) = *(uv_src_tmp + 1); + } + uv_src_tmp += 2; + } + uv_src += uv_src_pitch; + u_dst += u_dst_pitch; + v_dst += v_dst_pitch; + } +} + +static void yv12_to_nv12(const uint8_t *y_src, int y_src_pitch, + const uint8_t *u_src, int u_src_pitch, + const uint8_t *v_src, int v_src_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size) { + + int y_dst_size = dst_height * y_dst_pitch; + int y, x; + + lprintf("yv12_to_nv12 converter\n"); + + int uv_dst_size = dst_height * uv_dst_pitch / 2; + if((y_dst_size + uv_dst_size) != (dst_data_size)) + printf("yv12_to_nv12 strange %d\n", (y_dst_size + uv_dst_size) - (dst_data_size)); + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + for(y = 0; y < height; y++) { + xine_fast_memcpy(y_dst, y_src, width); + y_src += y_src_pitch; + y_dst += y_dst_pitch; + } + + for(y = 0; y < height; y++) { + uint8_t *uv_dst_tmp = uv_dst; + for(x = 0; x < u_src_pitch; x++) { + if(((y * uv_dst_pitch) + x) < uv_dst_size) { + *(uv_dst_tmp ) = *(u_src + x); + *(uv_dst_tmp + 1) = *(v_src + x); + } + uv_dst_tmp += 2; + } + uv_dst += uv_dst_pitch; + u_src += u_src_pitch; + v_src += v_src_pitch; + } +} + +static void yuy2_to_nv12(const uint8_t *src_yuy2_map, int yuy2_pitch, + uint8_t *y_dst, int y_dst_pitch, + uint8_t *uv_dst, int uv_dst_pitch, + int src_width, int src_height, + int dst_width, int dst_height, + int dst_data_size) { + + int height = (src_height > dst_height) ? dst_height : src_height; + int width = (src_width > dst_width) ? dst_width : src_width; + + int y, x; + int uv_dst_size = dst_height * uv_dst_pitch / 2; + + const uint8_t *yuy2_map = src_yuy2_map; + for(y = 0; y < height; y++) { + uint8_t *y_dst_tmp = y_dst; + const uint8_t *yuy2_src_tmp = yuy2_map; + for(x = 0; x < width / 2; x++) { + *(y_dst_tmp++ ) = *(yuy2_src_tmp++); + yuy2_src_tmp++; + *(y_dst_tmp++ ) = *(yuy2_src_tmp++); + yuy2_src_tmp++; + } + y_dst += y_dst_pitch; + yuy2_map += yuy2_pitch; + } + + yuy2_map = src_yuy2_map; + uint8_t *uv_dst_tmp = uv_dst; + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + *(uv_dst_tmp + (height*width/4) ) = (yuy2_map + (height*width/2)); + *(uv_dst_tmp + (height*width/4) + 2 ) = (yuy2_map + (height*width/2) + 2); + } + uv_dst += uv_dst_pitch / 2; + yuy2_map += yuy2_pitch; + } + +} + + +static VAStatus vaapi_software_render_frame(vo_driver_t *this_gen, vo_frame_t *frame_gen, + VAImage *va_image, VASurfaceID va_surface_id) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + void *p_base = NULL; + VAStatus vaStatus; + + if(va_image == NULL || va_image->image_id == VA_INVALID_ID || + va_surface_id == VA_INVALID_SURFACE || !va_context->valid_context) + return VA_STATUS_ERROR_UNKNOWN; + + lprintf("vaapi_software_render_frame : va_surface_id 0x%08x va_image.image_id 0x%08x width %d height %d f_width %d f_height %d sw_width %d sw_height %d\n", + va_surface_id, va_image->image_id, va_image->width, va_image->height, frame->width, frame->height, + va_context->sw_width, va_context->sw_height); + + if(frame->width != va_image->width || frame->height != va_image->height) + return VA_STATUS_SUCCESS; + + vaStatus = vaMapBuffer( va_context->va_display, va_image->buf, &p_base ) ; + if(!vaapi_check_status(va_context->driver, vaStatus, "vaMapBuffer()")) + return vaStatus; + + + uint8_t *dst[3] = { NULL, }; + uint32_t pitches[3]; + + if(this->swap_uv_planes) { + dst[0] = (uint8_t *)p_base + va_image->offsets[0]; pitches[0] = va_image->pitches[0]; + dst[1] = (uint8_t *)p_base + va_image->offsets[1]; pitches[1] = va_image->pitches[1]; + dst[2] = (uint8_t *)p_base + va_image->offsets[2]; pitches[2] = va_image->pitches[2]; + } else { + dst[0] = (uint8_t *)p_base + va_image->offsets[0]; pitches[0] = va_image->pitches[0]; + dst[1] = (uint8_t *)p_base + va_image->offsets[2]; pitches[1] = va_image->pitches[1]; + dst[2] = (uint8_t *)p_base + va_image->offsets[1]; pitches[2] = va_image->pitches[2]; + } + + /* Copy xine frames into VAAPI images */ + if(frame->format == XINE_IMGFMT_YV12) { + + if (va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("vaapi_software_render_frame yv12 -> yv12 convert\n"); + + yv12_to_yv12( + /* Y */ + frame_gen->base[0], frame_gen->pitches[0], + dst[0], pitches[0], + /* U */ + frame_gen->base[1], frame_gen->pitches[1], + dst[1], pitches[1], + /* V */ + frame_gen->base[2], frame_gen->pitches[2], + dst[2], pitches[2], + /* width x height */ + frame_gen->width, frame_gen->height); + + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' )) { + lprintf("vaapi_software_render_frame yv12 -> nv12 convert\n"); + + yv12_to_nv12(frame_gen->base[0], frame_gen->pitches[0], + frame_gen->base[1], frame_gen->pitches[1], + frame_gen->base[2], frame_gen->pitches[2], + (uint8_t *)p_base + va_image->offsets[0], va_image->pitches[0], + (uint8_t *)p_base + va_image->offsets[1], va_image->pitches[1], + frame_gen->width, frame_gen->height, + va_image->width, va_image->height, + va_image->data_size); + + } + } else if (frame->format == XINE_IMGFMT_YUY2) { + + if (va_image->format.fourcc == VA_FOURCC( 'Y', 'V', '1', '2' ) || + va_image->format.fourcc == VA_FOURCC( 'I', '4', '2', '0' ) ) { + lprintf("vaapi_software_render_frame yuy2 -> yv12 convert\n"); + + yuy2_to_yv12(frame_gen->base[0], frame_gen->pitches[0], + dst[0], pitches[0], + dst[1], pitches[1], + dst[2], pitches[2], + frame_gen->width, frame_gen->height); + + } else if (va_image->format.fourcc == VA_FOURCC( 'N', 'V', '1', '2' )) { + lprintf("vaapi_software_render_frame yuy2 -> nv12 convert\n"); + + yuy2_to_nv12(frame_gen->base[0], frame_gen->pitches[0], + (uint8_t *)p_base + va_image->offsets[0], va_image->pitches[0], + (uint8_t *)p_base + va_image->offsets[1], va_image->pitches[1], + frame_gen->width, frame_gen->height, + va_image->width, va_image->height, + va_image->data_size); + } + + } + + vaStatus = vaUnmapBuffer(va_context->va_display, va_image->buf); + if(!vaapi_check_status(this_gen, vaStatus, "vaUnmapBuffer()")) + return vaStatus; + + if(!va_context->is_bound) { + vaStatus = vaPutImage(va_context->va_display, va_surface_id, va_image->image_id, + 0, 0, va_image->width, va_image->height, + 0, 0, va_image->width, va_image->height); + if(!vaapi_check_status(va_context->driver, vaStatus, "vaPutImage()")) + return vaStatus; + } + + return VA_STATUS_SUCCESS; +} + +static VAStatus vaapi_hardware_render_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen, + VASurfaceID va_surface_id) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + ff_vaapi_context_t *va_context = this->va_context; + VAStatus vaStatus = VA_STATUS_ERROR_UNKNOWN; + int i = 0; + int interlaced_frame = !frame->vo_frame.progressive_frame; + int top_field_first = frame->vo_frame.top_field_first; + int width, height; + + if(frame->format == XINE_IMGFMT_VAAPI) { + width = va_context->width; + height = va_context->height; + } else { + width = (frame->width > va_context->sw_width) ? va_context->sw_width : frame->width; + height = (frame->height > va_context->sw_height) ? va_context->sw_height : frame->height; + } + + if(!va_context->valid_context || va_surface_id == VA_INVALID_SURFACE) + return VA_STATUS_ERROR_UNKNOWN; + + if(this->opengl_render && !this->valid_opengl_context) + return VA_STATUS_ERROR_UNKNOWN; + + /* Final VAAPI rendering. The deinterlacing can be controled by xine config.*/ + unsigned int deint = this->deinterlace; + for(i = 0; i <= !!((deint > 1) && interlaced_frame); i++) { + unsigned int flags = (deint && (interlaced_frame) ? (((!!(top_field_first)) ^ i) == 0 ? VA_BOTTOM_FIELD : VA_TOP_FIELD) : VA_FRAME_PICTURE); + + //flags |= vaapi_get_colorspace_flags(this_gen); + + flags |= VA_CLEAR_DRAWABLE; + flags |= this->scaling_level; + + lprintf("Putsrfc srfc 0x%08X flags 0x%08x %dx%d -> %dx%d interlaced %d top_field_first %d\n", + va_surface_id, flags, width, height, + this->sc.output_width, this->sc.output_height, + interlaced_frame, top_field_first); + + if(this->opengl_render) { + + vaapi_x11_trap_errors(); + + if(this->opengl_use_tfp) { + lprintf("opengl render tfp\n"); + vaStatus = vaPutSurface(va_context->va_display, va_surface_id, this->gl_image_pixmap, + 0, 0, width, height, 0, 0, width, height, NULL, 0, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaPutSurface()")) + return vaStatus; + } else { + lprintf("opengl render\n"); + vaStatus = vaCopySurfaceGLX(va_context->va_display, va_context->gl_surface, va_surface_id, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaCopySurfaceGLX()")) + return vaStatus; + } + if(vaapi_x11_untrap_errors()) + return VA_STATUS_ERROR_UNKNOWN; + + vaapi_glx_flip_page(frame_gen, 0, 0, va_context->width, va_context->height); + + } else { + + vaStatus = vaPutSurface(va_context->va_display, va_surface_id, this->window, + this->sc.displayed_xoffset, this->sc.displayed_yoffset, + this->sc.displayed_width, this->sc.displayed_height, + this->sc.output_xoffset, this->sc.output_yoffset, + this->sc.output_width, this->sc.output_height, + NULL, 0, flags); + if(!vaapi_check_status(this_gen, vaStatus, "vaPutSurface()")) + return vaStatus; + } + // workaround by johns from vdrportal.de + usleep(1 * 1000); + } + return VA_STATUS_SUCCESS; +} + +/* Used in vaapi_display_frame to determine how long displaying a frame takes + - if slower than 60fps, print a message +*/ +/* +static double timeOfDay() +{ + struct timeval t; + gettimeofday( &t, NULL ); + return ((double)t.tv_sec) + (((double)t.tv_usec)/1000000.0); +} +*/ + +static void vaapi_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + vaapi_frame_t *frame = (vaapi_frame_t *) frame_gen; + vaapi_accel_t *accel = &frame->vaapi_accel_data; + ff_vaapi_context_t *va_context = this->va_context; + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + VASurfaceID va_surface_id = VA_INVALID_SURFACE; + VAImage *va_image = NULL; + VAStatus vaStatus; + + lprintf("vaapi_display_frame\n"); + + /* + if((frame->height < 17 || frame->width < 17) && ((frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2))) { + frame->vo_frame.free( frame_gen ); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " frame size to small width %d height %d\n", frame->height, frame->width); + return; + } + */ + + /* + * let's see if this frame is different in size / aspect + * ratio from the previous one + */ + + if ( (frame->width != this->sc.delivered_width) + || (frame->height != this->sc.delivered_height) + || (frame->ratio != this->sc.delivered_ratio) + || (frame->vo_frame.crop_left != this->sc.crop_left) + || (frame->vo_frame.crop_right != this->sc.crop_right) + || (frame->vo_frame.crop_top != this->sc.crop_top) + || (frame->vo_frame.crop_bottom != this->sc.crop_bottom) ) { + lprintf("frame format changed\n"); + this->sc.force_redraw = 1; + } + + /* + * tell gui that we are about to display a frame, + * ask for offset and output size + */ + this->sc.delivered_height = frame->height; + this->sc.delivered_width = frame->width; + this->sc.delivered_ratio = frame->ratio; + + this->sc.crop_left = frame->vo_frame.crop_left; + this->sc.crop_right = frame->vo_frame.crop_right; + this->sc.crop_top = frame->vo_frame.crop_top; + this->sc.crop_bottom = frame->vo_frame.crop_bottom; + + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + lprintf("vaapi_display_frame %s frame->width %d frame->height %d va_context->sw_width %d va_context->sw_height %d valid_context %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height, va_context->sw_width, va_context->sw_height, va_context->valid_context); + + if( ((frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2)) + && ((frame->width != va_context->sw_width) ||(frame->height != va_context->sw_height )) ) { + + lprintf("vaapi_display_frame %s frame->width %d frame->height %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height); + + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + if(!va_context->valid_context) { + lprintf("vaapi_display_frame init full context\n"); + vaapi_init_internal(frame_gen->driver, SW_CONTEXT_INIT_FORMAT, frame->width, frame->height, 0); + } else { + lprintf("vaapi_display_frame init soft surfaces\n"); + vaapi_init_soft_surfaces(frame_gen->driver, frame->width, frame->height); + } + + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + } + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + + vaapi_redraw_needed (this_gen); + + /* posible race could happen while the lock is opened */ + if(!this->va_context || !this->va_context->valid_context) + return; + + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + /* initialize opengl rendering */ + if(this->opengl_render && this->init_opengl_render && va_context->valid_context) { + unsigned int last_sub_img_fmt = va_context->last_sub_image_fmt; + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, 0); + + destroy_glx(this_gen); + + vaapi_glx_config_glx(frame_gen->driver, va_context->width, va_context->height); + + vaapi_resize_glx_window(frame_gen->driver, this->sc.gui_width, this->sc.gui_height); + + if(last_sub_img_fmt) + vaapi_ovl_associate(this_gen, frame_gen->format, this->has_overlay); + + this->sc.force_redraw = 1; + this->init_opengl_render = 0; + } + + /* + double start_time; + double end_time; + double elapse_time; + int factor; + + start_time = timeOfDay(); + */ + + if(va_context->valid_context && ( (frame->format == XINE_IMGFMT_VAAPI) || (frame->format == XINE_IMGFMT_YV12) || (frame->format == XINE_IMGFMT_YUY2) )) { + + if((frame->format == XINE_IMGFMT_YUY2) || (frame->format == XINE_IMGFMT_YV12)) { + va_surface_id = va_soft_surface_ids[va_context->va_soft_head]; + va_image = &va_soft_images[va_context->va_soft_head]; + va_context->va_soft_head = (va_context->va_soft_head + 1) % (SOFT_SURFACES); + } + + if(this->guarded_render) { + if(frame->format == XINE_IMGFMT_VAAPI) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + if(va_surface->status == SURFACE_RENDER || va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface_id = va_surface->va_surface_id; + } + va_image = NULL; + } +#ifdef DEBUG_SURFACE + printf("vaapi_display_frame va_surface 0x%08x status %d index %d\n", va_surface_id, va_surface->status, accel->index); +#endif + } else { + if(frame->format == XINE_IMGFMT_VAAPI) { + va_surface_id = va_surface->va_surface_id; + va_image = NULL; + } + } + + lprintf("2: 0x%08x\n", va_surface_id); + + VASurfaceStatus surf_status = 0; + if(va_surface_id != VA_INVALID_SURFACE) { + + if(this->query_va_status) { + vaStatus = vaQuerySurfaceStatus(va_context->va_display, va_surface_id, &surf_status); + vaapi_check_status(this_gen, vaStatus, "vaQuerySurfaceStatus()"); + } else { + surf_status = VASurfaceReady; + } + + if(surf_status != VASurfaceReady) { + va_surface_id = VA_INVALID_SURFACE; + va_image = NULL; +#ifdef DEBUG_SURFACE + printf("Surface srfc 0x%08X not ready for render\n", va_surface_id); +#endif + } + } else { +#ifdef DEBUG_SURFACE + printf("Invalid srfc 0x%08X\n", va_surface_id); +#endif + } + + if(va_surface_id != VA_INVALID_SURFACE) { + + lprintf("vaapi_display_frame: 0x%08x %d %d\n", va_surface_id, va_context->width, va_context->height); + + vaStatus = vaSyncSurface(va_context->va_display, va_surface_id); + vaapi_check_status(this_gen, vaStatus, "vaSyncSurface()"); + + /* transfer image data to a VAAPI surface */ + if((frame->format == XINE_IMGFMT_YUY2 || frame->format == XINE_IMGFMT_YV12)) + vaapi_software_render_frame(this_gen, frame_gen, va_image, va_surface_id); + + vaapi_hardware_render_frame(this_gen, frame_gen, va_surface_id); + + } + } else { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " unsupported image format %s width %d height %d valid_context %d\n", + (frame->format == XINE_IMGFMT_VAAPI) ? "XINE_IMGFMT_VAAPI" : ((frame->format == XINE_IMGFMT_YV12) ? "XINE_IMGFMT_YV12" : "XINE_IMGFMT_YUY2") , + frame->width, frame->height, va_context->valid_context); + } + + XSync(this->display, False); + + //end_time = timeOfDay(); + + if(this->guarded_render) { + ff_vaapi_surface_t *va_surface = &va_render_surfaces[accel->index]; + + if(va_surface->status == SURFACE_RENDER_RELEASE) { + va_surface->status = SURFACE_FREE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_display_frame 0x%08x\n", va_surface->va_surface_id); +#endif + } else if(va_surface->status == SURFACE_RENDER) { + va_surface->status = SURFACE_RELEASE; +#ifdef DEBUG_SURFACE + printf("release_surface vaapi_display_frame 0x%08x\n", va_surface->va_surface_id); +#endif + } + } + + DO_UNLOCKDISPLAY; + + frame->vo_frame.free( frame_gen ); + + pthread_mutex_unlock(&this->vaapi_lock); + + /* + elapse_time = end_time - start_time; + factor = (int)(elapse_time/(1.0/60.0)); + + if( factor > 1 ) + { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " PutImage %dX interval (%fs)\n", factor, elapse_time ); + } + */ +} + +static int vaapi_get_property (vo_driver_t *this_gen, int property) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + switch (property) { + case VO_PROP_WINDOW_WIDTH: + this->props[property].value = this->sc.gui_width; + break; + case VO_PROP_WINDOW_HEIGHT: + this->props[property].value = this->sc.gui_height; + break; + case VO_PROP_OUTPUT_WIDTH: + this->props[property].value = this->sc.output_width; + break; + case VO_PROP_OUTPUT_HEIGHT: + this->props[property].value = this->sc.output_height; + break; + case VO_PROP_OUTPUT_XOFFSET: + this->props[property].value = this->sc.output_xoffset; + break; + case VO_PROP_OUTPUT_YOFFSET: + this->props[property].value = this->sc.output_yoffset; + break; + case VO_PROP_MAX_NUM_FRAMES: + if(!this->guarded_render) + this->props[property].value = RENDER_SURFACES; + else + this->props[property].value = 2; + break; + } + + lprintf("vaapi_get_property property=%d, value=%d\n", property, this->props[property].value ); + + return this->props[property].value; +} + +static int vaapi_set_property (vo_driver_t *this_gen, int property, int value) { + + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + lprintf("vaapi_set_property property=%d, value=%d\n", property, value ); + + if(this->props[property].atom) { + VADisplayAttribute attr; + + if((value < this->props[property].min) || (value > this->props[property].max)) + value = (this->props[property].min + this->props[property].max) >> 1; + + this->props[property].value = value; + attr.type = this->props[property].type; + attr.value = value; + + if(va_context && va_context->valid_context) { + vaSetDisplayAttributes(va_context->va_display, &attr, 1); + //vaapi_check_status((vo_driver_t *)this, vaStatus, "vaSetDisplayAttributes()"); + } + + if (this->props[property].entry) + this->props[property].entry->num_value = this->props[property].value; + + vaapi_show_display_props((vo_driver_t*)this); + + return this->props[property].value; + } else { + switch (property) { + + case VO_PROP_ASPECT_RATIO: + if (value>=XINE_VO_ASPECT_NUM_RATIOS) + value = XINE_VO_ASPECT_AUTO; + this->props[property].value = value; + this->sc.user_ratio = value; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + break; + + case VO_PROP_ZOOM_X: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + this->sc.zoom_factor_x = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + } + break; + + case VO_PROP_ZOOM_Y: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->props[property].value = value; + this->sc.zoom_factor_y = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size (&this->sc); + this->sc.force_redraw = 1; + } + break; + } + } + return value; +} + +static void vaapi_get_property_min_max (vo_driver_t *this_gen, + int property, int *min, int *max) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + *min = this->props[property].min; + *max = this->props[property].max; +} + +static int vaapi_gui_data_exchange (vo_driver_t *this_gen, + int data_type, void *data) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + lprintf("vaapi_gui_data_exchange %d\n", data_type); + + switch (data_type) { +#ifndef XINE_DISABLE_DEPRECATED_FEATURES + case XINE_GUI_SEND_COMPLETION_EVENT: + break; +#endif + + case XINE_GUI_SEND_EXPOSE_EVENT: { + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + lprintf("XINE_GUI_SEND_EXPOSE_EVENT:\n"); + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + } + break; + + case XINE_GUI_SEND_WILL_DESTROY_DRAWABLE: { + printf("XINE_GUI_SEND_WILL_DESTROY_DRAWABLE\n"); + } + break; + + case XINE_GUI_SEND_DRAWABLE_CHANGED: { + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + lprintf("XINE_GUI_SEND_DRAWABLE_CHANGED\n"); + + this->drawable = (Drawable) data; + + XReparentWindow(this->display, this->window, this->drawable, 0, 0); + + this->sc.force_redraw = 1; + this->init_opengl_render = 1; + + DO_UNLOCKDISPLAY; + pthread_mutex_unlock(&this->vaapi_lock); + } + break; + + case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO: { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + + _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y, &x1, &y1); + _x_vo_scale_translate_gui2video(&this->sc, rect->x + rect->w, rect->y + rect->h, &x2, &y2); + rect->x = x1; + rect->y = y1; + rect->w = x2-x1; + rect->h = y2-y1; + } + break; + + default: + return -1; + } + + return 0; +} + +static void vaapi_dispose (vo_driver_t *this_gen) { + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + ff_vaapi_context_t *va_context = this->va_context; + + lprintf("vaapi_dispose\n"); + + pthread_mutex_lock(&this->vaapi_lock); + DO_LOCKDISPLAY; + + this->ovl_yuv2rgb->dispose(this->ovl_yuv2rgb); + this->yuv2rgb_factory->dispose (this->yuv2rgb_factory); + + vaapi_close(this_gen); + free(va_context); + + if(this->overlay_bitmap) + free(this->overlay_bitmap); + + if(va_surface_ids) + free(va_surface_ids); + if(va_soft_surface_ids) + free(va_soft_surface_ids); + if(va_render_surfaces) + free(va_render_surfaces); + if(va_soft_images) + free(va_soft_images); + + XDestroyWindow(this->display, this->window); + DO_UNLOCKDISPLAY; + + pthread_mutex_unlock(&this->vaapi_lock); + pthread_mutex_destroy(&this->vaapi_lock); + + free (this); +} + +static void vaapi_vdr_osd_width_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->vdr_osd_width = entry->num_value; +} + +static void vaapi_vdr_osd_height_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->vdr_osd_height = entry->num_value; +} + +static void vaapi_deinterlace_flag( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->deinterlace = entry->num_value; + if(this->deinterlace > 2) + this->deinterlace = 2; +} + +static void vaapi_opengl_render( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->opengl_render = entry->num_value; +} + +static void vaapi_opengl_use_tfp( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->opengl_use_tfp = entry->num_value; +} + +static void vaapi_guarded_render( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->guarded_render = entry->num_value; +} + +static void vaapi_scaling_level( void *this_gen, xine_cfg_entry_t *entry ) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->scaling_level = entry->num_value; +} + +static void vaapi_swap_uv_planes(void *this_gen, xine_cfg_entry_t *entry) +{ + vaapi_driver_t *this = (vaapi_driver_t *) this_gen; + + this->swap_uv_planes = entry->num_value; +} + +static vo_driver_t *vaapi_open_plugin (video_driver_class_t *class_gen, const void *visual_gen) { + + vaapi_class_t *class = (vaapi_class_t *) class_gen; + x11_visual_t *visual = (x11_visual_t *) visual_gen; + vaapi_driver_t *this; + config_values_t *config = class->config; + XSetWindowAttributes xswa; + unsigned long xswa_mask; + XWindowAttributes wattr; + unsigned long black_pixel; + XVisualInfo visualInfo; + XVisualInfo *vi; + int depth; + const int x11_event_mask = ExposureMask | + StructureNotifyMask; + + this = (vaapi_driver_t *) calloc(1, sizeof(vaapi_driver_t)); + if (!this) + return NULL; + + this->config = config; + this->xine = class->xine; + + this->display = visual->display; + this->screen = visual->screen; + this->drawable = visual->d; + + this->va_context = calloc(1, sizeof(ff_vaapi_context_t)); + +#ifdef LOCKDISPLAY + guarded_display = visual->display; +#endif + + /* number of video frames from config - register it with the default value. */ + int frame_num = config->register_num (config, "engine.buffers.video_num_frames", RENDER_SURFACES, /* default */ + _("default number of video frames"), + _("The default number of video frames to request " + "from xine video out driver. Some drivers will " + "override this setting with their own values."), + 20, NULL, this); + + /* now make sure we have at least 22 frames, to prevent + * locks with vdpau_h264 */ + if(frame_num != RENDER_SURFACES) + config->update_num(config,"engine.buffers.video_num_frames", RENDER_SURFACES); + + this->opengl_render = config->register_bool( config, "video.output.vaapi_opengl_render", 0, + _("vaapi: opengl output rendering"), + _("vaapi: opengl output rendering"), + 20, vaapi_opengl_render, this ); + + this->init_opengl_render = 1; + + this->opengl_use_tfp = config->register_bool( config, "video.output.vaapi_opengl_use_tfp", 0, + _("vaapi: opengl rendering tfp"), + _("vaapi: opengl rendering tfp"), + 20, vaapi_opengl_use_tfp, this ); + + if(this->opengl_render) { + this->opengl_render = vaapi_opengl_verify_direct ((x11_visual_t *)visual_gen); + if(!this->opengl_render) + xprintf (this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Opengl indirect/software rendering does not work. Fallback to plain VAAPI output !!!!\n"); + } + + this->valid_opengl_context = 0; + this->gl_vinfo = NULL; + this->gl_pixmap = None; + this->gl_image_pixmap = None; + this->gl_texture = GL_NONE; + + this->num_frame_buffers = 0; + + va_render_surfaces = calloc(RENDER_SURFACES + 1, sizeof(ff_vaapi_surface_t)); + va_surface_ids = calloc(RENDER_SURFACES + 1, sizeof(VASurfaceID)); + va_soft_surface_ids = calloc(SOFT_SURFACES + 1, sizeof(VASurfaceID)); + va_soft_images = calloc(SOFT_SURFACES + 1, sizeof(VAImage)); + + vaapi_init_va_context(this); + vaapi_init_subpicture(this); + + _x_vo_scale_init (&this->sc, 1, 0, config ); + + this->sc.frame_output_cb = visual->frame_output_cb; + this->sc.dest_size_cb = visual->dest_size_cb; + this->sc.user_data = visual->user_data; + this->sc.user_ratio = XINE_VO_ASPECT_AUTO; + + black_pixel = BlackPixel(this->display, this->screen); + + XGetWindowAttributes(this->display, this->drawable, &wattr); + + depth = wattr.depth; + if (depth != 15 && depth != 16 && depth != 24 && depth != 32) + depth = 24; + + vi = &visualInfo; + XMatchVisualInfo(this->display, this->screen, depth, TrueColor, vi); + + xswa_mask = CWBorderPixel | CWBackPixel | CWColormap; + xswa.border_pixel = black_pixel; + xswa.background_pixel = black_pixel; + xswa.colormap = CopyFromParent; + + this->window = XCreateWindow(this->display, this->drawable, + 0, 0, 1, 1, 0, depth, + InputOutput, vi->visual, xswa_mask, &xswa); + + if(this->window == None) + return NULL; + + XSelectInput(this->display, this->window, x11_event_mask); + + XMapWindow(this->display, this->window); + vaapi_x11_wait_event(this->display, this->window, MapNotify); + + if(vi != &visualInfo) + XFree(vi); + + this->capabilities = VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CROP | VO_CAP_UNSCALED_OVERLAY | VO_CAP_ARGB_LAYER_OVERLAY | VO_CAP_VAAPI | VO_CAP_CUSTOM_EXTENT_OVERLAY; + + /* overlay converter */ + this->yuv2rgb_factory = yuv2rgb_factory_init (MODE_24_BGR, 0, NULL); + this->ovl_yuv2rgb = this->yuv2rgb_factory->create_converter( this->yuv2rgb_factory ); + + this->vo_driver.get_capabilities = vaapi_get_capabilities; + this->vo_driver.alloc_frame = vaapi_alloc_frame; + this->vo_driver.update_frame_format = vaapi_update_frame_format; + this->vo_driver.overlay_begin = vaapi_overlay_begin; + this->vo_driver.overlay_blend = vaapi_overlay_blend; + this->vo_driver.overlay_end = vaapi_overlay_end; + this->vo_driver.display_frame = vaapi_display_frame; + this->vo_driver.get_property = vaapi_get_property; + this->vo_driver.set_property = vaapi_set_property; + this->vo_driver.get_property_min_max = vaapi_get_property_min_max; + this->vo_driver.gui_data_exchange = vaapi_gui_data_exchange; + this->vo_driver.dispose = vaapi_dispose; + this->vo_driver.redraw_needed = vaapi_redraw_needed; + + this->deinterlace = 0; + this->vdr_osd_width = 0; + this->vdr_osd_height = 0; + + this->vdr_osd_width = config->register_num( config, "video.output.vaapi_vdr_osd_width", 0, + _("vaapi: VDR osd width workaround."), + _("vaapi: VDR osd width workaround."), + 10, vaapi_vdr_osd_width_flag, this ); + + this->vdr_osd_height = config->register_num( config, "video.output.vaapi_vdr_osd_height", 0, + _("vaapi: VDR osd height workaround."), + _("vaapi: VDR osd height workaround."), + 10, vaapi_vdr_osd_height_flag, this ); + + this->deinterlace = config->register_num( config, "video.output.vaapi_deinterlace", 0, + _("vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob )."), + _("vaapi: set deinterlace to 0 ( none ), 1 ( top field ), 2 ( bob )."), + 10, vaapi_deinterlace_flag, this ); + + this->guarded_render = config->register_num( config, "video.output.vaapi_guarded_render", 1, + _("vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )"), + _("vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )"), + 10, vaapi_guarded_render, this ); + + this->scaling_level_enum = config->register_enum(config, "video.output.vaapi_scaling_level", 0, + (char **)scaling_level_enum_names, + _("vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla (anamorphic)"), + _("vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla (anamorphic)"), + 10, vaapi_scaling_level, this); + + this->scaling_level = scaling_level_enum_values[this->scaling_level_enum]; + + this->swap_uv_planes = config->register_bool( config, "video.output.vaapi_swap_uv_planes", 0, + _("vaapi: swap UV planes."), + _("vaapi: this is a workaround for buggy drivers ( intel IronLake ).\n" + "There the UV planes are swapped.\n"), + 10, vaapi_swap_uv_planes, this); + + + pthread_mutex_init(&this->vaapi_lock, NULL); + + pthread_mutex_lock(&this->vaapi_lock); + + int i; + for (i = 0; i < VO_NUM_PROPERTIES; i++) { + this->props[i].value = 0; + this->props[i].min = 0; + this->props[i].max = 0; + this->props[i].atom = 0; + this->props[i].entry = NULL; + this->props[i].this = this; + } + + this->sc.user_ratio = + this->props[VO_PROP_ASPECT_RATIO].value = XINE_VO_ASPECT_AUTO; + this->props[VO_PROP_ZOOM_X].value = 100; + this->props[VO_PROP_ZOOM_Y].value = 100; + + this->va_context->last_sub_surface_id = VA_INVALID_SURFACE; + this->va_context->last_sub_image_fmt = 0; + + if(vaapi_init_internal((vo_driver_t *)this, SW_CONTEXT_INIT_FORMAT, SW_WIDTH, SW_HEIGHT, 0) != VA_STATUS_SUCCESS) { + vaapi_dispose((vo_driver_t *)this); + return NULL; + } + vaapi_close((vo_driver_t *)this); + this->va_context->valid_context = 0; + this->va_context->driver = (vo_driver_t *)this; + + pthread_mutex_unlock(&this->vaapi_lock); + + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Deinterlace : %d\n", this->deinterlace); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Render surfaces : %d\n", RENDER_SURFACES); + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE " vaapi_open: Opengl render : %d\n", this->opengl_render); + + return &this->vo_driver; +} + +/* + * class functions + */ +static void *vaapi_init_class (xine_t *xine, void *visual_gen) { + vaapi_class_t *this = (vaapi_class_t *) calloc(1, sizeof(vaapi_class_t)); + + this->driver_class.open_plugin = vaapi_open_plugin; + this->driver_class.identifier = "vaapi"; + this->driver_class.description = N_("xine video output plugin using the MIT X video extension"); + this->driver_class.dispose = default_video_driver_class_dispose; + this->config = xine->config; + this->xine = xine; + + return this; +} + +static const vo_info_t vo_info_vaapi = { + 9, /* priority */ + XINE_VISUAL_TYPE_X11 /* visual type */ +}; + +/* + * exported plugin catalog entry + */ + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_OUT, 22, "vaapi", XINE_VERSION_CODE, &vo_info_vaapi, vaapi_init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; diff --git a/src/xine-engine/accel_vaapi.h b/src/xine-engine/accel_vaapi.h new file mode 100644 index 000000000..666b23fd6 --- /dev/null +++ b/src/xine-engine/accel_vaapi.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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 2 of the License, or + * (at your option) any later version. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + * Common acceleration definitions for vdpau + * + * + */ + +#ifndef HAVE_XINE_ACCEL_VAAPI_H +#define HAVE_XINE_ACCEL_VAAPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <va/va_x11.h> +#include <pthread.h> +#ifdef HAVE_FFMPEG_AVUTIL_H +# include <avcodec.h> +#else +# include <libavcodec/avcodec.h> +#endif + +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 32) +# define AVVIDEO 2 +#else +# define AVVIDEO 1 +# define pp_context pp_context_t +# define pp_mode pp_mode_t +#endif + +#define NUM_OUTPUT_SURFACES 22 + +#define SURFACE_FREE 0 +#define SURFACE_ALOC 1 +#define SURFACE_RELEASE 2 +#define SURFACE_RENDER 3 +#define SURFACE_RENDER_RELEASE 5 + +struct vaapi_equalizer { + VADisplayAttribute brightness; + VADisplayAttribute contrast; + VADisplayAttribute hue; + VADisplayAttribute saturation; +}; + +typedef struct ff_vaapi_context_s ff_vaapi_context_t; + +struct ff_vaapi_context_s { + VADisplay va_display; + VAContextID va_context_id; + VAConfigID va_config_id; + int width; + int height; + int sw_width; + int sw_height; + int va_profile; + unsigned int va_colorspace; + VAImage va_subpic_image; + VASubpictureID va_subpic_id; + int va_subpic_width; + int va_subpic_height; + int is_bound; + void *gl_surface; + unsigned int soft_head; + unsigned int valid_context; + unsigned int va_head; + unsigned int va_soft_head; + vo_driver_t *driver; + unsigned int last_sub_image_fmt; + VASurfaceID last_sub_surface_id; + struct vaapi_equalizer va_equalizer; + VAImageFormat *va_image_formats; + int va_num_image_formats; + VAImageFormat *va_subpic_formats; + int va_num_subpic_formats; +}; + +typedef struct ff_vaapi_surface_s ff_vaapi_surface_t; +typedef struct vaapi_accel_s vaapi_accel_t; + +struct ff_vaapi_surface_s { + unsigned int index; + vaapi_accel_t *accel; + VASurfaceID va_surface_id; + unsigned int status; +}; + +struct vaapi_accel_s { + unsigned int index; + vo_frame_t *vo_frame; + +#if AVVIDEO > 1 + int (*avcodec_decode_video2)(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, AVPacket *avpkt); +#else + int (*avcodec_decode_video)(vo_frame_t *frame_gen, AVCodecContext *avctx, AVFrame *picture, + int *got_picture_ptr, uint8_t *buf, int buf_size); +#endif + VAStatus (*vaapi_init)(vo_frame_t *frame_gen, int va_profile, int width, int height, int softrender); + int (*profile_from_imgfmt)(vo_frame_t *frame_gen, enum PixelFormat pix_fmt, int codec_id, int vaapi_mpeg_sofdec); + ff_vaapi_context_t *(*get_context)(vo_frame_t *frame_gen); + int (*guarded_render)(vo_frame_t *frame_gen); + ff_vaapi_surface_t *(*get_vaapi_surface)(vo_frame_t *frame_gen); + void (*render_vaapi_surface)(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface); + void (*release_vaapi_surface)(vo_frame_t *frame_gen, ff_vaapi_surface_t *va_surface); +}; + +#ifdef __cplusplus +} +#endif + +#endif + |