From aabf5b5e103f0bebd8c5e6201688696bb3397b6e Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 3 Oct 2012 04:30:28 +0200 Subject: [PATCH] xbmc: add XVBA support and VDPAU redesign patch Signed-off-by: Stephan Raue --- ...bmc-6cf5b9d-801-xvba_support-75e6d4b.patch | 15656 ++++++++++++++++ 1 file changed, 15656 insertions(+) create mode 100644 packages/mediacenter/xbmc/patches/xbmc-6cf5b9d-801-xvba_support-75e6d4b.patch diff --git a/packages/mediacenter/xbmc/patches/xbmc-6cf5b9d-801-xvba_support-75e6d4b.patch b/packages/mediacenter/xbmc/patches/xbmc-6cf5b9d-801-xvba_support-75e6d4b.patch new file mode 100644 index 0000000000..3590cb06f8 --- /dev/null +++ b/packages/mediacenter/xbmc/patches/xbmc-6cf5b9d-801-xvba_support-75e6d4b.patch @@ -0,0 +1,15656 @@ +diff -Naur xbmc-6cf5b9d/configure.in xbmc-6cf5b9d.patch/configure.in +--- xbmc-6cf5b9d/configure.in 2012-10-03 01:08:54.000000000 +0200 ++++ xbmc-6cf5b9d.patch/configure.in 2012-10-03 03:02:11.402941766 +0200 +@@ -124,6 +124,8 @@ + vaapi_disabled="== VAAPI support manually disabled. ==" + crystalhd_not_found="== Could not find libcrystalhd. CrystalHD support disabled. ==" + crystalhd_disabled="== CrystalHD support manually disabled. ==" ++xvba_not_found="== Could not find amdxvba.h. XVBA support disabled. ==" ++xvba_disabled="== XVBA support manually disabled. ==" + vdadecoder_enabled="== VDADecoder support enabled. ==" + vdadecoder_disabled="== VDADecoder support manually disabled. ==" + vtbdecoder_enabled="== VTBDecoder support enabled. ==" +@@ -246,6 +248,12 @@ + [enable CrystalHD decoding (default is auto)])], + [use_crystalhd=$enableval], + [use_crystalhd=auto]) ++ ++AC_ARG_ENABLE([xvba], ++ [AS_HELP_STRING([--enable-xvba], ++ [enable XVBA decoding (default is auto)])], ++ [use_xvba=$enableval], ++ [use_xvba=auto]) + + AC_ARG_ENABLE([vdadecoder], + [AS_HELP_STRING([--enable-vdadecoder], +@@ -1711,6 +1719,38 @@ + USE_CRYSTALHD=0 + fi + ++# XVBA ++if test "x$use_xvba" != "xno"; then ++ if test "$host_vendor" = "apple" ; then ++ if test "x$use_xvba" = "xyes"; then ++ AC_MSG_ERROR([XVBA not supported on this platform]) ++ else ++ use_xvba="no" ++ AC_MSG_NOTICE($xvba_disabled) ++ fi ++ USE_XVBA=0 ++ else ++ initial_val=$use_xvba ++ AC_CHECK_HEADER([amd/amdxvba.h],, use_xvba=no, [#include ]) ++ ++ if test "x$use_xvba" = "xno"; then ++ if test "x$initial_val" = "xyes"; then ++ AC_MSG_ERROR($xvba_not_found) ++ else ++ AC_MSG_RESULT($xvba_not_found) ++ fi ++ USE_XVBA=0 ++ else ++ AC_DEFINE([HAVE_LIBXVBA], [1], [Define to 1 if you have the 'xvba' header (amdxvba.h)]) ++ USE_XVBA=1 ++ fi ++ fi ++else ++ AC_MSG_NOTICE($xvba_disabled) ++ USE_XVBA=0 ++fi ++ ++ + # VDADecoder + if test "x$use_vdadecoder" != "xno"; then + if test "$host_vendor" = "apple" ; then +@@ -1922,6 +1962,12 @@ + final_message="$final_message\n CrystalHD:\tNo" + fi + ++if test "x$use_xvba" != "xno"; then ++ final_message="$final_message\n XVBA:\t\tYes" ++else ++ final_message="$final_message\n XVBA:\t\tNo" ++fi ++ + if test "x$use_vdadecoder" != "xno"; then + final_message="$final_message\n VDADecoder:\tYes" + else +@@ -2391,6 +2437,7 @@ + AC_SUBST(USE_VDPAU) + AC_SUBST(USE_VAAPI) + AC_SUBST(USE_CRYSTALHD) ++AC_SUBST(USE_XVBA) + AC_SUBST(USE_LIBSMBCLIENT) + AC_SUBST(USE_LIBNFS) + AC_SUBST(USE_LIBAFPCLIENT) +@@ -2574,6 +2621,7 @@ + `if test "x$use_vdpau" != "xno"; then echo --enable-vdpau; else echo --disable-vdpau; fi` \ + `if test "x$use_vaapi" != "xno"; then echo --enable-vaapi; else echo --disable-vaapi; fi` \ + `if test "$use_optimizations" != "no"; then echo --enable-optimizations; else echo --disable-optimizations; fi` \ ++ `if test "x$use_xvba" != "xno"; then echo --enable-xvba; else echo --disable-xvba; fi` \ + --enable-protocol=http \ + --enable-pthreads \ + --enable-runtime-cpudetect \ +diff -Naur xbmc-6cf5b9d/language/English/strings.po xbmc-6cf5b9d.patch/language/English/strings.po +--- xbmc-6cf5b9d/language/English/strings.po 2012-10-03 01:10:30.000000000 +0200 ++++ xbmc-6cf5b9d.patch/language/English/strings.po 2012-10-03 03:02:11.406941846 +0200 +@@ -846,7 +846,9 @@ + msgid "Sizing: (%i,%i)->(%i,%i) (Zoom x%2.2f) AR:%2.2f:1 (Pixels: %2.2f:1) (VShift: %2.2f)" + msgstr "" + +-#empty string with id 246 ++msgctxt "#246" ++msgid "Monitor" ++msgstr "" + + msgctxt "#247" + msgid "Scripts" +@@ -4827,7 +4829,19 @@ + msgid "Allow hardware acceleration (VideoToolbox)" + msgstr "" + +-#empty strings from id 13433 to 13499 ++msgctxt "#13433" ++msgid "Allow Vdpau OpenGL interop" ++msgstr "" ++ ++msgctxt "#13434" ++msgid "Allow Vdpau OpenGL interop YUV" ++msgstr "" ++ ++msgctxt "#13435" ++msgid "Allow hardware acceleration (XVBA)" ++msgstr "" ++ ++#empty strings from id 13436 to 13499 + + msgctxt "#13500" + msgid "A/V sync method" +@@ -6016,10 +6030,14 @@ + msgstr "" + + msgctxt "#16325" +-msgid "Auto - ION Optimized" ++msgid "VDPAU - Bob" ++msgstr "" ++ ++msgctxt "#16326" ++msgid "XVBA" + msgstr "" + +-#empty strings from id 16326 to 16399 ++#empty strings from id 16327 to 16399 + + msgctxt "#16400" + msgid "Post-processing" +diff -Naur xbmc-6cf5b9d/lib/DllAvFormat.h xbmc-6cf5b9d.patch/lib/DllAvFormat.h +--- xbmc-6cf5b9d/lib/DllAvFormat.h 2012-10-03 01:09:29.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/DllAvFormat.h 2012-10-03 03:02:11.494943636 +0200 +@@ -98,6 +98,7 @@ + virtual int avformat_write_header (AVFormatContext *s, AVDictionary **options)=0; + virtual int av_write_trailer(AVFormatContext *s)=0; + virtual int av_write_frame (AVFormatContext *s, AVPacket *pkt)=0; ++ virtual int av_find_default_stream_index(AVFormatContext *s)=0; + }; + + #if (defined USE_EXTERNAL_FFMPEG) || (defined TARGET_DARWIN) +@@ -153,6 +154,7 @@ + virtual int avformat_write_header (AVFormatContext *s, AVDictionary **options) { return ::avformat_write_header (s, options); } + virtual int av_write_trailer(AVFormatContext *s) { return ::av_write_trailer(s); } + virtual int av_write_frame (AVFormatContext *s, AVPacket *pkt) { return ::av_write_frame(s, pkt); } ++ virtual int av_find_default_stream_index(AVFormatContext *s) { return ::av_find_default_stream_index(s); } + + // DLL faking. + virtual bool ResolveExports() { return true; } +@@ -209,6 +211,7 @@ + DEFINE_METHOD2(int, avformat_write_header , (AVFormatContext *p1, AVDictionary **p2)) + DEFINE_METHOD1(int, av_write_trailer, (AVFormatContext *p1)) + DEFINE_METHOD2(int, av_write_frame , (AVFormatContext *p1, AVPacket *p2)) ++ DEFINE_METHOD1(int, av_find_default_stream_index, (AVFormatContext *p1)) + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(av_register_all, av_register_all_dont_call) + RESOLVE_METHOD(av_find_input_format) +@@ -243,6 +246,7 @@ + RESOLVE_METHOD(avformat_write_header) + RESOLVE_METHOD(av_write_trailer) + RESOLVE_METHOD(av_write_frame) ++ RESOLVE_METHOD(av_find_default_stream_index) + END_METHOD_RESOLVE() + + /* dependencies of libavformat */ +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/configure xbmc-6cf5b9d.patch/lib/ffmpeg/configure +--- xbmc-6cf5b9d/lib/ffmpeg/configure 2012-10-03 01:09:55.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/configure 2012-10-03 03:02:11.387941461 +0200 +@@ -113,6 +113,7 @@ + --enable-vdpau enable VDPAU code [autodetect] + --disable-dxva2 disable DXVA2 code + --disable-vda disable VDA code ++ --disable-xvba disable XVBA code + --enable-runtime-cpudetect detect cpu capabilities at runtime (bigger binary) + --enable-hardcoded-tables use hardcoded tables instead of runtime generation + --disable-safe-bitstream-reader +@@ -1084,6 +1085,7 @@ + vaapi + vda + vdpau ++ xvba + version3 + x11grab + zlib +@@ -1423,6 +1425,7 @@ + h264_vaapi_hwaccel_select="vaapi h264_decoder" + h264_vda_hwaccel_deps="VideoDecodeAcceleration_VDADecoder_h pthreads" + h264_vda_hwaccel_select="vda h264_decoder" ++h264_xvba_hwaccel_select="xvba h264_decoder" + h264_vdpau_decoder_select="vdpau h264_decoder" + imc_decoder_select="fft mdct sinewin" + jpegls_decoder_select="golomb" +@@ -1459,6 +1462,7 @@ + mpeg4_decoder_select="h263_decoder mpeg4video_parser" + mpeg4_encoder_select="h263_encoder" + mpeg4_vaapi_hwaccel_select="vaapi mpeg4_decoder" ++mpeg2_xvba_hwaccel_select="xvba mpeg2video_decoder" + mpeg4_vdpau_decoder_select="vdpau mpeg4_decoder" + msmpeg4_crystalhd_decoder_select="crystalhd" + msmpeg4v1_decoder_select="h263_decoder" +@@ -1501,6 +1505,7 @@ + vc1_dxva2_hwaccel_deps="dxva2api_h" + vc1_dxva2_hwaccel_select="dxva2 vc1_decoder" + vc1_vaapi_hwaccel_select="vaapi vc1_decoder" ++vc1_xvba_hwaccel_select="xvba vc1_decoder" + vc1_vdpau_decoder_select="vdpau vc1_decoder" + vc1image_decoder_select="vc1_decoder" + vorbis_decoder_select="mdct" +@@ -1525,6 +1530,7 @@ + wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" + wmv3_vdpau_decoder_select="vc1_vdpau_decoder" + wmv3image_decoder_select="wmv3_decoder" ++wmv3_xvba_hwaccel_select="vc1_xvba_hwaccel" + zlib_decoder_select="zlib" + zlib_encoder_select="zlib" + zmbv_decoder_select="zlib" +@@ -1533,6 +1539,7 @@ + crystalhd_deps="libcrystalhd_libcrystalhd_if_h" + vaapi_deps="va_va_h" + vda_deps="VideoDecodeAcceleration_VDADecoder_h pthreads" ++xvba_deps="amd_amdxvba_h" + vdpau_deps="vdpau_vdpau_h vdpau_vdpau_x11_h" + + # parsers +@@ -3062,6 +3069,7 @@ + check_header termios.h + check_header vdpau/vdpau.h + check_header vdpau/vdpau_x11.h ++check_header amd/amdxvba.h + check_cpp_condition vdpau/vdpau.h "defined(VDP_DECODER_PROFILE_MPEG4_PART2_SP)" && enable vdpau_mpeg4_support + + check_header X11/extensions/XvMClib.h +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/allcodecs.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/allcodecs.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/allcodecs.c 2012-10-03 01:10:00.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/allcodecs.c 2012-10-03 03:02:11.389941501 +0200 +@@ -59,14 +59,18 @@ + REGISTER_HWACCEL (H264_VAAPI, h264_vaapi); + REGISTER_HWACCEL (H264_VDA, h264_vda); + REGISTER_HWACCEL (MPEG1_VDPAU, mpeg1_vdpau); ++ REGISTER_HWACCEL (H264_XVBA, h264_xvba); + REGISTER_HWACCEL (MPEG2_DXVA2, mpeg2_dxva2); + REGISTER_HWACCEL (MPEG2_VAAPI, mpeg2_vaapi); + REGISTER_HWACCEL (MPEG2_VDPAU, mpeg2_vdpau); + REGISTER_HWACCEL (MPEG4_VAAPI, mpeg4_vaapi); ++ REGISTER_HWACCEL (MPEG2_XVBA, mpeg2_xvba); + REGISTER_HWACCEL (VC1_DXVA2, vc1_dxva2); + REGISTER_HWACCEL (VC1_VAAPI, vc1_vaapi); ++ REGISTER_HWACCEL (VC1_XVBA, vc1_xvba); + REGISTER_HWACCEL (WMV3_DXVA2, wmv3_dxva2); + REGISTER_HWACCEL (WMV3_VAAPI, wmv3_vaapi); ++ REGISTER_HWACCEL (WMV3_XVBA, wmv3_xvba); + + /* video codecs */ + REGISTER_ENCODER (A64MULTI, a64multi); +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/h264.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/h264.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/h264.c 2012-10-03 01:10:02.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/h264.c 2012-10-03 03:02:11.391941542 +0200 +@@ -60,6 +60,7 @@ + PIX_FMT_DXVA2_VLD, + PIX_FMT_VAAPI_VLD, + PIX_FMT_VDA_VLD, ++ PIX_FMT_XVBA_VLD, + PIX_FMT_YUVJ420P, + PIX_FMT_NONE + }; +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/Makefile xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/Makefile +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/Makefile 2012-10-03 01:10:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/Makefile 2012-10-03 03:02:11.388941481 +0200 +@@ -3,7 +3,7 @@ + NAME = avcodec + FFLIBS = avutil + +-HEADERS = avcodec.h avfft.h dxva2.h opt.h vaapi.h vda.h vdpau.h version.h xvmc.h ++HEADERS = avcodec.h avfft.h dxva2.h opt.h vaapi.h vda.h vdpau.h version.h xvmc.h xvba.h + + OBJS = allcodecs.o \ + audioconvert.o \ +@@ -51,6 +51,7 @@ + OBJS-$(CONFIG_VAAPI) += vaapi.o + OBJS-$(CONFIG_VDA) += vda.o + OBJS-$(CONFIG_VDPAU) += vdpau.o ++OBJS-$(CONFIG_XVBA) += xvba.o + + # decoders/encoders/hardware accelerators + OBJS-$(CONFIG_A64MULTI_ENCODER) += a64multienc.o elbg.o +@@ -201,6 +202,7 @@ + OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o + OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o + OBJS-$(CONFIG_H264_VDA_HWACCEL) += vda_h264.o ++OBJS-$(CONFIG_H264_XVBA_HWACCEL) += xvba_h264.o + OBJS-$(CONFIG_HUFFYUV_DECODER) += huffyuv.o + OBJS-$(CONFIG_HUFFYUV_ENCODER) += huffyuv.o + OBJS-$(CONFIG_IDCIN_DECODER) += idcinvideo.o +@@ -284,6 +286,7 @@ + mpegvideo.o error_resilience.o + OBJS-$(CONFIG_MPEG2_DXVA2_HWACCEL) += dxva2_mpeg2.o + OBJS-$(CONFIG_MPEG2_VAAPI_HWACCEL) += vaapi_mpeg2.o ++OBJS-$(CONFIG_MPEG2_XVBA_HWACCEL) += xvba_mpeg2.o + OBJS-$(CONFIG_MPEG2VIDEO_DECODER) += mpeg12.o mpeg12data.o \ + mpegvideo.o error_resilience.o + OBJS-$(CONFIG_MPEG2VIDEO_ENCODER) += mpeg12enc.o mpegvideo_enc.o \ +@@ -431,6 +434,7 @@ + intrax8.o intrax8dsp.o + OBJS-$(CONFIG_VC1_DXVA2_HWACCEL) += dxva2_vc1.o + OBJS-$(CONFIG_VC1_VAAPI_HWACCEL) += vaapi_vc1.o ++OBJS-$(CONFIG_VC1_XVBA_HWACCEL) += xvba_vc1.o + OBJS-$(CONFIG_VCR1_DECODER) += vcr1.o + OBJS-$(CONFIG_VCR1_ENCODER) += vcr1.o + OBJS-$(CONFIG_VMDAUDIO_DECODER) += vmdav.o +@@ -732,6 +736,7 @@ + SKIPHEADERS-$(CONFIG_LIBSCHROEDINGER) += libschroedinger.h + SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_internal.h + SKIPHEADERS-$(CONFIG_VDA) += vda_internal.h ++SKIPHEADERS-$(CONFIG_XVBA) += xvba_internal.h + SKIPHEADERS-$(CONFIG_VDPAU) += vdpau.h + SKIPHEADERS-$(CONFIG_XVMC) += xvmc.h + SKIPHEADERS-$(HAVE_W32THREADS) += w32pthreads.h +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/mpegvideo.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/mpegvideo.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/mpegvideo.c 2012-10-03 01:10:15.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/mpegvideo.c 2012-10-03 03:02:11.394941602 +0200 +@@ -136,6 +136,7 @@ + PIX_FMT_DXVA2_VLD, + PIX_FMT_VAAPI_VLD, + PIX_FMT_VDA_VLD, ++ PIX_FMT_XVBA_VLD, + PIX_FMT_YUV420P, + PIX_FMT_NONE + }; +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba.c 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba.c 2012-10-03 03:02:11.394941602 +0200 +@@ -0,0 +1,66 @@ ++/* ++ * HW decode acceleration for MPEG-2, H.264 and VC-1 ++ * ++ * Copyright (C) 2005-2011 Team XBMC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++/** ++ * \addtogroup XVBA_Decoding ++ * ++ * @{ ++ */ ++ ++#include ++#include "xvba.h" ++#include "xvba_internal.h" ++#include "avcodec.h" ++ ++int ff_xvba_translate_profile(int profile) { ++ ++ if (profile == 66) ++ return 1; ++ else if (profile == 77) ++ return 2; ++ else if (profile == 100) ++ return 3; ++ else if (profile == 0) ++ return 4; ++ else if (profile == 1) ++ return 5; ++ else if (profile == 3) ++ return 6; ++ else ++ return -1; ++} ++ ++void ff_xvba_add_slice_data(struct xvba_render_state *render, const uint8_t *buffer, uint32_t size) { ++ ++ render->buffers = av_fast_realloc( ++ render->buffers, ++ &render->buffers_alllocated, ++ sizeof(struct xvba_bitstream_buffers)*(render->num_slices + 1) ++ ); ++ ++ render->buffers[render->num_slices].buffer = buffer; ++ render->buffers[render->num_slices].size = size; ++ ++ render->num_slices++; ++} ++ +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba.h xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba.h +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba.h 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba.h 2012-10-03 03:02:11.506943879 +0200 +@@ -0,0 +1,71 @@ ++/* ++ * HW decode acceleration for MPEG-2, H.264 and VC-1 ++ * ++ * Copyright (C) 2005-2011 Team XBMC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVCODEC_XVBA_H ++#define AVCODEC_XVBA_H ++ ++#include ++#include ++#include ++ ++ ++/** ++ * \defgroup XVBA_Decoding VA API Decoding ++ * \ingroup Decoder ++ * @{ ++ */ ++ ++/** \brief The videoSurface is used for rendering. */ ++#define FF_XVBA_STATE_USED_FOR_RENDER 1 ++ ++/** ++ * \brief The videoSurface is needed for reference/prediction. ++ * The codec manipulates this. ++ */ ++#define FF_XVBA_STATE_USED_FOR_REFERENCE 2 ++ ++/** ++ * \brief The videoSurface holds a decoded frame. ++ * The codec manipulates this. ++ */ ++#define FF_XVBA_STATE_DECODED 4 ++ ++/* @} */ ++ ++struct xvba_bitstream_buffers ++{ ++ const void *buffer; ++ unsigned int size; ++}; ++ ++struct xvba_render_state { ++ ++ int state; ///< Holds FF_XVBA_STATE_* values. ++ void *surface; ++ XVBAPictureDescriptor *picture_descriptor; ++ XVBAQuantMatrixAvc *iq_matrix; ++ unsigned int num_slices; ++ struct xvba_bitstream_buffers *buffers; ++ uint32_t buffers_alllocated; ++}; ++ ++#endif /* AVCODEC_XVBA_H */ +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_h264.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_h264.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_h264.c 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_h264.c 2012-10-03 03:02:11.396941644 +0200 +@@ -0,0 +1,189 @@ ++/* ++ * H.264 HW decode acceleration through XVBA ++ * ++ * Copyright (C) 2005-2011 Team XBMC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "xvba.h" ++#include "xvba_internal.h" ++#include "h264.h" ++#include ++ ++/** @file ++ * This file implements the glue code between FFmpeg's and XvBA API's ++ * structures for H.264 decoding. ++ */ ++ ++ ++/** Initialize and start decoding a frame with XVBA. */ ++static int start_frame(AVCodecContext *avctx, ++ av_unused const uint8_t *buffer, ++ av_unused uint32_t size) ++{ ++ H264Context * const h = avctx->priv_data; ++ MpegEncContext * const s = &h->s; ++ struct xvba_render_state *render; ++ XVBAPictureDescriptor *pic_descriptor; ++ int i; ++ ++ render = (struct xvba_render_state *)s->current_picture_ptr->f.data[0]; ++ assert(render); ++ ++ if (render->picture_descriptor == 0) ++ return -1; ++ ++ pic_descriptor = render->picture_descriptor; ++ ++ for (i = 0; i < 2; ++i) { ++ int foc = s->current_picture_ptr->field_poc[i]; ++ if (foc == INT_MAX) ++ foc = 0; ++ pic_descriptor->avc_curr_field_order_cnt_list[i] = foc; ++ } ++ ++ pic_descriptor->avc_frame_num = h->frame_num; ++ ++ render->num_slices = 0; ++ ++ return 0; ++} ++ ++/** End a hardware decoding based frame. */ ++static int end_frame(AVCodecContext *avctx) ++{ ++ H264Context * const h = avctx->priv_data; ++ MpegEncContext * const s = &h->s; ++ struct xvba_render_state *render; ++ XVBAPictureDescriptor *pic_descriptor; ++ XVBAQuantMatrixAvc *iq_matrix; ++ ++ render = (struct xvba_render_state *)s->current_picture_ptr->f.data[0]; ++ assert(render); ++ ++ if (render->picture_descriptor == 0 || render->iq_matrix == 0) ++ return -1; ++ ++ pic_descriptor = render->picture_descriptor; ++ iq_matrix = render->iq_matrix; ++ ++ av_dlog(avctx, "end_frame()\n"); ++ ++ /* Fill in Picture Parameters*/ ++ pic_descriptor->profile = ff_xvba_translate_profile(avctx->profile); ++ pic_descriptor->level = avctx->level; ++ pic_descriptor->width_in_mb = s->mb_width; ++ pic_descriptor->height_in_mb = s->mb_height; ++ pic_descriptor->picture_structure = s->picture_structure; ++ pic_descriptor->chroma_format = s->chroma_format ? s->chroma_format : 1; ++ pic_descriptor->avc_intra_flag = (h->slice_type == AV_PICTURE_TYPE_I) ? 1 : 0; ++ pic_descriptor->avc_reference = (s->current_picture_ptr->f.reference & 3) ? 1 : 0; ++ ++ pic_descriptor->avc_bit_depth_luma_minus8 = h->sps.bit_depth_luma - 8; ++ pic_descriptor->avc_bit_depth_chroma_minus8 = h->sps.bit_depth_chroma - 8; ++ pic_descriptor->avc_log2_max_frame_num_minus4 = h->sps.log2_max_frame_num -4; ++ pic_descriptor->avc_pic_order_cnt_type = h->sps.poc_type; ++ pic_descriptor->avc_log2_max_pic_order_cnt_lsb_minus4 = h->sps.log2_max_poc_lsb - 4; ++ pic_descriptor->avc_num_ref_frames = h->sps.ref_frame_count; ++ pic_descriptor->avc_reserved_8bit = 0; ++ ++ /* Set correct level */ ++ if (pic_descriptor->level == 41) { ++ const unsigned int mbw = pic_descriptor->width_in_mb; ++ const unsigned int mbh = pic_descriptor->height_in_mb; ++ const unsigned int max_ref_frames = 12288 * 1024 / (mbw * mbh * 384); ++ const unsigned int num_ref_frames = pic_descriptor->avc_num_ref_frames; ++ if (max_ref_frames < num_ref_frames) ++ pic_descriptor->level = 51; ++ } ++ ++ pic_descriptor->avc_num_slice_groups_minus1 = h->pps.slice_group_count - 1; ++ pic_descriptor->avc_num_ref_idx_l0_active_minus1 = h->pps.ref_count[0] - 1; ++ pic_descriptor->avc_num_ref_idx_l1_active_minus1 = h->pps.ref_count[1] - 1; ++ ++ pic_descriptor->avc_pic_init_qp_minus26 = h->pps.init_qp - 26; ++ pic_descriptor->avc_pic_init_qs_minus26 = h->pps.init_qs - 26; ++ pic_descriptor->avc_chroma_qp_index_offset = h->pps.chroma_qp_index_offset[0]; ++ pic_descriptor->avc_second_chroma_qp_index_offset = h->pps.chroma_qp_index_offset[1]; ++ pic_descriptor->avc_slice_group_change_rate_minus1 = 0; // not implemented in ffmpeg ++ pic_descriptor->avc_reserved_16bit = 0; // must be 0 ++ memset(pic_descriptor->avc_field_order_cnt_list,0,sizeof(pic_descriptor->avc_field_order_cnt_list)); // must be 0 ++ memset(pic_descriptor->avc_slice_group_map,0,sizeof(pic_descriptor->avc_slice_group_map)); // must be 0 ++ ++ // sps ++ pic_descriptor->sps_info.avc.delta_pic_always_zero_flag = h->sps.delta_pic_order_always_zero_flag; ++ pic_descriptor->sps_info.avc.direct_8x8_inference_flag = h->sps.direct_8x8_inference_flag; ++ pic_descriptor->sps_info.avc.frame_mbs_only_flag = h->sps.frame_mbs_only_flag; ++ pic_descriptor->sps_info.avc.gaps_in_frame_num_value_allowed_flag = h->sps.gaps_in_frame_num_allowed_flag; ++ pic_descriptor->sps_info.avc.mb_adaptive_frame_field_flag = h->sps.mb_aff; ++ pic_descriptor->sps_info.avc.residual_colour_transform_flag = h->sps.residual_color_transform_flag; ++ pic_descriptor->sps_info.avc.xvba_avc_sps_reserved = 0; ++ ++ // pps ++ pic_descriptor->pps_info.avc.entropy_coding_mode_flag = h->pps.cabac; ++ pic_descriptor->pps_info.avc.pic_order_present_flag = h->pps.pic_order_present; ++ pic_descriptor->pps_info.avc.weighted_pred_flag = h->pps.weighted_pred; ++ pic_descriptor->pps_info.avc.weighted_bipred_idc = h->pps.weighted_bipred_idc; ++ pic_descriptor->pps_info.avc.deblocking_filter_control_present_flag = h->pps.deblocking_filter_parameters_present; ++ pic_descriptor->pps_info.avc.constrained_intra_pred_flag = h->pps.constrained_intra_pred; ++ pic_descriptor->pps_info.avc.redundant_pic_cnt_present_flag = h->pps.redundant_pic_cnt_present; ++ pic_descriptor->pps_info.avc.transform_8x8_mode_flag = h->pps.transform_8x8_mode; ++ pic_descriptor->pps_info.avc.xvba_avc_pps_reserved = 0; // must be 0 ++ ++ memcpy(iq_matrix->bScalingLists4x4, h->pps.scaling_matrix4, sizeof(iq_matrix->bScalingLists4x4)); ++ memcpy(iq_matrix->bScalingLists8x8[0], h->pps.scaling_matrix8[0], sizeof(iq_matrix->bScalingLists8x8[0])); ++ memcpy(iq_matrix->bScalingLists8x8[1], h->pps.scaling_matrix8[3], sizeof(iq_matrix->bScalingLists8x8[0])); ++ ++ // Wait for an I-frame before start decoding. Workaround for ATI UVD and UVD+ GPUs ++ if (!h->got_first_iframe) { ++ if (h->slice_type != AV_PICTURE_TYPE_I && h->slice_type != AV_PICTURE_TYPE_SI) ++ return -1; ++ h->got_first_iframe = 1; ++ } ++ ++ ff_draw_horiz_band(s, 0, s->avctx->height); ++ ++ return 0; ++} ++ ++/** Decode the given H.264 slice with XVBA. */ ++static int decode_slice(AVCodecContext *avctx, ++ const uint8_t *buffer, ++ uint32_t size) ++{ ++ H264Context * const h = avctx->priv_data; ++ MpegEncContext * const s = &h->s; ++ struct xvba_render_state *render; ++ ++ render = (struct xvba_render_state *)s->current_picture_ptr->f.data[0]; ++ assert(render); ++ ++ ff_xvba_add_slice_data(render, buffer, size); ++ ++ return 0; ++} ++ ++AVHWAccel ff_h264_xvba_hwaccel = { ++ .name = "h264_xvba", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .id = CODEC_ID_H264, ++ .pix_fmt = PIX_FMT_XVBA_VLD, ++ .start_frame = start_frame, ++ .end_frame = end_frame, ++ .decode_slice = decode_slice, ++}; +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_internal.h xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_internal.h +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_internal.h 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_internal.h 2012-10-03 03:02:11.396941644 +0200 +@@ -0,0 +1,24 @@ ++/* ++ * HW decode acceleration for MPEG-2, H.264 and VC-1 ++ * ++ * Copyright (C) 2005-2011 Team XBMC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++int ff_xvba_translate_profile(int profile); ++void ff_xvba_add_slice_data(struct xvba_render_state *render, const uint8_t *buffer, uint32_t size); +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_mpeg2.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_mpeg2.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_mpeg2.c 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_mpeg2.c 2012-10-03 03:02:11.397941665 +0200 +@@ -0,0 +1,52 @@ ++/* ++ * MPEG-2 HW decode acceleration through XVBA ++ * ++ * Copyright (C) 2005-2011 Team XBMC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "dsputil.h" ++ ++static int start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) ++{ ++ struct MpegEncContext * const s = avctx->priv_data; ++ return 0; ++} ++ ++static int end_frame(AVCodecContext *avctx) ++{ ++ return 0; ++} ++ ++static int decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) ++{ ++ struct MpegEncContext * const s = avctx->priv_data; ++ return 0; ++} ++ ++AVHWAccel ff_mpeg2_xvba_hwaccel = { ++ .name = "mpeg2_xvba", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .id = CODEC_ID_MPEG2VIDEO, ++ .pix_fmt = PIX_FMT_XVBA_VLD, ++ .capabilities = 0, ++ .start_frame = start_frame, ++ .end_frame = end_frame, ++ .decode_slice = decode_slice, ++ .priv_data_size = 0, ++}; +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_vc1.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_vc1.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvba_vc1.c 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvba_vc1.c 2012-10-03 03:02:11.398941685 +0200 +@@ -0,0 +1,190 @@ ++/* ++ * VC-1 HW decode acceleration through XVBA ++ * ++ * Copyright (C) 2005-2011 Team XBMC ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "xvba.h" ++#include "xvba_internal.h" ++#include "vc1.h" ++#include "vc1data.h" ++#include ++ ++ ++/** @file ++ * Implement structures of ffmpeg <-> XvBA ++ */ ++ ++/* Initialize and start decoding a frame with XvBA */ ++static int start_frame(AVCodecContext *avctx, ++ av_unused const uint8_t *buffer, ++ av_unused uint32_t size) ++{ ++ VC1Context * const v = avctx->priv_data; ++ MpegEncContext * const s = &v->s; ++ struct xvba_render_state *render; ++ ++ render = (struct xvba_render_state *)s->current_picture_ptr->f.data[0]; ++ assert(render); ++ ++ render->num_slices = 0; ++ return 0; ++} ++ ++/* End a hardware decoding based frame */ ++static int end_frame(AVCodecContext *avctx) ++{ ++ VC1Context* const v = avctx->priv_data; ++ MpegEncContext* const s = &v->s; ++ struct xvba_render_state *render, *last, *next; ++ XVBAPictureDescriptor *pic_descriptor; ++ ++ render = (struct xvba_render_state *)s->current_picture_ptr->f.data[0]; ++ assert(render); ++ ++ if (render->picture_descriptor == 0) ++ return -1; ++ ++ pic_descriptor = render->picture_descriptor; ++ ++ av_dlog(avctx, "xvba_vc1_end_frame()\n"); ++ ++ memset(pic_descriptor, 0, sizeof(*pic_descriptor)); ++ ++ /* Fill in Parameters - for reference see AMD sdk documentation */ ++ pic_descriptor->profile = ff_xvba_translate_profile(v->profile); ++ pic_descriptor->level = v->level; ++ //done like in va-driver and vaapi ++ if (v->profile == PROFILE_ADVANCED) { ++ pic_descriptor->width_in_mb = s->avctx->coded_width; ++ pic_descriptor->height_in_mb = s->avctx->coded_height; ++ } else { ++ pic_descriptor->width_in_mb = s->mb_width; ++ pic_descriptor->height_in_mb = s->mb_height; ++ } ++ pic_descriptor->picture_structure = s->picture_structure; ++ // xvba-video set this to 1 only 4:2:0 supported ++ // doc says: if not set, choose 1 - we try this ++ pic_descriptor->chroma_format = 1; ++ pic_descriptor->avc_intra_flag = s->pict_type == AV_PICTURE_TYPE_I || v->bi_type == 1; ++ pic_descriptor->avc_reference = (s->current_picture_ptr->f.reference & 3) ? 1 : 0; ++ ++ // VC-1 explicit parameters see page 30 of sdk ++ // sps_info ++ pic_descriptor->sps_info.vc1.postprocflag = v->postprocflag; ++ ++ // done as in vaapi ++ pic_descriptor->sps_info.vc1.pulldown = v->broadcast; ++ pic_descriptor->sps_info.vc1.interlace = v->interlace; ++ pic_descriptor->sps_info.vc1.tfcntrflag = v->tfcntrflag; ++ pic_descriptor->sps_info.vc1.finterpflag = v->finterpflag; ++ pic_descriptor->sps_info.vc1.reserved = 1; ++ // eventually check if this makes sense together with interlace ++ pic_descriptor->sps_info.vc1.psf = v->psf; ++ // what about if it is a frame (page 31) ++ // looked at xvba-driver ++ pic_descriptor->sps_info.vc1.second_field = !s->first_field; ++ pic_descriptor->sps_info.vc1.xvba_vc1_sps_reserved = 0; ++ ++ // VC-1 explicit parameters see page 30 of sdk ++ // pps_info ++ pic_descriptor->pps_info.vc1.panscan_flag = v->panscanflag; ++ pic_descriptor->pps_info.vc1.refdist_flag = v->refdist_flag; ++ pic_descriptor->pps_info.vc1.loopfilter = s->loop_filter; ++ pic_descriptor->pps_info.vc1.fastuvmc = v->fastuvmc; ++ pic_descriptor->pps_info.vc1.extended_mv = v->extended_mv; ++ pic_descriptor->pps_info.vc1.dquant = v->dquant; ++ pic_descriptor->pps_info.vc1.vstransform = v->vstransform; ++ pic_descriptor->pps_info.vc1.overlap = v->overlap; ++ pic_descriptor->pps_info.vc1.quantizer = v->quantizer_mode; ++ pic_descriptor->pps_info.vc1.extended_dmv = v->extended_dmv; ++ pic_descriptor->pps_info.vc1.maxbframes = s->avctx->max_b_frames; ++ pic_descriptor->pps_info.vc1.rangered = (pic_descriptor->profile == PROFILE_SIMPLE) ? 0 : v->rangered; ++ pic_descriptor->pps_info.vc1.syncmarker = (pic_descriptor->profile == PROFILE_SIMPLE) ? 0 : s->resync_marker; ++ pic_descriptor->pps_info.vc1.multires = v->multires; ++ pic_descriptor->pps_info.vc1.reserved = 1; ++ pic_descriptor->pps_info.vc1.range_mapy_flag = v->range_mapy_flag; ++ pic_descriptor->pps_info.vc1.range_mapy = v->range_mapy; ++ pic_descriptor->pps_info.vc1.range_mapuv_flag = v->range_mapuv_flag; ++ pic_descriptor->pps_info.vc1.range_mapuv = v->range_mapuv; ++ pic_descriptor->pps_info.vc1.xvba_vc1_pps_reserved = 0; ++ ++ pic_descriptor->past_surface = 0; ++ pic_descriptor->future_surface = 0; ++ switch (s->pict_type) { ++ case AV_PICTURE_TYPE_B: ++ next = (struct xvba_render_state *)s->next_picture.f.data[0]; ++ assert(next); ++ if (next) ++ pic_descriptor->past_surface = next->surface; ++ // fall-through ++ case AV_PICTURE_TYPE_P: ++ last = (struct xvba_render_state *)s->last_picture.f.data[0]; ++ assert(last); ++ if (last) ++ pic_descriptor->future_surface = last->surface; ++ break; ++ } ++ ++ ff_draw_horiz_band(s, 0, s->avctx->height); ++ ++ return 0; ++} ++ ++static int decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) ++{ ++ VC1Context* const v = avctx->priv_data; ++ MpegEncContext* const s = &v->s; ++ struct xvba_render_state *render; ++ ++ render = (struct xvba_render_state *)s->current_picture_ptr->f.data[0]; ++ assert(render); ++ ++ if (avctx->codec_id == CODEC_ID_VC1 && ++ size >= 4 && IS_MARKER(AV_RB32(buffer))) { ++ buffer += 4; ++ size -= 4; ++ } ++ ++ ff_xvba_add_slice_data(render, buffer, size); ++ ++ return 0; ++} ++ ++#if CONFIG_WMV3_XVBA_HWACCEL ++AVHWAccel ff_wmv3_xvba_hwaccel = { ++ .name = "wmv3_xvba", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .id = CODEC_ID_WMV3, ++ .pix_fmt = PIX_FMT_XVBA_VLD, ++ .start_frame = start_frame, ++ .end_frame = end_frame, ++ .decode_slice = decode_slice, ++}; ++#endif ++ ++AVHWAccel ff_vc1_xvba_hwaccel = { ++ .name = "vc1_xvba", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .id = CODEC_ID_VC1, ++ .pix_fmt = PIX_FMT_XVBA_VLD, ++ .start_frame = start_frame, ++ .end_frame = end_frame, ++ .decode_slice = decode_slice, ++}; +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvmc_internal.h xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvmc_internal.h +--- xbmc-6cf5b9d/lib/ffmpeg/libavcodec/xvmc_internal.h 2012-10-03 01:10:03.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavcodec/xvmc_internal.h 2012-10-03 03:02:11.398941685 +0200 +@@ -1,5 +1,7 @@ + /* +- * XVideo Motion Compensation internal functions ++ * HW decode acceleration for MPEG-2, H.264 and VC-1 ++ * ++ * Copyright (C) 2005-2011 Team XBMC + * + * This file is part of FFmpeg. + * +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavutil/pixdesc.c xbmc-6cf5b9d.patch/lib/ffmpeg/libavutil/pixdesc.c +--- xbmc-6cf5b9d/lib/ffmpeg/libavutil/pixdesc.c 2012-10-03 01:09:56.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavutil/pixdesc.c 2012-10-03 03:02:11.399941705 +0200 +@@ -874,6 +874,12 @@ + .log2_chroma_h = 1, + .flags = PIX_FMT_HWACCEL, + }, ++ [PIX_FMT_XVBA_VLD] = { ++ .name = "xvba_vld", ++ .log2_chroma_w = 1, ++ .log2_chroma_h = 1, ++ .flags = PIX_FMT_HWACCEL, ++ }, + [PIX_FMT_YUV420P9LE] = { + .name = "yuv420p9le", + .nb_components = 3, +diff -Naur xbmc-6cf5b9d/lib/ffmpeg/libavutil/pixfmt.h xbmc-6cf5b9d.patch/lib/ffmpeg/libavutil/pixfmt.h +--- xbmc-6cf5b9d/lib/ffmpeg/libavutil/pixfmt.h 2012-10-03 01:09:56.000000000 +0200 ++++ xbmc-6cf5b9d.patch/lib/ffmpeg/libavutil/pixfmt.h 2012-10-03 03:02:11.400941725 +0200 +@@ -129,6 +129,7 @@ + PIX_FMT_YUV444P16BE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian + PIX_FMT_VDPAU_MPEG4, ///< MPEG4 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + PIX_FMT_DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer ++ PIX_FMT_XVBA_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a vaapi_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + + PIX_FMT_RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), little-endian, most significant bits to 0 + PIX_FMT_RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4A 4R 4G 4B(lsb), big-endian, most significant bits to 0 +diff -Naur xbmc-6cf5b9d/system/shaders/yuv2rgb_basic.glsl xbmc-6cf5b9d.patch/system/shaders/yuv2rgb_basic.glsl +--- xbmc-6cf5b9d/system/shaders/yuv2rgb_basic.glsl 2012-10-03 01:09:09.000000000 +0200 ++++ xbmc-6cf5b9d.patch/system/shaders/yuv2rgb_basic.glsl 2012-10-03 03:02:11.294939571 +0200 +@@ -70,6 +70,18 @@ + rgb.a = gl_Color.a; + gl_FragColor = rgb; + ++#elif defined(XBMC_VDPAU_NV12) ++ ++ vec4 yuv, rgb; ++ yuv.rgba = vec4( texture2D(m_sampY, stretch(m_cordY)).r ++ , texture2D(m_sampU, stretch(m_cordU)).r ++ , texture2D(m_sampV, stretch(m_cordV)).g ++ , 1.0 ); ++ ++ rgb = m_yuvmat * yuv; ++ rgb.a = gl_Color.a; ++ gl_FragColor = rgb; ++ + #elif defined(XBMC_YUY2) || defined(XBMC_UYVY) + + #if(XBMC_texture_rectangle) +diff -Naur xbmc-6cf5b9d/xbmc/Application.cpp xbmc-6cf5b9d.patch/xbmc/Application.cpp +--- xbmc-6cf5b9d/xbmc/Application.cpp 2012-10-03 01:09:24.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/Application.cpp 2012-10-03 03:02:11.438942497 +0200 +@@ -424,8 +424,6 @@ + #endif + m_currentStack = new CFileItemList; + +- m_frameCount = 0; +- + m_bPresentFrame = false; + m_bPlatformDirectories = true; + +@@ -794,7 +792,7 @@ + + uint32_t sdlFlags = 0; + +-#if defined(HAS_SDL_OPENGL) || (HAS_GLES == 2) ++#if (defined(HAS_SDL_OPENGL) || (HAS_GLES == 2)) && !defined(HAS_GLX) + sdlFlags |= SDL_INIT_VIDEO; + #endif + +@@ -2233,28 +2231,18 @@ + + bool CApplication::WaitFrame(unsigned int timeout) + { +- bool done = false; +- + // Wait for all other frames to be presented +- CSingleLock lock(m_frameMutex); +- //wait until event is set, but modify remaining time ++ m_frameEvent.Reset(); + +- TightConditionVariable > cv(m_frameCond, InversePredicate(m_frameCount)); +- cv.wait(lock,timeout); +- done = m_frameCount == 0; ++ if (!g_renderManager.HasFrame() && !m_frameEvent.WaitMSec(timeout)) ++ return false; + +- return done; ++ return g_renderManager.HasFrame(); + } + + void CApplication::NewFrame() + { +- // We just posted another frame. Keep track and notify. +- { +- CSingleLock lock(m_frameMutex); +- m_frameCount++; +- } +- +- m_frameCond.notifyAll(); ++ m_frameEvent.Set(); + } + + void CApplication::Render() +@@ -2288,12 +2276,13 @@ + m_bPresentFrame = false; + if (!extPlayerActive && g_graphicsContext.IsFullScreenVideo() && !IsPaused()) + { +- CSingleLock lock(m_frameMutex); +- +- TightConditionVariable cv(m_frameCond,m_frameCount); +- cv.wait(lock,100); ++ uint64_t timeout = CurrentHostCounter() + CurrentHostFrequency()/10; ++ while (!g_renderManager.HasFrame() && CurrentHostCounter() < timeout) ++ { ++ m_frameEvent.WaitMSec(100); ++ } ++ m_bPresentFrame = g_renderManager.HasFrame(); + +- m_bPresentFrame = m_frameCount > 0; + decrement = m_bPresentFrame; + hasRendered = true; + } +@@ -2342,8 +2331,6 @@ + + g_Windowing.EndRender(); + +- g_TextureManager.FreeUnusedTextures(); +- + // reset our info cache - we do this at the end of Render so that it is + // fresh for the next process(), or after a windowclose animation (where process() + // isn't called) +@@ -2375,18 +2362,16 @@ + m_lastFrameTime = XbmcThreads::SystemClockMillis(); + + if (flip) ++ { + g_graphicsContext.Flip(dirtyRegions); ++ g_renderManager.NotifyDisplayFlip(); ++ } + CTimeUtils::UpdateFrameTime(flip); + ++ g_TextureManager.FreeUnusedTextures(); ++ + g_renderManager.UpdateResolution(); + g_renderManager.ManageCaptures(); +- +- { +- CSingleLock lock(m_frameMutex); +- if(m_frameCount > 0 && decrement) +- m_frameCount--; +- } +- m_frameCond.notifyAll(); + } + + void CApplication::SetStandAlone(bool value) +@@ -5785,12 +5770,6 @@ + // See if we're playing a video, and are in GUI mode + if ( IsPlayingVideo() && g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO) + { +- // Reset frame count so that timing is FPS will be correct. +- { +- CSingleLock lock(m_frameMutex); +- m_frameCount = 0; +- } +- + // then switch to fullscreen mode + g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO); + return true; +@@ -6069,7 +6048,6 @@ + + bool CApplication::IsPresentFrame() + { +- CSingleLock lock(m_frameMutex); + bool ret = m_bPresentFrame; + + return ret; +diff -Naur xbmc-6cf5b9d/xbmc/Application.h xbmc-6cf5b9d.patch/xbmc/Application.h +--- xbmc-6cf5b9d/xbmc/Application.h 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/Application.h 2012-10-03 03:02:11.365941014 +0200 +@@ -433,10 +433,8 @@ + bool m_bEnableLegacyRes; + bool m_bTestMode; + bool m_bSystemScreenSaverEnable; +- +- int m_frameCount; +- CCriticalSection m_frameMutex; +- XbmcThreads::ConditionVariable m_frameCond; ++ ++ CEvent m_frameEvent; + + VIDEO::CVideoInfoScanner *m_videoInfoScanner; + MUSIC_INFO::CMusicInfoScanner *m_musicInfoScanner; +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDAudio.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDAudio.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDAudio.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDAudio.cpp 2012-10-03 03:02:11.237938412 +0200 +@@ -30,6 +30,68 @@ + + using namespace std; + ++ ++CPTSOutputQueue::CPTSOutputQueue() ++{ ++ Flush(); ++} ++ ++void CPTSOutputQueue::Add(double pts, double delay, double duration) ++{ ++ CSingleLock lock(m_sync); ++ ++ // don't accept a re-add, since that would cause time moving back ++ double last = m_queue.empty() ? m_current.pts : m_queue.back().pts; ++ if(last == pts) ++ return; ++ ++ TPTSItem item; ++ item.pts = pts; ++ item.timestamp = CDVDClock::GetAbsoluteClock() + delay; ++ item.duration = duration; ++ ++ // first one is applied directly ++ if(m_queue.empty() && m_current.pts == DVD_NOPTS_VALUE) ++ m_current = item; ++ else ++ m_queue.push(item); ++ ++ // call function to make sure the queue ++ // doesn't grow should nobody call it ++ Current(); ++} ++void CPTSOutputQueue::Flush() ++{ ++ CSingleLock lock(m_sync); ++ ++ while( !m_queue.empty() ) m_queue.pop(); ++ m_current.pts = DVD_NOPTS_VALUE; ++ m_current.timestamp = 0.0; ++ m_current.duration = 0.0; ++} ++ ++double CPTSOutputQueue::Current() ++{ ++ CSingleLock lock(m_sync); ++ ++ if(!m_queue.empty() && m_current.pts == DVD_NOPTS_VALUE) ++ { ++ m_current = m_queue.front(); ++ m_queue.pop(); ++ } ++ ++ while( !m_queue.empty() && CDVDClock::GetAbsoluteClock() >= m_queue.front().timestamp ) ++ { ++ m_current = m_queue.front(); ++ m_queue.pop(); ++ } ++ ++ if( m_current.timestamp == 0 ) return m_current.pts; ++ ++ return m_current.pts + min(m_current.duration, (CDVDClock::GetAbsoluteClock() - m_current.timestamp)); ++} ++ ++ + CDVDAudio::CDVDAudio(volatile bool &bStop) + : m_bStop(bStop) + { +@@ -114,6 +176,7 @@ + m_iBitsPerSample = 0; + m_bPassthrough = false; + m_bPaused = true; ++ m_time.Flush(); + } + + DWORD CDVDAudio::AddPacketsRenderer(unsigned char* data, DWORD len, CSingleLock &lock) +@@ -203,6 +266,10 @@ + m_iBufferSize = len; + memcpy(m_pBuffer, data, len); + } ++ ++ double time_added = DVD_SEC_TO_TIME(m_SecondsPerByte * (data - audioframe.data)); ++ m_time.Add(audioframe.pts, GetDelay() - time_added, audioframe.duration); ++ + return total; + } + +@@ -276,6 +343,7 @@ + { + CSingleLock lock (m_critSection); + if (m_pAudioStream) m_pAudioStream->Pause(); ++ m_time.Flush(); + } + + void CDVDAudio::Resume() +@@ -306,6 +374,7 @@ + m_pAudioStream->Flush(); + } + m_iBufferSize = 0; ++ m_time.Flush(); + } + + bool CDVDAudio::IsValidFormat(const DVDAudioFrame &audioframe) +@@ -354,3 +423,10 @@ + return 0.0; + return m_pAudioStream->GetCacheTotal(); + } ++ ++void CDVDAudio::SetPlayingPts(double pts) ++{ ++ CSingleLock lock (m_critSection); ++ m_time.Flush(); ++ m_time.Add(pts, GetDelay(), 0); ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDAudio.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDAudio.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDAudio.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDAudio.h 2012-10-03 03:02:11.235938372 +0200 +@@ -25,6 +25,7 @@ + #endif + #include "threads/CriticalSection.h" + #include "PlatformDefs.h" ++#include + + #include "cores/AudioEngine/Utils/AEChannelInfo.h" + class IAEStream; +@@ -46,6 +47,22 @@ + #endif + typedef struct stDVDAudioFrame DVDAudioFrame; + ++ ++class CPTSOutputQueue ++{ ++private: ++ typedef struct {double pts; double timestamp; double duration;} TPTSItem; ++ TPTSItem m_current; ++ std::queue m_queue; ++ CCriticalSection m_sync; ++ ++public: ++ CPTSOutputQueue(); ++ void Add(double pts, double delay, double duration); ++ void Flush(); ++ double Current(); ++}; ++ + class CSingleLock; + class IAudioCallback; + +@@ -68,6 +85,8 @@ + void Destroy(); + DWORD AddPackets(const DVDAudioFrame &audioframe); + double GetDelay(); // returns the time it takes to play a packet if we add one at this time ++ double GetPlayingPts() { return m_time.Current(); } ++ void SetPlayingPts(double pts); + double GetCacheTime(); // returns total amount of data cached in audio output at this time + double GetCacheTotal(); // returns total amount the audio device can buffer + void Flush(); +@@ -79,6 +98,7 @@ + + IAEStream *m_pAudioStream; + protected: ++ CPTSOutputQueue m_time; + DWORD AddPacketsRenderer(unsigned char* data, DWORD len, CSingleLock &lock); + BYTE* m_pBuffer; // should be [m_dwPacketSize] + DWORD m_iBufferSize; +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp 2012-10-03 03:02:11.414942010 +0200 +@@ -56,6 +56,9 @@ + #ifdef HAVE_LIBVA + #include "VAAPI.h" + #endif ++#ifdef HAVE_LIBXVBA ++#include "XVBA.h" ++#endif + + using namespace boost; + +@@ -71,14 +74,14 @@ + while(*cur != PIX_FMT_NONE) + { + #ifdef HAVE_LIBVDPAU +- if(CVDPAU::IsVDPAUFormat(*cur) && g_guiSettings.GetBool("videoplayer.usevdpau")) ++ if(VDPAU::CDecoder::IsVDPAUFormat(*cur) && g_guiSettings.GetBool("videoplayer.usevdpau")) + { + if(ctx->GetHardware()) + return *cur; + + CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::GetFormat - Creating VDPAU(%ix%i)", avctx->width, avctx->height); +- CVDPAU* vdp = new CVDPAU(); +- if(vdp->Open(avctx, *cur)) ++ VDPAU::CDecoder* vdp = new VDPAU::CDecoder(); ++ if(vdp->Open(avctx, *cur, ctx->m_uSurfacesCount)) + { + ctx->SetHardware(vdp); + return *cur; +@@ -100,13 +103,26 @@ + dec->Release(); + } + #endif ++#ifdef HAVE_LIBXVBA ++ if(*cur == PIX_FMT_XVBA_VLD && g_guiSettings.GetBool("videoplayer.usexvba")) ++ { ++ XVBA::CDecoder* dec = new XVBA::CDecoder(); ++ if(dec->Open(avctx, *cur, ctx->m_uSurfacesCount)) ++ { ++ ctx->SetHardware(dec); ++ return *cur; ++ } ++ else ++ dec->Release(); ++ } ++#endif + #ifdef HAVE_LIBVA + // mpeg4 vaapi decoding is disabled + if(*cur == PIX_FMT_VAAPI_VLD && g_guiSettings.GetBool("videoplayer.usevaapi") + && (avctx->codec_id != CODEC_ID_MPEG4 || g_advancedSettings.m_videoAllowMpeg4VAAPI)) + { + VAAPI::CDecoder* dec = new VAAPI::CDecoder(); +- if(dec->Open(avctx, *cur)) ++ if(dec->Open(avctx, *cur, ctx->m_uSurfacesCount)) + { + ctx->SetHardware(dec); + return *cur; +@@ -142,6 +158,7 @@ + m_iLastKeyframe = 0; + m_dts = DVD_NOPTS_VALUE; + m_started = false; ++ m_decoderPts = DVD_NOPTS_VALUE; + } + + CDVDVideoCodecFFmpeg::~CDVDVideoCodecFFmpeg() +@@ -204,14 +221,27 @@ + continue; + + CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Creating VDPAU(%ix%i, %d)",hints.width, hints.height, hints.codec); +- CVDPAU* vdp = new CVDPAU(); ++ ++ VDPAU::CDecoder* vdp = new VDPAU::CDecoder(); + m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec); + m_pCodecContext->codec_id = hints.codec; + m_pCodecContext->width = hints.width; + m_pCodecContext->height = hints.height; + m_pCodecContext->coded_width = hints.width; + m_pCodecContext->coded_height = hints.height; +- if(vdp->Open(m_pCodecContext, pCodec->pix_fmts ? pCodec->pix_fmts[0] : PIX_FMT_NONE)) ++ ++ // check number of surfaces used in renderer ++ unsigned int surfaces = 0; ++ for(std::vector::iterator it = options.m_keys.begin(); it != options.m_keys.end(); it++) ++ { ++ if (it->m_name == "surfaces") ++ { ++ surfaces = std::atoi(it->m_value.c_str()); ++ break; ++ } ++ } ++ ++ if(vdp->Open(m_pCodecContext, pCodec->pix_fmts ? pCodec->pix_fmts[0] : PIX_FMT_NONE, surfaces)) + { + m_pHardware = vdp; + m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set +@@ -340,6 +370,14 @@ + { + if( m_pCodecContext ) + { ++ if (bDrop && m_pHardware && m_pHardware->CanSkipDeint()) ++ { ++ m_requestSkipDeint = true; ++ bDrop = false; ++ } ++ else ++ m_requestSkipDeint = false; ++ + // i don't know exactly how high this should be set + // couldn't find any good docs on it. think it varies + // from codec to codec on what it does +@@ -540,6 +578,7 @@ + void CDVDVideoCodecFFmpeg::Reset() + { + m_started = false; ++ m_decoderPts = DVD_NOPTS_VALUE; + m_iLastKeyframe = m_pCodecContext->has_b_frames; + m_dllAvCodec.avcodec_flush_buffers(m_pCodecContext); + +@@ -638,6 +677,22 @@ + else + pDvdVideoPicture->pts = DVD_NOPTS_VALUE; + ++ if (pDvdVideoPicture->pts != DVD_NOPTS_VALUE) ++ m_decoderPts = pDvdVideoPicture->pts; ++ else ++ m_decoderPts = m_dts; ++ ++ if (m_requestSkipDeint) ++ { ++ pDvdVideoPicture->iFlags |= DVP_FLAG_DROPDEINT; ++ m_skippedDeint = 1; ++ } ++ else ++ m_skippedDeint = 0; ++ ++ m_requestSkipDeint = false; ++ pDvdVideoPicture->iFlags |= m_codecControlFlags; ++ + if(!m_started) + pDvdVideoPicture->iFlags |= DVP_FLAG_DROPPED; + +@@ -860,3 +915,8 @@ + else + return 0; + } ++ ++void CDVDVideoCodecFFmpeg::SetCodecControl(int flags) ++{ ++ m_codecControlFlags = flags; ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h 2012-10-03 03:02:11.371941136 +0200 +@@ -28,7 +28,6 @@ + #include "DllSwScale.h" + #include "DllAvFilter.h" + +-class CVDPAU; + class CCriticalSection; + + class CDVDVideoCodecFFmpeg : public CDVDVideoCodec +@@ -44,6 +43,7 @@ + virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) = 0; + virtual int Check (AVCodecContext* avctx) = 0; + virtual void Reset () {} ++ virtual bool CanSkipDeint() {return false; } + virtual const std::string Name() = 0; + virtual CCriticalSection* Section() { return NULL; } + }; +@@ -60,6 +60,8 @@ + virtual unsigned int SetFilters(unsigned int filters); + virtual const char* GetName() { return m_name.c_str(); }; // m_name is never changed after open + virtual unsigned GetConvergeCount(); ++ virtual bool GetPts(double &pts, int &skippedDeint, int &interlaced) {pts=m_decoderPts; skippedDeint=m_skippedDeint; if (m_pFrame) interlaced = m_pFrame->interlaced_frame; return true;} ++ virtual void SetCodecControl(int flags); + + bool IsHardwareAllowed() { return !m_bSoftware; } + IHardwareDecoder * GetHardware() { return m_pHardware; }; +@@ -119,4 +121,8 @@ + double m_dts; + bool m_started; + std::vector m_formats; ++ double m_decoderPts, m_decoderInterval; ++ int m_skippedDeint; ++ bool m_requestSkipDeint; ++ int m_codecControlFlags; + }; +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h 2012-10-03 03:02:11.413941989 +0200 +@@ -34,7 +34,8 @@ + + namespace DXVA { class CSurfaceContext; } + namespace VAAPI { struct CHolder; } +-class CVDPAU; ++namespace VDPAU { class CVdpauRenderPicture; } ++namespace XVBA { class CXvbaRenderPicture; } + class COpenMax; + class COpenMaxVideo; + struct OpenMaxVideoBuffer; +@@ -55,11 +56,14 @@ + DXVA::CSurfaceContext* context; + }; + struct { +- CVDPAU* vdpau; ++ VDPAU::CVdpauRenderPicture* vdpau; + }; + struct { + VAAPI::CHolder* vaapi; + }; ++ struct { ++ XVBA::CXvbaRenderPicture* xvba; ++ }; + + struct { + COpenMax *openMax; +@@ -110,6 +114,10 @@ + #define DVP_FLAG_NOSKIP 0x00000010 // indicate this picture should never be dropped + #define DVP_FLAG_DROPPED 0x00000020 // indicate that this picture has been dropped in decoder stage, will have no data + ++#define DVP_FLAG_DROPDEINT 0x00000040 // indicate that this picture was requested to have been dropped in deint stage ++#define DVP_FLAG_NO_POSTPROC 0x00000100 ++#define DVP_FLAG_DRAIN 0x00000200 ++ + // DVP_FLAG 0x00000100 - 0x00000f00 is in use by libmpeg2! + + #define DVP_QSCALE_UNKNOWN 0 +@@ -127,6 +135,9 @@ + #define VC_PICTURE 0x00000004 // the decoder got a picture, call Decode(NULL, 0) again to parse the rest of the data + #define VC_USERDATA 0x00000008 // the decoder found some userdata, call Decode(NULL, 0) again to parse the rest of the data + #define VC_FLUSHED 0x00000010 // the decoder lost it's state, we need to restart decoding again ++#define VC_DROPPED 0x00000020 // needed to identify if a picture was dropped ++#define VC_HURRY 0x00000040 ++ + class CDVDVideoCodec + { + public: +@@ -237,4 +248,11 @@ + { + return 0; + } ++ ++ virtual bool GetPts(double &pts, int &skippedDeint, int &interlaced) ++ { ++ return false; ++ } ++ ++ virtual void SetCodecControl(int flags) {} + }; +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in 2012-10-03 03:02:11.414942010 +0200 +@@ -14,6 +14,10 @@ + SRCS += CrystalHD.cpp + SRCS += DVDVideoCodecCrystalHD.cpp + endif ++ifeq (@USE_XVBA@,1) ++SRCS+= XVBA.cpp \ ++ ++endif + ifeq (@USE_VDA@,1) + SRCS += DVDVideoCodecVDA.cpp + endif +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp 2012-10-03 03:02:11.426942253 +0200 +@@ -261,6 +261,15 @@ + + bool CDecoder::Open(AVCodecContext *avctx, enum PixelFormat fmt, unsigned int surfaces) + { ++#ifdef HAVE_LIBXVBA ++ std::string Vendor = g_Windowing.GetRenderVendor(); ++ std::transform(Vendor.begin(), Vendor.end(), Vendor.begin(), ::tolower); ++ if (Vendor.compare(0, 3, "ati") == 0) ++ { ++ return false; ++ } ++#endif ++ + VAEntrypoint entrypoint = VAEntrypointVLD; + VAProfile profile; + +@@ -357,6 +366,7 @@ + CHECK(vaCreateConfig(m_display->get(), profile, entrypoint, &attrib, 1, &m_hwaccel->config_id)) + m_config = m_hwaccel->config_id; + ++ m_renderbuffers_count = surfaces; + if (!EnsureContext(avctx)) + return false; + +@@ -388,7 +398,7 @@ + else + m_refs = 2; + } +- return EnsureSurfaces(avctx, m_refs + 3); ++ return EnsureSurfaces(avctx, m_refs + m_renderbuffers_count + 1); + } + + bool CDecoder::EnsureSurfaces(AVCodecContext *avctx, unsigned n_surfaces_count) +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h 2012-10-03 03:02:11.382941358 +0200 +@@ -122,6 +122,7 @@ + static const unsigned m_surfaces_max = 32; + unsigned m_surfaces_count; + VASurfaceID m_surfaces[m_surfaces_max]; ++ unsigned m_renderbuffers_count; + + int m_refs; + std::list m_surfaces_used; +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp 2012-10-03 03:02:11.424942213 +0200 +@@ -1,5 +1,6 @@ + /* + * Copyright (C) 2005-2012 Team XBMC ++ * Copyright (C) 2005-2011 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify +@@ -32,11 +33,16 @@ + #include "settings/AdvancedSettings.h" + #include "Application.h" + #include "utils/MathUtils.h" ++#include "utils/TimeUtils.h" + #include "DVDCodecs/DVDCodecUtils.h" ++#include "cores/VideoRenderers/RenderFlags.h" ++ ++using namespace VDPAU; ++#define NUM_RENDER_PICS 9 + + #define ARSIZE(x) (sizeof(x) / sizeof((x)[0])) + +-CVDPAU::Desc decoder_profiles[] = { ++CDecoder::Desc decoder_profiles[] = { + {"MPEG1", VDP_DECODER_PROFILE_MPEG1}, + {"MPEG2_SIMPLE", VDP_DECODER_PROFILE_MPEG2_SIMPLE}, + {"MPEG2_MAIN", VDP_DECODER_PROFILE_MPEG2_MAIN}, +@@ -50,14 +56,16 @@ + {"MPEG4_PART2_ASP", VDP_DECODER_PROFILE_MPEG4_PART2_ASP}, + #endif + }; +-const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CVDPAU::Desc); ++const size_t decoder_profile_count = sizeof(decoder_profiles)/sizeof(CDecoder::Desc); + +-static float studioCSC[3][4] = +-{ +- { 1.0f, 0.0f, 1.57480000f,-0.78740000f}, +- { 1.0f,-0.18737736f,-0.46813736f, 0.32775736f}, +- { 1.0f, 1.85556000f, 0.0f,-0.92780000f} +-}; ++//static float studioCSC[3][4] = ++//{ ++// { 1.0f, 0.0f, 1.57480000f,-0.78740000f}, ++// { 1.0f,-0.18737736f,-0.46813736f, 0.32775736f}, ++// { 1.0f, 1.85556000f, 0.0f,-0.92780000f} ++//}; ++static float studioCSCKCoeffs601[3] = {0.299, 0.587, 0.114}; //BT601 {Kr, Kg, Kb} ++static float studioCSCKCoeffs709[3] = {0.2126, 0.7152, 0.0722}; //BT709 {Kr, Kg, Kb} + + static struct SInterlaceMapping + { +@@ -68,88 +76,30 @@ + , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL} + , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL , VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL} + , {VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL} +-, {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE} ++, {VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE , VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE} + , {VS_INTERLACEMETHOD_NONE , (VdpVideoMixerFeature)-1} + }; + + //since libvdpau 0.4, vdp_device_create_x11() installs a callback on the Display*, + //if we unload libvdpau with dlclose(), we segfault on XCloseDisplay, + //so we just keep a static handle to libvdpau around +-void* CVDPAU::dl_handle; ++void* CDecoder::dl_handle; + +-CVDPAU::CVDPAU() +-{ +- glXBindTexImageEXT = NULL; +- glXReleaseTexImageEXT = NULL; +- vdp_device = VDP_INVALID_HANDLE; +- surfaceNum = presentSurfaceNum = 0; +- picAge.b_age = picAge.ip_age[0] = picAge.ip_age[1] = 256*256*256*64; +- vdpauConfigured = false; +- m_DisplayState = VDPAU_OPEN; +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; +- m_mixerstep = 0; ++//----------------------------------------------------------------------------- ++// CVDPAU ++//----------------------------------------------------------------------------- + +- m_glPixmap = 0; +- m_Pixmap = 0; +- if (!glXBindTexImageEXT) +- glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXBindTexImageEXT"); +- if (!glXReleaseTexImageEXT) +- glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXReleaseTexImageEXT"); ++CDecoder::CDecoder() : m_vdpauOutput(&m_inMsgEvent) ++{ ++ m_vdpauConfig.vdpDevice = VDP_INVALID_HANDLE; ++ m_vdpauConfig.videoSurfaces = &m_videoSurfaces; ++ m_vdpauConfig.videoSurfaceSec = &m_videoSurfaceSec; + +- totalAvailableOutputSurfaces = 0; +- outputSurface = presentSurface = VDP_INVALID_HANDLE; +- vdp_flip_target = VDP_INVALID_HANDLE; +- vdp_flip_queue = VDP_INVALID_HANDLE; +- vid_width = vid_height = OutWidth = OutHeight = 0; +- surface_width = surface_height = 0; +- +- memset(&decoder, 0, sizeof(decoder)); +- memset(&outRect, 0, sizeof(outRect)); +- memset(&outRectVid, 0, sizeof(outRectVid)); +- +- m_Display = NULL; +- +- tmpBrightness = 0; +- tmpContrast = 0; +- tmpDeintMode = 0; +- tmpDeintGUI = 0; +- tmpDeint = 0; +- max_references = 0; +- +- for (int i = 0; i < NUM_OUTPUT_SURFACES; i++) +- outputSurfaces[i] = VDP_INVALID_HANDLE; +- +- videoMixer = VDP_INVALID_HANDLE; +- m_BlackBar = NULL; +- +- memset(m_features, 0, sizeof(m_features)); +- m_feature_count = 0; +- m_vdpauOutputMethod = OUTPUT_NONE; +- +- upScale = g_advancedSettings.m_videoVDPAUScaling; +- +- vdp_video_mixer_set_attribute_values = NULL; +- vdp_generate_csc_matrix = NULL; +- vdp_presentation_queue_target_destroy = NULL; +- vdp_presentation_queue_create = NULL; +- vdp_presentation_queue_destroy = NULL; +- vdp_presentation_queue_display = NULL; +- vdp_presentation_queue_block_until_surface_idle = NULL; +- vdp_presentation_queue_target_create_x11 = NULL; +- vdp_presentation_queue_query_surface_status = NULL; +- vdp_presentation_queue_get_time = NULL; +- vdp_get_error_string = NULL; +- vdp_decoder_create = NULL; +- vdp_decoder_destroy = NULL; +- vdp_decoder_render = NULL; +- vdp_decoder_query_caps = NULL; +- vdp_preemption_callback_register = NULL; +- dl_vdp_device_create_x11 = NULL; +- dl_vdp_get_proc_address = NULL; +- dl_vdp_preemption_callback_register = NULL; ++ m_vdpauConfigured = false; ++ m_DisplayState = VDPAU_OPEN; + } + +-bool CVDPAU::Open(AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces) ++bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces) + { + if(avctx->coded_width == 0 + || avctx->coded_height == 0) +@@ -157,6 +107,8 @@ + CLog::Log(LOGWARNING,"(VDPAU) no width/height available, can't init"); + return false; + } ++ m_vdpauConfig.numRenderBuffers = surfaces; ++ m_decoderThread = CThread::GetCurrentThreadId(); + + if (!dl_handle) + { +@@ -168,8 +120,6 @@ + error = "dlerror() returned NULL"; + + CLog::Log(LOGNOTICE,"(VDPAU) Unable to get handle to libvdpau: %s", error); +- //g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Error, "VDPAU", error, 10000); +- + return false; + } + } +@@ -178,8 +128,9 @@ + return false; + + InitVDPAUProcs(); ++ m_presentPicture = 0; + +- if (vdp_device != VDP_INVALID_HANDLE) ++ if (m_vdpauConfig.vdpDevice != VDP_INVALID_HANDLE) + { + SpewHardwareAvailable(); + +@@ -197,25 +148,23 @@ + + /* attempt to create a decoder with this width/height, some sizes are not supported by hw */ + VdpStatus vdp_st; +- vdp_st = vdp_decoder_create(vdp_device, profile, avctx->coded_width, avctx->coded_height, 5, &decoder); ++ vdp_st = m_vdpauConfig.vdpProcs.vdp_decoder_create(m_vdpauConfig.vdpDevice, profile, avctx->coded_width, avctx->coded_height, 5, &m_vdpauConfig.vdpDecoder); + + if(vdp_st != VDP_STATUS_OK) + { +- CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) checking for decoder support\n", vdp_get_error_string(vdp_st), vdp_st); ++ CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) checking for decoder support\n", m_vdpauConfig.vdpProcs.vdp_get_error_string(vdp_st), vdp_st); + FiniVDPAUProcs(); + return false; + } + +- vdp_decoder_destroy(decoder); ++ m_vdpauConfig.vdpProcs.vdp_decoder_destroy(m_vdpauConfig.vdpDecoder); + CheckStatus(vdp_st, __LINE__); + } + +- InitCSCMatrix(avctx->coded_height); +- + /* finally setup ffmpeg */ +- avctx->get_buffer = CVDPAU::FFGetBuffer; +- avctx->release_buffer = CVDPAU::FFReleaseBuffer; +- avctx->draw_horiz_band = CVDPAU::FFDrawSlice; ++ avctx->get_buffer = CDecoder::FFGetBuffer; ++ avctx->release_buffer = CDecoder::FFReleaseBuffer; ++ avctx->draw_horiz_band = CDecoder::FFDrawSlice; + avctx->slice_flags=SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD; + + g_Windowing.Register(this); +@@ -224,17 +173,20 @@ + return false; + } + +-CVDPAU::~CVDPAU() ++CDecoder::~CDecoder() + { + Close(); + } + +-void CVDPAU::Close() ++void CDecoder::Close() + { + CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__); + ++ CSingleLock lock(m_DecoderSection); ++ + FiniVDPAUOutput(); + FiniVDPAUProcs(); ++ m_vdpauOutput.Dispose(); + + while (!m_videoSurfaces.empty()) + { +@@ -250,188 +202,111 @@ + m_dllAvUtil.Unload(); + } + +-bool CVDPAU::MakePixmapGL() ++long CDecoder::Release() + { +- int num=0; +- int fbConfigIndex = 0; +- +- int doubleVisAttributes[] = { +- GLX_RENDER_TYPE, GLX_RGBA_BIT, +- GLX_RED_SIZE, 8, +- GLX_GREEN_SIZE, 8, +- GLX_BLUE_SIZE, 8, +- GLX_ALPHA_SIZE, 8, +- GLX_DEPTH_SIZE, 8, +- GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, +- GLX_BIND_TO_TEXTURE_RGBA_EXT, True, +- GLX_DOUBLEBUFFER, True, +- GLX_Y_INVERTED_EXT, True, +- GLX_X_RENDERABLE, True, +- None +- }; +- +- int pixmapAttribs[] = { +- GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, +- GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, +- None +- }; +- +- GLXFBConfig *fbConfigs; +- fbConfigs = glXChooseFBConfig(m_Display, DefaultScreen(m_Display), doubleVisAttributes, &num); +- if (fbConfigs==NULL) +- { +- CLog::Log(LOGERROR, "GLX Error: MakePixmap: No compatible framebuffers found"); +- return false; +- } +- CLog::Log(LOGDEBUG, "Found %d fbconfigs.", num); +- fbConfigIndex = 0; +- CLog::Log(LOGDEBUG, "Using fbconfig index %d.", fbConfigIndex); +- +- m_glPixmap = glXCreatePixmap(m_Display, fbConfigs[fbConfigIndex], m_Pixmap, pixmapAttribs); ++ // check if we should do some pre-cleanup here ++ // a second decoder might need resources ++ if (m_vdpauConfigured == true) ++ { ++ CSingleLock lock(m_DecoderSection); ++ CLog::Log(LOGNOTICE,"CVDPAU::Release pre-cleanup"); ++ ++ Message *reply; ++ if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP, ++ &reply, ++ 2000)) ++ { ++ bool success = reply->signal == COutputControlProtocol::ACC ? true : false; ++ reply->Release(); ++ if (!success) ++ { ++ CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup returned error", __FUNCTION__); ++ m_DisplayState = VDPAU_ERROR; ++ } ++ } ++ else ++ { ++ CLog::Log(LOGERROR, "VDPAU::%s - pre-cleanup timed out", __FUNCTION__); ++ m_DisplayState = VDPAU_ERROR; ++ } + +- if (!m_glPixmap) +- { +- CLog::Log(LOGINFO, "GLX Error: Could not create Pixmap"); +- XFree(fbConfigs); +- return false; ++ for(unsigned int i = 0; i < m_videoSurfaces.size(); ++i) ++ { ++ vdpau_render_state *render = m_videoSurfaces[i]; ++ if (render->surface != VDP_INVALID_HANDLE && !(render->state & FF_VDPAU_STATE_USED_FOR_RENDER)) ++ { ++ m_vdpauConfig.vdpProcs.vdp_video_surface_destroy(render->surface); ++ render->surface = VDP_INVALID_HANDLE; ++ } ++ } + } +- XFree(fbConfigs); ++ IHardwareDecoder::Release(); ++} + +- return true; ++long CDecoder::ReleasePicReference() ++{ ++ return IHardwareDecoder::Release(); + } + +-bool CVDPAU::MakePixmap(int width, int height) ++void CDecoder::SetWidthHeight(int width, int height) + { ++ m_vdpauConfig.upscale = g_advancedSettings.m_videoVDPAUScaling; ++ + //pick the smallest dimensions, so we downscale with vdpau and upscale with opengl when appropriate + //this requires the least amount of gpu memory bandwidth +- if (g_graphicsContext.GetWidth() < width || g_graphicsContext.GetHeight() < height || upScale) ++ if (g_graphicsContext.GetWidth() < width || g_graphicsContext.GetHeight() < height || m_vdpauConfig.upscale >= 0) + { + //scale width to desktop size if the aspect ratio is the same or bigger than the desktop + if ((double)height * g_graphicsContext.GetWidth() / width <= (double)g_graphicsContext.GetHeight()) + { +- OutWidth = g_graphicsContext.GetWidth(); +- OutHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width); ++ m_vdpauConfig.outWidth = g_graphicsContext.GetWidth(); ++ m_vdpauConfig.outHeight = MathUtils::round_int((double)height * g_graphicsContext.GetWidth() / width); + } + else //scale height to the desktop size if the aspect ratio is smaller than the desktop + { +- OutHeight = g_graphicsContext.GetHeight(); +- OutWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height); ++ m_vdpauConfig.outHeight = g_graphicsContext.GetHeight(); ++ m_vdpauConfig.outWidth = MathUtils::round_int((double)width * g_graphicsContext.GetHeight() / height); + } + } + else + { //let opengl scale +- OutWidth = width; +- OutHeight = height; +- } +- +- CLog::Log(LOGNOTICE,"Creating %ix%i pixmap", OutWidth, OutHeight); +- +- // Get our window attribs. +- XWindowAttributes wndattribs; +- XGetWindowAttributes(m_Display, DefaultRootWindow(m_Display), &wndattribs); // returns a status but I don't know what success is +- +- m_Pixmap = XCreatePixmap(m_Display, +- DefaultRootWindow(m_Display), +- OutWidth, +- OutHeight, +- wndattribs.depth); +- if (!m_Pixmap) +- { +- CLog::Log(LOGERROR, "GLX Error: MakePixmap: Unable to create XPixmap"); +- return false; +- } +- +- XGCValues values = {}; +- GC xgc; +- values.foreground = BlackPixel (m_Display, DefaultScreen (m_Display)); +- xgc = XCreateGC(m_Display, m_Pixmap, GCForeground, &values); +- XFillRectangle(m_Display, m_Pixmap, xgc, 0, 0, OutWidth, OutHeight); +- XFreeGC(m_Display, xgc); +- +- if(!MakePixmapGL()) +- return false; +- +- return true; +-} +- +-void CVDPAU::BindPixmap() +-{ +- CSharedLock lock(m_DecoderSection); +- +- { CSharedLock dLock(m_DisplaySection); +- if (m_DisplayState != VDPAU_OPEN) +- return; +- } +- +- if (m_glPixmap) +- { +- if(presentSurface != VDP_INVALID_HANDLE) +- { +- VdpPresentationQueueStatus status; +- VdpTime time; +- VdpStatus vdp_st; +- vdp_st = vdp_presentation_queue_query_surface_status( +- vdp_flip_queue, presentSurface, &status, &time); +- CheckStatus(vdp_st, __LINE__); +- while(status != VDP_PRESENTATION_QUEUE_STATUS_VISIBLE && vdp_st == VDP_STATUS_OK) +- { +- Sleep(1); +- vdp_st = vdp_presentation_queue_query_surface_status( +- vdp_flip_queue, presentSurface, &status, &time); +- CheckStatus(vdp_st, __LINE__); +- } +- } +- +- glXBindTexImageEXT(m_Display, m_glPixmap, GLX_FRONT_LEFT_EXT, NULL); +- } +- else CLog::Log(LOGERROR,"(VDPAU) BindPixmap called without valid pixmap"); +-} +- +-void CVDPAU::ReleasePixmap() +-{ +- CSharedLock lock(m_DecoderSection); +- +- { CSharedLock dLock(m_DisplaySection); +- if (m_DisplayState != VDPAU_OPEN) +- return; +- } +- +- if (m_glPixmap) +- { +- glXReleaseTexImageEXT(m_Display, m_glPixmap, GLX_FRONT_LEFT_EXT); ++ m_vdpauConfig.outWidth = width; ++ m_vdpauConfig.outHeight = height; + } +- else CLog::Log(LOGERROR,"(VDPAU) ReleasePixmap called without valid pixmap"); ++ CLog::Log(LOGDEBUG, "CVDPAU::SetWidthHeight Setting OutWidth: %i OutHeight: %i", m_vdpauConfig.outWidth, m_vdpauConfig.outHeight); + } + +-void CVDPAU::OnLostDevice() ++void CDecoder::OnLostDevice() + { + CLog::Log(LOGNOTICE,"CVDPAU::OnLostDevice event"); + +- CExclusiveLock lock(m_DecoderSection); ++ CSingleLock lock(m_DecoderSection); + FiniVDPAUOutput(); + FiniVDPAUProcs(); + + m_DisplayState = VDPAU_LOST; ++ lock.Leave(); + m_DisplayEvent.Reset(); + } + +-void CVDPAU::OnResetDevice() ++void CDecoder::OnResetDevice() + { + CLog::Log(LOGNOTICE,"CVDPAU::OnResetDevice event"); + +- CExclusiveLock lock(m_DisplaySection); ++ CSingleLock lock(m_DecoderSection); + if (m_DisplayState == VDPAU_LOST) + { + m_DisplayState = VDPAU_RESET; ++ lock.Leave(); + m_DisplayEvent.Set(); + } + } + +-int CVDPAU::Check(AVCodecContext* avctx) ++int CDecoder::Check(AVCodecContext* avctx) + { + EDisplayState state; + +- { CSharedLock lock(m_DisplaySection); ++ { CSingleLock lock(m_DecoderSection); + state = m_DisplayState; + } + +@@ -445,16 +320,13 @@ + } + else + { +- CSharedLock lock(m_DisplaySection); ++ CSingleLock lock(m_DecoderSection); + state = m_DisplayState; + } + } + if (state == VDPAU_RESET || state == VDPAU_ERROR) + { +- CLog::Log(LOGNOTICE,"Attempting recovery"); +- +- CSingleLock gLock(g_graphicsContext); +- CExclusiveLock lock(m_DecoderSection); ++ CSingleLock lock(m_DecoderSection); + + FiniVDPAUOutput(); + FiniVDPAUProcs(); +@@ -469,7 +341,7 @@ + return 0; + } + +-bool CVDPAU::IsVDPAUFormat(PixelFormat format) ++bool CDecoder::IsVDPAUFormat(PixelFormat format) + { + if ((format >= PIX_FMT_VDPAU_H264) && (format <= PIX_FMT_VDPAU_VC1)) return true; + #if (defined PIX_FMT_VDPAU_MPEG4_IN_AVUTIL) +@@ -478,91 +350,28 @@ + else return false; + } + +-void CVDPAU::CheckFeatures() +-{ +- if (videoMixer == VDP_INVALID_HANDLE) +- { +- CLog::Log(LOGNOTICE, " (VDPAU) Creating the video mixer"); +- // Creation of VideoMixer. +- VdpVideoMixerParameter parameters[] = { +- VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH, +- VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT, +- VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE +- }; +- +- void const * parameter_values[] = { +- &surface_width, +- &surface_height, +- &vdp_chroma_type +- }; +- +- tmpBrightness = 0; +- tmpContrast = 0; +- tmpNoiseReduction = 0; +- tmpSharpness = 0; +- +- VdpStatus vdp_st = VDP_STATUS_ERROR; +- vdp_st = vdp_video_mixer_create(vdp_device, +- m_feature_count, +- m_features, +- ARSIZE(parameters), +- parameters, +- parameter_values, +- &videoMixer); +- CheckStatus(vdp_st, __LINE__); +- +- SetHWUpscaling(); +- } +- +- if (tmpBrightness != g_settings.m_currentVideoSettings.m_Brightness || +- tmpContrast != g_settings.m_currentVideoSettings.m_Contrast) +- { +- SetColor(); +- tmpBrightness = g_settings.m_currentVideoSettings.m_Brightness; +- tmpContrast = g_settings.m_currentVideoSettings.m_Contrast; +- } +- if (tmpNoiseReduction != g_settings.m_currentVideoSettings.m_NoiseReduction) +- { +- tmpNoiseReduction = g_settings.m_currentVideoSettings.m_NoiseReduction; +- SetNoiseReduction(); +- } +- if (tmpSharpness != g_settings.m_currentVideoSettings.m_Sharpness) +- { +- tmpSharpness = g_settings.m_currentVideoSettings.m_Sharpness; +- SetSharpness(); +- } +- if ( tmpDeintMode != g_settings.m_currentVideoSettings.m_DeinterlaceMode || +- tmpDeintGUI != g_settings.m_currentVideoSettings.m_InterlaceMethod || +- (tmpDeintGUI == VS_INTERLACEMETHOD_AUTO && tmpDeint != AutoInterlaceMethod())) +- { +- tmpDeintMode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; +- tmpDeintGUI = g_settings.m_currentVideoSettings.m_InterlaceMethod; +- if (tmpDeintGUI == VS_INTERLACEMETHOD_AUTO) +- tmpDeint = AutoInterlaceMethod(); +- else +- tmpDeint = tmpDeintGUI; +- +- SetDeinterlacing(); +- } +-} +- +-bool CVDPAU::Supports(VdpVideoMixerFeature feature) ++bool CDecoder::Supports(VdpVideoMixerFeature feature) + { +- for(int i = 0; i < m_feature_count; i++) ++ for(int i = 0; i < m_vdpauConfig.featureCount; i++) + { +- if(m_features[i] == feature) ++ if(m_vdpauConfig.vdpFeatures[i] == feature) + return true; + } + return false; + } + +-bool CVDPAU::Supports(EINTERLACEMETHOD method) ++bool CDecoder::Supports(EINTERLACEMETHOD method) + { + if(method == VS_INTERLACEMETHOD_VDPAU_BOB +- || method == VS_INTERLACEMETHOD_AUTO +- || method == VS_INTERLACEMETHOD_AUTO_ION) ++ || method == VS_INTERLACEMETHOD_AUTO) + return true; + ++ if (g_guiSettings.GetBool("videoplayer.usevdpauinteropyuv")) ++ { ++ if (method == VS_INTERLACEMETHOD_RENDER_BOB) ++ return true; ++ } ++ + for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++) + { + if(p->method == method) +@@ -571,162 +380,12 @@ + return false; + } + +-EINTERLACEMETHOD CVDPAU::AutoInterlaceMethod() +-{ +- return VS_INTERLACEMETHOD_VDPAU_TEMPORAL; +-} +- +-void CVDPAU::SetColor() +-{ +- VdpStatus vdp_st; +- +- if (tmpBrightness != g_settings.m_currentVideoSettings.m_Brightness) +- m_Procamp.brightness = (float)((g_settings.m_currentVideoSettings.m_Brightness)-50) / 100; +- if (tmpContrast != g_settings.m_currentVideoSettings.m_Contrast) +- m_Procamp.contrast = (float)((g_settings.m_currentVideoSettings.m_Contrast)+50) / 100; +- +- if(vid_height >= 600 || vid_width > 1024) +- vdp_st = vdp_generate_csc_matrix(&m_Procamp, VDP_COLOR_STANDARD_ITUR_BT_709, &m_CSCMatrix); +- else +- vdp_st = vdp_generate_csc_matrix(&m_Procamp, VDP_COLOR_STANDARD_ITUR_BT_601, &m_CSCMatrix); +- +- VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX }; +- if (g_guiSettings.GetBool("videoplayer.vdpaustudiolevel")) +- { +- void const * pm_CSCMatix[] = { &studioCSC }; +- vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix); +- } +- else +- { +- void const * pm_CSCMatix[] = { &m_CSCMatrix }; +- vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix); +- } +- CheckStatus(vdp_st, __LINE__); +-} +- +-void CVDPAU::SetNoiseReduction() +-{ +- if(!Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION)) +- return; +- +- VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION }; +- VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL }; +- VdpStatus vdp_st; +- +- if (!g_settings.m_currentVideoSettings.m_NoiseReduction) +- { +- VdpBool enabled[]= {0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- CheckStatus(vdp_st, __LINE__); +- return; +- } +- VdpBool enabled[]={1}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- CheckStatus(vdp_st, __LINE__); +- void* nr[] = { &g_settings.m_currentVideoSettings.m_NoiseReduction }; +- CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",g_settings.m_currentVideoSettings.m_NoiseReduction); +- vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, nr); +- CheckStatus(vdp_st, __LINE__); +-} +- +-void CVDPAU::SetSharpness() +-{ +- if(!Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS)) +- return; +- +- VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS }; +- VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL }; +- VdpStatus vdp_st; +- +- if (!g_settings.m_currentVideoSettings.m_Sharpness) +- { +- VdpBool enabled[]={0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- CheckStatus(vdp_st, __LINE__); +- return; +- } +- VdpBool enabled[]={1}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- CheckStatus(vdp_st, __LINE__); +- void* sh[] = { &g_settings.m_currentVideoSettings.m_Sharpness }; +- CLog::Log(LOGNOTICE,"Setting Sharpness to %f",g_settings.m_currentVideoSettings.m_Sharpness); +- vdp_st = vdp_video_mixer_set_attribute_values(videoMixer, ARSIZE(attributes), attributes, sh); +- CheckStatus(vdp_st, __LINE__); +-} +- +-void CVDPAU::SetHWUpscaling() +-{ +-#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 +- if(!Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1) || !upScale) +- return; +- +- VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 }; +- VdpStatus vdp_st; +- VdpBool enabled[]={1}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- CheckStatus(vdp_st, __LINE__); +-#endif +-} +- +-void CVDPAU::SetDeinterlacing() ++EINTERLACEMETHOD CDecoder::AutoInterlaceMethod() + { +- VdpStatus vdp_st; +- EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; +- EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; +- if (method == VS_INTERLACEMETHOD_AUTO) +- method = AutoInterlaceMethod(); +- +- VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL, +- VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL, +- VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE }; +- if (mode == VS_DEINTERLACEMODE_OFF) +- { +- VdpBool enabled[]={0,0,0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- else +- { +- if (method == VS_INTERLACEMETHOD_AUTO_ION) +- { +- if (vid_height <= 576) +- { +- VdpBool enabled[]={1,1,0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- else if (vid_height > 576) +- { +- VdpBool enabled[]={1,0,0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- } +- else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF) +- { +- VdpBool enabled[]={1,0,0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF) +- { +- VdpBool enabled[]={1,1,0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- else if (method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE) +- { +- VdpBool enabled[]={1,0,1}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- else +- { +- VdpBool enabled[]={0,0,0}; +- vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); +- } +- } +- +- CheckStatus(vdp_st, __LINE__); ++ return VS_INTERLACEMETHOD_RENDER_BOB; + } + +-void CVDPAU::InitVDPAUProcs() ++void CDecoder::InitVDPAUProcs() + { + char* error; + +@@ -736,151 +395,115 @@ + if (error) + { + CLog::Log(LOGERROR,"(VDPAU) - %s in %s",error,__FUNCTION__); +- vdp_device = VDP_INVALID_HANDLE; +- +- //g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Error, "VDPAU", error, 10000); +- ++ m_vdpauConfig.vdpDevice = VDP_INVALID_HANDLE; + return; + } + + if (dl_vdp_device_create_x11) + { +- CSingleLock lock(g_graphicsContext); +- m_Display = g_Windowing.GetDisplay(); +- } +- else +- { +- CLog::Log(LOGERROR,"(VDPAU) - Unable to get dl_vdp_device_create_x11 in %s", __FUNCTION__); +- vdp_device = VDP_INVALID_HANDLE; +- return; ++ m_Display = XOpenDisplay(NULL); + } + +- int mScreen = DefaultScreen(m_Display); ++ int mScreen = g_Windowing.GetCurrentScreen(); + VdpStatus vdp_st; + + // Create Device +- // tested on 64bit Ubuntu 11.10 and it deadlocked without this +- XLockDisplay(m_Display); + vdp_st = dl_vdp_device_create_x11(m_Display, //x_display, + mScreen, //x_screen, +- &vdp_device, +- &vdp_get_proc_address); +- XUnlockDisplay(m_Display); ++ &m_vdpauConfig.vdpDevice, ++ &m_vdpauConfig.vdpProcs.vdp_get_proc_address); + +- CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",vdp_device,vdp_st); ++ CLog::Log(LOGNOTICE,"vdp_device = 0x%08x vdp_st = 0x%08x",m_vdpauConfig.vdpDevice,vdp_st); + if (vdp_st != VDP_STATUS_OK) + { + CLog::Log(LOGERROR,"(VDPAU) unable to init VDPAU - vdp_st = 0x%x. Falling back.",vdp_st); +- vdp_device = VDP_INVALID_HANDLE; ++ m_vdpauConfig.vdpDevice = VDP_INVALID_HANDLE; + return; + } + + #define VDP_PROC(id, proc) \ + do { \ +- vdp_st = vdp_get_proc_address(vdp_device, id, (void**)&proc); \ ++ vdp_st = m_vdpauConfig.vdpProcs.vdp_get_proc_address(m_vdpauConfig.vdpDevice, id, (void**)&proc); \ + CheckStatus(vdp_st, __LINE__); \ + } while(0); + +- VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING , vdp_get_error_string); +- VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY , vdp_device_destroy); +- VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX , vdp_generate_csc_matrix); +- VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE , vdp_video_surface_create); +- VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY , vdp_video_surface_destroy); +- VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR , vdp_video_surface_put_bits_y_cb_cr); +- VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR , vdp_video_surface_get_bits_y_cb_cr); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR , vdp_output_surface_put_bits_y_cb_cr); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE , vdp_output_surface_put_bits_native); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE , vdp_output_surface_create); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY , vdp_output_surface_destroy); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE , vdp_output_surface_get_bits_native); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, vdp_output_surface_render_output_surface); +- VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED , vdp_output_surface_put_bits_indexed); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE , vdp_video_mixer_create); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES , vdp_video_mixer_set_feature_enables); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY , vdp_video_mixer_destroy); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER , vdp_video_mixer_render); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES , vdp_video_mixer_set_attribute_values); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , vdp_video_mixer_query_parameter_support); +- VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT , vdp_video_mixer_query_feature_support); +- VDP_PROC(VDP_FUNC_ID_DECODER_CREATE , vdp_decoder_create); +- VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY , vdp_decoder_destroy); +- VDP_PROC(VDP_FUNC_ID_DECODER_RENDER , vdp_decoder_render); +- VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES , vdp_decoder_query_caps); +- VDP_PROC(VDP_FUNC_ID_PREEMPTION_CALLBACK_REGISTER , vdp_preemption_callback_register); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY , vdp_presentation_queue_target_destroy); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE , vdp_presentation_queue_create); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY , vdp_presentation_queue_destroy); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY , vdp_presentation_queue_display); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_BLOCK_UNTIL_SURFACE_IDLE, vdp_presentation_queue_block_until_surface_idle); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11 , vdp_presentation_queue_target_create_x11); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_QUERY_SURFACE_STATUS , vdp_presentation_queue_query_surface_status); +- VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME , vdp_presentation_queue_get_time); +- ++ VDP_PROC(VDP_FUNC_ID_GET_ERROR_STRING , m_vdpauConfig.vdpProcs.vdp_get_error_string); ++ VDP_PROC(VDP_FUNC_ID_DEVICE_DESTROY , m_vdpauConfig.vdpProcs.vdp_device_destroy); ++ VDP_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX , m_vdpauConfig.vdpProcs.vdp_generate_csc_matrix); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE , m_vdpauConfig.vdpProcs.vdp_video_surface_create); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY , m_vdpauConfig.vdpProcs.vdp_video_surface_destroy); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR , m_vdpauConfig.vdpProcs.vdp_video_surface_put_bits_y_cb_cr); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR , m_vdpauConfig.vdpProcs.vdp_video_surface_get_bits_y_cb_cr); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_Y_CB_CR , m_vdpauConfig.vdpProcs.vdp_output_surface_put_bits_y_cb_cr); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_NATIVE , m_vdpauConfig.vdpProcs.vdp_output_surface_put_bits_native); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE , m_vdpauConfig.vdpProcs.vdp_output_surface_create); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY , m_vdpauConfig.vdpProcs.vdp_output_surface_destroy); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE , m_vdpauConfig.vdpProcs.vdp_output_surface_get_bits_native); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE, m_vdpauConfig.vdpProcs.vdp_output_surface_render_output_surface); ++ VDP_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_PUT_BITS_INDEXED , m_vdpauConfig.vdpProcs.vdp_output_surface_put_bits_indexed); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE , m_vdpauConfig.vdpProcs.vdp_video_mixer_create); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES , m_vdpauConfig.vdpProcs.vdp_video_mixer_set_feature_enables); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY , m_vdpauConfig.vdpProcs.vdp_video_mixer_destroy); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER , m_vdpauConfig.vdpProcs.vdp_video_mixer_render); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES , m_vdpauConfig.vdpProcs.vdp_video_mixer_set_attribute_values); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_PARAMETER_SUPPORT , m_vdpauConfig.vdpProcs.vdp_video_mixer_query_parameter_support); ++ VDP_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT , m_vdpauConfig.vdpProcs.vdp_video_mixer_query_feature_support); ++ VDP_PROC(VDP_FUNC_ID_DECODER_CREATE , m_vdpauConfig.vdpProcs.vdp_decoder_create); ++ VDP_PROC(VDP_FUNC_ID_DECODER_DESTROY , m_vdpauConfig.vdpProcs.vdp_decoder_destroy); ++ VDP_PROC(VDP_FUNC_ID_DECODER_RENDER , m_vdpauConfig.vdpProcs.vdp_decoder_render); ++ VDP_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES , m_vdpauConfig.vdpProcs.vdp_decoder_query_caps); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY , m_vdpauConfig.vdpProcs.vdp_presentation_queue_target_destroy); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE , m_vdpauConfig.vdpProcs.vdp_presentation_queue_create); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY , m_vdpauConfig.vdpProcs.vdp_presentation_queue_destroy); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY , m_vdpauConfig.vdpProcs.vdp_presentation_queue_display); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_BLOCK_UNTIL_SURFACE_IDLE, m_vdpauConfig.vdpProcs.vdp_presentation_queue_block_until_surface_idle); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11 , m_vdpauConfig.vdpProcs.vdp_presentation_queue_target_create_x11); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_QUERY_SURFACE_STATUS , m_vdpauConfig.vdpProcs.vdp_presentation_queue_query_surface_status); ++ VDP_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME , m_vdpauConfig.vdpProcs.vdp_presentation_queue_get_time); ++ + #undef VDP_PROC + + // set all vdpau resources to invalid +- vdp_flip_target = VDP_INVALID_HANDLE; +- vdp_flip_queue = VDP_INVALID_HANDLE; +- videoMixer = VDP_INVALID_HANDLE; +- totalAvailableOutputSurfaces = 0; +- presentSurface = VDP_INVALID_HANDLE; +- outputSurface = VDP_INVALID_HANDLE; +- for (int i = 0; i < NUM_OUTPUT_SURFACES; i++) +- outputSurfaces[i] = VDP_INVALID_HANDLE; +- +- m_vdpauOutputMethod = OUTPUT_NONE; +- +- CExclusiveLock lock(m_DisplaySection); + m_DisplayState = VDPAU_OPEN; +- vdpauConfigured = false; ++ m_vdpauConfigured = false; + } + +-void CVDPAU::FiniVDPAUProcs() ++void CDecoder::FiniVDPAUProcs() + { +- if (vdp_device == VDP_INVALID_HANDLE) return; ++ if (m_vdpauConfig.vdpDevice == VDP_INVALID_HANDLE) return; + + VdpStatus vdp_st; +- vdp_st = vdp_device_destroy(vdp_device); ++ vdp_st = m_vdpauConfig.vdpProcs.vdp_device_destroy(m_vdpauConfig.vdpDevice); + CheckStatus(vdp_st, __LINE__); +- vdp_device = VDP_INVALID_HANDLE; +- vdpauConfigured = false; ++ m_vdpauConfig.vdpDevice = VDP_INVALID_HANDLE; + } + +-void CVDPAU::InitCSCMatrix(int Height) ++void CDecoder::FiniVDPAUOutput() + { +- VdpStatus vdp_st; +- m_Procamp.struct_version = VDP_PROCAMP_VERSION; +- m_Procamp.brightness = 0.0; +- m_Procamp.contrast = 1.0; +- m_Procamp.saturation = 1.0; +- m_Procamp.hue = 0; +- vdp_st = vdp_generate_csc_matrix(&m_Procamp, +- (Height < 720)? VDP_COLOR_STANDARD_ITUR_BT_601 : VDP_COLOR_STANDARD_ITUR_BT_709, +- &m_CSCMatrix); +- CheckStatus(vdp_st, __LINE__); +-} +- +-void CVDPAU::FiniVDPAUOutput() +-{ +- FiniOutputMethod(); +- +- if (vdp_device == VDP_INVALID_HANDLE || !vdpauConfigured) return; ++ if (m_vdpauConfig.vdpDevice == VDP_INVALID_HANDLE || !m_vdpauConfigured) return; + + CLog::Log(LOGNOTICE, " (VDPAU) %s", __FUNCTION__); + ++ // uninit output ++ m_vdpauOutput.Dispose(); ++ m_vdpauConfigured = false; ++ + VdpStatus vdp_st; + +- vdp_st = vdp_decoder_destroy(decoder); ++ vdp_st = m_vdpauConfig.vdpProcs.vdp_decoder_destroy(m_vdpauConfig.vdpDecoder); + if (CheckStatus(vdp_st, __LINE__)) + return; +- decoder = VDP_INVALID_HANDLE; ++ m_vdpauConfig.vdpDecoder = VDP_INVALID_HANDLE; ++ ++ CSingleLock lock(m_videoSurfaceSec); ++ CLog::Log(LOGDEBUG, "CVDPAU::FiniVDPAUOutput destroying %d video surfaces", (int)m_videoSurfaces.size()); + +- for (unsigned int i = 0; i < m_videoSurfaces.size(); ++i) ++ for(unsigned int i = 0; i < m_videoSurfaces.size(); ++i) + { + vdpau_render_state *render = m_videoSurfaces[i]; + if (render->surface != VDP_INVALID_HANDLE) + { +- vdp_st = vdp_video_surface_destroy(render->surface); ++ vdp_st = m_vdpauConfig.vdpProcs.vdp_video_surface_destroy(render->surface); + render->surface = VDP_INVALID_HANDLE; + } + if (CheckStatus(vdp_st, __LINE__)) +@@ -888,8 +511,7 @@ + } + } + +- +-void CVDPAU::ReadFormatOf( PixelFormat fmt ++void CDecoder::ReadFormatOf( PixelFormat fmt + , VdpDecoderProfile &vdp_decoder_profile + , VdpChromaType &vdp_chroma_type) + { +@@ -916,9 +538,9 @@ + vdp_chroma_type = VDP_CHROMA_TYPE_420; + break; + #if (defined PIX_FMT_VDPAU_MPEG4_IN_AVUTIL) && \ +- (defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP) ++ (defined VDP_DECODER_PROFILE_MP) + case PIX_FMT_VDPAU_MPEG4: +- vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP; ++ vdp_decoder_profile = VDP_DECOPEG4_PART2_ASP; + vdp_chroma_type = VDP_CHROMA_TYPE_420; + break; + #endif +@@ -929,170 +551,78 @@ + } + } + +- +-bool CVDPAU::ConfigVDPAU(AVCodecContext* avctx, int ref_frames) ++bool CDecoder::ConfigVDPAU(AVCodecContext* avctx, int ref_frames) + { + FiniVDPAUOutput(); + + VdpStatus vdp_st; + VdpDecoderProfile vdp_decoder_profile; +- vid_width = avctx->width; +- vid_height = avctx->height; +- surface_width = avctx->coded_width; +- surface_height = avctx->coded_height; +- +- past[1] = past[0] = current = future = NULL; +- CLog::Log(LOGNOTICE, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i",OutWidth,vid_width,surface_width); +- CLog::Log(LOGNOTICE, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",OutHeight,vid_height,surface_height); +- ReadFormatOf(avctx->pix_fmt, vdp_decoder_profile, vdp_chroma_type); ++ ++ m_vdpauConfig.vidWidth = avctx->width; ++ m_vdpauConfig.vidHeight = avctx->height; ++ m_vdpauConfig.surfaceWidth = avctx->coded_width; ++ m_vdpauConfig.surfaceHeight = avctx->coded_height; ++ ++ SetWidthHeight(avctx->width,avctx->height); ++ ++ CLog::Log(LOGNOTICE, " (VDPAU) screenWidth:%i vidWidth:%i surfaceWidth:%i",m_vdpauConfig.outWidth,m_vdpauConfig.vidWidth,m_vdpauConfig.surfaceWidth); ++ CLog::Log(LOGNOTICE, " (VDPAU) screenHeight:%i vidHeight:%i surfaceHeight:%i",m_vdpauConfig.outHeight,m_vdpauConfig.vidHeight,m_vdpauConfig.surfaceHeight); ++ ++ ReadFormatOf(avctx->pix_fmt, vdp_decoder_profile, m_vdpauConfig.vdpChromaType); + + if(avctx->pix_fmt == PIX_FMT_VDPAU_H264) + { +- max_references = ref_frames; +- if (max_references > 16) max_references = 16; +- if (max_references < 5) max_references = 5; ++ m_vdpauConfig.maxReferences = ref_frames; ++ if (m_vdpauConfig.maxReferences > 16) m_vdpauConfig.maxReferences = 16; ++ if (m_vdpauConfig.maxReferences < 5) m_vdpauConfig.maxReferences = 5; + } + else +- max_references = 2; ++ m_vdpauConfig.maxReferences = 2; + +- vdp_st = vdp_decoder_create(vdp_device, ++ vdp_st = m_vdpauConfig.vdpProcs.vdp_decoder_create(m_vdpauConfig.vdpDevice, + vdp_decoder_profile, +- surface_width, +- surface_height, +- max_references, +- &decoder); +- if (CheckStatus(vdp_st, __LINE__)) +- return false; +- +- m_vdpauOutputMethod = OUTPUT_NONE; +- +- vdpauConfigured = true; +- return true; +-} +- +-bool CVDPAU::ConfigOutputMethod(AVCodecContext *avctx, AVFrame *pFrame) +-{ +- VdpStatus vdp_st; +- +- if (m_vdpauOutputMethod == OUTPUT_PIXMAP) +- return true; +- +- FiniOutputMethod(); +- +- MakePixmap(avctx->width,avctx->height); +- +- vdp_st = vdp_presentation_queue_target_create_x11(vdp_device, +- m_Pixmap, //x_window, +- &vdp_flip_target); ++ m_vdpauConfig.surfaceWidth, ++ m_vdpauConfig.surfaceHeight, ++ m_vdpauConfig.maxReferences, ++ &m_vdpauConfig.vdpDecoder); + if (CheckStatus(vdp_st, __LINE__)) + return false; + +- vdp_st = vdp_presentation_queue_create(vdp_device, +- vdp_flip_target, +- &vdp_flip_queue); +- if (CheckStatus(vdp_st, __LINE__)) +- return false; +- +- totalAvailableOutputSurfaces = 0; +- +- int tmpMaxOutputSurfaces = NUM_OUTPUT_SURFACES; +- if (vid_width == FULLHD_WIDTH) +- tmpMaxOutputSurfaces = NUM_OUTPUT_SURFACES_FOR_FULLHD; +- +- // Creation of outputSurfaces +- for (int i = 0; i < NUM_OUTPUT_SURFACES && i < tmpMaxOutputSurfaces; i++) +- { +- vdp_st = vdp_output_surface_create(vdp_device, +- VDP_RGBA_FORMAT_B8G8R8A8, +- OutWidth, +- OutHeight, +- &outputSurfaces[i]); +- if (CheckStatus(vdp_st, __LINE__)) ++ // initialize output ++ CSingleLock lock(g_graphicsContext); ++ m_vdpauConfig.stats = &m_bufferStats; ++ m_vdpauConfig.vdpau = this; ++ m_bufferStats.Reset(); ++ m_vdpauOutput.Start(); ++ Message *reply; ++ if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT, ++ &reply, ++ 2000, ++ &m_vdpauConfig, ++ sizeof(m_vdpauConfig))) ++ { ++ bool success = reply->signal == COutputControlProtocol::ACC ? true : false; ++ reply->Release(); ++ if (!success) ++ { ++ CLog::Log(LOGERROR, "VDPAU::%s - vdpau output returned error", __FUNCTION__); ++ m_vdpauOutput.Dispose(); + return false; +- totalAvailableOutputSurfaces++; +- } +- CLog::Log(LOGNOTICE, " (VDPAU) Total Output Surfaces Available: %i of a max (tmp: %i const: %i)", +- totalAvailableOutputSurfaces, +- tmpMaxOutputSurfaces, +- NUM_OUTPUT_SURFACES); +- +- // create 3 pitches of black lines needed for clipping top +- // and bottom lines when de-interlacing +- m_BlackBar = new uint32_t[3*OutWidth]; +- memset(m_BlackBar, 0, 3*OutWidth*sizeof(uint32_t)); +- +- surfaceNum = presentSurfaceNum = 0; +- outputSurface = presentSurface = VDP_INVALID_HANDLE; +- videoMixer = VDP_INVALID_HANDLE; +- +- m_vdpauOutputMethod = OUTPUT_PIXMAP; +- +- return true; +-} +- +-bool CVDPAU::FiniOutputMethod() +-{ +- VdpStatus vdp_st; +- +- if (vdp_flip_queue != VDP_INVALID_HANDLE) +- { +- vdp_st = vdp_presentation_queue_destroy(vdp_flip_queue); +- vdp_flip_queue = VDP_INVALID_HANDLE; +- CheckStatus(vdp_st, __LINE__); +- } +- +- if (vdp_flip_target != VDP_INVALID_HANDLE) +- { +- vdp_st = vdp_presentation_queue_target_destroy(vdp_flip_target); +- vdp_flip_target = VDP_INVALID_HANDLE; +- CheckStatus(vdp_st, __LINE__); +- } +- +- if (m_glPixmap) +- { +- CLog::Log(LOGDEBUG, "GLX: Destroying glPixmap"); +- glXDestroyPixmap(m_Display, m_glPixmap); +- m_glPixmap = None; +- } +- +- if (m_Pixmap) +- { +- CLog::Log(LOGDEBUG, "GLX: Destroying XPixmap"); +- XFreePixmap(m_Display, m_Pixmap); +- m_Pixmap = None; +- } +- +- outputSurface = presentSurface = VDP_INVALID_HANDLE; +- +- for (int i = 0; i < totalAvailableOutputSurfaces; i++) +- { +- if (outputSurfaces[i] == VDP_INVALID_HANDLE) +- continue; +- vdp_st = vdp_output_surface_destroy(outputSurfaces[i]); +- outputSurfaces[i] = VDP_INVALID_HANDLE; +- CheckStatus(vdp_st, __LINE__); +- } +- +- if (videoMixer != VDP_INVALID_HANDLE) +- { +- vdp_st = vdp_video_mixer_destroy(videoMixer); +- videoMixer = VDP_INVALID_HANDLE; +- CheckStatus(vdp_st, __LINE__); ++ } + } +- +- if (m_BlackBar) ++ else + { +- delete [] m_BlackBar; +- m_BlackBar = NULL; ++ CLog::Log(LOGERROR, "VDPAU::%s - failed to init output", __FUNCTION__); ++ m_vdpauOutput.Dispose(); ++ return false; + } + +- while (!m_DVDVideoPics.empty()) +- m_DVDVideoPics.pop(); +- ++ m_inMsgEvent.Reset(); ++ m_vdpauConfigured = true; + return true; + } + +-void CVDPAU::SpewHardwareAvailable() //Copyright (c) 2008 Wladimir J. van der Laan -- VDPInfo ++void CDecoder::SpewHardwareAvailable() //CopyrighVDPAUt (c) 2008 Wladimir J. van der Laan -- VDPInfo + { + VdpStatus rv; + CLog::Log(LOGNOTICE,"VDPAU Decoder capabilities:"); +@@ -1102,7 +632,7 @@ + { + VdpBool is_supported = false; + uint32_t max_level, max_macroblocks, max_width, max_height; +- rv = vdp_decoder_query_caps(vdp_device, decoder_profiles[x].id, ++ rv = m_vdpauConfig.vdpProcs.vdp_decoder_query_caps(m_vdpauConfig.vdpDevice, decoder_profiles[x].id, + &is_supported, &max_level, &max_macroblocks, &max_width, &max_height); + if(rv == VDP_STATUS_OK && is_supported) + { +@@ -1111,13 +641,13 @@ + } + } + CLog::Log(LOGNOTICE,"------------------------------------"); +- m_feature_count = 0; ++ m_vdpauConfig.featureCount = 0; + #define CHECK_SUPPORT(feature) \ + do { \ + VdpBool supported; \ +- if(vdp_video_mixer_query_feature_support(vdp_device, feature, &supported) == VDP_STATUS_OK && supported) { \ ++ if(m_vdpauConfig.vdpProcs.vdp_video_mixer_query_feature_support(m_vdpauConfig.vdpDevice, feature, &supported) == VDP_STATUS_OK && supported) { \ + CLog::Log(LOGNOTICE, "Mixer feature: "#feature); \ +- m_features[m_feature_count++] = feature; \ ++ m_vdpauConfig.vdpFeatures[m_vdpauConfig.featureCount++] = feature; \ + } \ + } while(false) + +@@ -1141,7 +671,7 @@ + + } + +-bool CVDPAU::IsSurfaceValid(vdpau_render_state *render) ++bool CDecoder::IsSurfaceValid(vdpau_render_state *render) + { + // find render state in queue + bool found(false); +@@ -1168,34 +698,33 @@ + return true; + } + +-int CVDPAU::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic) ++int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic) + { + //CLog::Log(LOGNOTICE,"%s",__FUNCTION__); + CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque; +- CVDPAU* vdp = (CVDPAU*)ctx->GetHardware(); +- struct pictureAge* pA = &vdp->picAge; ++ CDecoder* vdp = (CDecoder*)ctx->GetHardware(); + + // while we are waiting to recover we can't do anything +- CSharedLock lock(vdp->m_DecoderSection); ++ CSingleLock lock(vdp->m_DecoderSection); + +- { CSharedLock dLock(vdp->m_DisplaySection); +- if(vdp->m_DisplayState != VDPAU_OPEN) +- { +- CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery"); +- return -1; +- } ++ if(vdp->m_DisplayState != VDPAU_OPEN) ++ { ++ CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - returning due to awaiting recovery"); ++ return -1; + } + + vdpau_render_state * render = NULL; + + // find unused surface +- for(unsigned int i = 0; i < vdp->m_videoSurfaces.size(); i++) +- { +- if(!(vdp->m_videoSurfaces[i]->state & (FF_VDPAU_STATE_USED_FOR_REFERENCE | FF_VDPAU_STATE_USED_FOR_RENDER))) ++ { CSingleLock lock(vdp->m_videoSurfaceSec); ++ for(unsigned int i = 0; i < vdp->m_videoSurfaces.size(); i++) + { +- render = vdp->m_videoSurfaces[i]; +- render->state = 0; +- break; ++ if(!(vdp->m_videoSurfaces[i]->state & (FF_VDPAU_STATE_USED_FOR_REFERENCE | FF_VDPAU_STATE_USED_FOR_RENDER))) ++ { ++ render = vdp->m_videoSurfaces[i]; ++ render->state = 0; ++ break; ++ } + } + } + +@@ -1204,21 +733,22 @@ + { + // create a new surface + VdpDecoderProfile profile; +- ReadFormatOf(avctx->pix_fmt, profile, vdp->vdp_chroma_type); ++ ReadFormatOf(avctx->pix_fmt, profile, vdp->m_vdpauConfig.vdpChromaType); + render = (vdpau_render_state*)calloc(sizeof(vdpau_render_state), 1); + if (render == NULL) + { + CLog::Log(LOGWARNING, "CVDPAU::FFGetBuffer - calloc failed"); + return -1; + } ++ CSingleLock lock(vdp->m_videoSurfaceSec); + render->surface = VDP_INVALID_HANDLE; + vdp->m_videoSurfaces.push_back(render); + } + + if (render->surface == VDP_INVALID_HANDLE) + { +- vdp_st = vdp->vdp_video_surface_create(vdp->vdp_device, +- vdp->vdp_chroma_type, ++ vdp_st = vdp->m_vdpauConfig.vdpProcs.vdp_video_surface_create(vdp->m_vdpauConfig.vdpDevice, ++ vdp->m_vdpauConfig.vdpChromaType, + avctx->coded_width, + avctx->coded_height, + &render->surface); +@@ -1239,18 +769,6 @@ + + pic->linesize[0] = pic->linesize[1] = pic->linesize[2] = 0; + +- if(pic->reference) +- { +- pA->ip_age[0]= pA->ip_age[1]+1; +- pA->ip_age[1]= 1; +- pA->b_age++; +- } +- else +- { +- pA->ip_age[0]++; +- pA->ip_age[1]++; +- pA->b_age = 1; +- } + pic->type= FF_BUFFER_TYPE_USER; + + render->state |= FF_VDPAU_STATE_USED_FOR_REFERENCE; +@@ -1258,15 +776,16 @@ + return 0; + } + +-void CVDPAU::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic) ++void CDecoder::FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic) + { + //CLog::Log(LOGNOTICE,"%s",__FUNCTION__); + CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque; +- CVDPAU* vdp = (CVDPAU*)ctx->GetHardware(); ++ CDecoder* vdp = (CDecoder*)ctx->GetHardware(); ++ + vdpau_render_state * render; + unsigned int i; + +- CSharedLock lock(vdp->m_DecoderSection); ++ CSingleLock lock(vdp->m_DecoderSection); + + render=(vdpau_render_state*)pic->data[0]; + if(!render) +@@ -1275,6 +794,8 @@ + return; + } + ++ CSingleLock vLock(vdp->m_videoSurfaceSec); ++ render->state &= ~FF_VDPAU_STATE_USED_FOR_REFERENCE; + for(i=0; i<4; i++) + pic->data[i]= NULL; + +@@ -1289,21 +810,18 @@ + } + + +-void CVDPAU::FFDrawSlice(struct AVCodecContext *s, ++void CDecoder::FFDrawSlice(struct AVCodecContext *s, + const AVFrame *src, int offset[4], + int y, int type, int height) + { + CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)s->opaque; +- CVDPAU* vdp = (CVDPAU*)ctx->GetHardware(); ++ CDecoder* vdp = (CDecoder*)ctx->GetHardware(); + + // while we are waiting to recover we can't do anything +- CSharedLock lock(vdp->m_DecoderSection); +- +- { CSharedLock dLock(vdp->m_DisplaySection); +- if(vdp->m_DisplayState != VDPAU_OPEN) +- return; +- } ++ CSingleLock lock(vdp->m_DecoderSection); + ++ if(vdp->m_DisplayState != VDPAU_OPEN) ++ return; + + if(src->linesize[0] || src->linesize[1] || src->linesize[2] + || offset[0] || offset[1] || offset[2]) +@@ -1333,59 +851,41 @@ + if(s->pix_fmt == PIX_FMT_VDPAU_H264) + max_refs = render->info.h264.num_ref_frames; + +- if(vdp->decoder == VDP_INVALID_HANDLE +- || vdp->vdpauConfigured == false +- || vdp->max_references < max_refs) ++ if(vdp->m_vdpauConfig.vdpDecoder == VDP_INVALID_HANDLE ++ || vdp->m_vdpauConfigured == false ++ || vdp->m_vdpauConfig.maxReferences < max_refs) + { + if(!vdp->ConfigVDPAU(s, max_refs)) + return; + } + +- vdp_st = vdp->vdp_decoder_render(vdp->decoder, ++ uint64_t startTime = CurrentHostCounter(); ++ uint16_t decoded, processed, rend; ++ vdp->m_bufferStats.Get(decoded, processed, rend); ++ vdp_st = vdp->m_vdpauConfig.vdpProcs.vdp_decoder_render(vdp->m_vdpauConfig.vdpDecoder, + render->surface, + (VdpPictureInfo const *)&(render->info), + render->bitstream_buffers_used, + render->bitstream_buffers); + vdp->CheckStatus(vdp_st, __LINE__); ++ uint64_t diff = CurrentHostCounter() - startTime; ++ if (diff*1000/CurrentHostFrequency() > 30) ++ CLog::Log(LOGWARNING,"CVDPAU::DrawSlice - VdpDecoderRender long decoding: %d ms, dec: %d, proc: %d, rend: %d", (int)((diff*1000)/CurrentHostFrequency()), decoded, processed, rend); ++ + } + +-int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame) +-{ +- //CLog::Log(LOGNOTICE,"%s",__FUNCTION__); +- VdpStatus vdp_st; +- VdpTime time; + ++int CDecoder::Decode(AVCodecContext *avctx, AVFrame *pFrame, bool bSoftDrain, bool bHardDrain) ++{ + int result = Check(avctx); + if (result) + return result; + +- CSharedLock lock(m_DecoderSection); ++ CSingleLock lock(m_DecoderSection); + +- if (!vdpauConfigured) ++ if (!m_vdpauConfigured) + return VC_ERROR; + +- // configure vdpau output +- if (!ConfigOutputMethod(avctx, pFrame)) +- return VC_FLUSHED; +- +- outputSurface = outputSurfaces[surfaceNum]; +- +- CheckFeatures(); +- +- if (( (int)outRectVid.x1 != OutWidth ) || +- ( (int)outRectVid.y1 != OutHeight )) +- { +- outRectVid.x0 = 0; +- outRectVid.y0 = 0; +- outRectVid.x1 = OutWidth; +- outRectVid.y1 = OutHeight; +- } +- +- EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; +- EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; +- if (method == VS_INTERLACEMETHOD_AUTO) +- method = AutoInterlaceMethod(); +- + if(pFrame) + { // we have a new frame from decoder + +@@ -1393,7 +893,10 @@ + if(!render) // old style ffmpeg gave data on plane 0 + render = (vdpau_render_state*)pFrame->data[0]; + if(!render) ++ { ++ CLog::Log(LOGERROR, "CVDPAU::Decode: no valid frame"); + return VC_ERROR; ++ } + + // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid + if (!IsSurfaceValid(render)) +@@ -1402,258 +905,166 @@ + return VC_BUFFER; + } + ++ CSingleLock lock(m_videoSurfaceSec); + render->state |= FF_VDPAU_STATE_USED_FOR_RENDER; ++ lock.Leave(); + +- ClearUsedForRender(&past[0]); +- past[0] = past[1]; +- past[1] = current; +- current = future; +- future = render; +- +- DVDVideoPicture DVDPic; +- memset(&DVDPic, 0, sizeof(DVDVideoPicture)); +- ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&DVDPic); +- m_DVDVideoPics.push(DVDPic); +- +- int pics = m_DVDVideoPics.size(); +- if (pics < 2) +- return VC_BUFFER; +- else if (pics > 2) +- { +- // this should not normally happen +- CLog::Log(LOGERROR, "CVDPAU::Decode - invalid number of pictures in queue"); +- while (pics-- != 2) +- m_DVDVideoPics.pop(); ++ // send frame to output for processing ++ CVdpauDecodedPicture pic; ++ memset(&pic.DVDPic, 0, sizeof(pic.DVDPic)); ++ ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic); ++ pic.render = render; ++ m_bufferStats.IncDecoded(); ++ m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic)); ++ ++ m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC); ++ } ++ ++ int retval = 0; ++ uint16_t decoded, processed, render; ++ Message *msg; ++ while (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputControlProtocol::ERROR) ++ { ++ m_DisplayState = VDPAU_ERROR; ++ retval |= VC_ERROR; + } ++ msg->Release(); ++ } + +- if (mode == VS_DEINTERLACEMODE_FORCE +- || (mode == VS_DEINTERLACEMODE_AUTO && m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED)) ++ m_bufferStats.Get(decoded, processed, render); ++ ++ uint64_t startTime = CurrentHostCounter(); ++ while (!retval) ++ { ++ if (m_vdpauOutput.m_dataPort.ReceiveInMessage(&msg)) + { +- if((method == VS_INTERLACEMETHOD_AUTO_ION +- || method == VS_INTERLACEMETHOD_VDPAU_BOB +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF +- || method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE )) ++ if (msg->signal == COutputDataProtocol::PICTURE) + { +- if((method == VS_INTERLACEMETHOD_AUTO_ION && vid_height > 576) +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF +- || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF +- || avctx->skip_frame == AVDISCARD_NONREF) +- m_mixerstep = 0; +- else +- m_mixerstep = 1; +- +- if(m_DVDVideoPics.front().iFlags & DVP_FLAG_TOP_FIELD_FIRST) +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; +- else +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD; ++ if (m_presentPicture) ++ { ++ m_presentPicture->ReturnUnused(); ++ m_presentPicture = 0; ++ } ++ ++ m_presentPicture = *(CVdpauRenderPicture**)msg->data; ++ m_presentPicture->vdpau = this; ++ m_bufferStats.DecRender(); ++ m_bufferStats.Get(decoded, processed, render); ++ retval |= VC_PICTURE; ++ msg->Release(); ++ break; ++ } ++ msg->Release(); ++ } ++ else if (m_vdpauOutput.m_controlPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputControlProtocol::STATS) ++ { ++ m_bufferStats.Get(decoded, processed, render); + } + else + { +- m_mixerstep = 0; +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; ++ m_DisplayState = VDPAU_ERROR; ++ retval |= VC_ERROR; + } ++ msg->Release(); + } +- else ++ ++ if ((m_codecControl & DVP_FLAG_DRAIN)) + { +- m_mixerstep = 0; +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; ++ if (decoded + processed + render < 4) ++ { ++ retval |= VC_BUFFER; ++ } + } +- +- } +- else if(m_mixerstep == 1) +- { // no new frame given, output second field of old frame +- +- if(avctx->skip_frame == AVDISCARD_NONREF) ++ else + { +- ClearUsedForRender(&past[1]); +- m_DVDVideoPics.pop(); +- return VC_BUFFER; ++ if (decoded < 4 && (processed + render) < 3) ++ { ++ retval |= VC_BUFFER; ++ } + } + +- m_mixerstep = 2; +- if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD) +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD; +- else +- m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; ++ if (!retval && !m_inMsgEvent.WaitMSec(2000)) ++ break; + } +- else ++ uint64_t diff = CurrentHostCounter() - startTime; ++ if (retval & VC_PICTURE) + { +- CLog::Log(LOGERROR, "CVDPAU::Decode - invalid mixer state reached"); +- return VC_BUFFER; ++ m_bufferStats.SetParams(diff, m_codecControl); + } ++ if (diff*1000/CurrentHostFrequency() > 50) ++ CLog::Log(LOGDEBUG,"CVDPAU::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency())); + +- VdpVideoSurface past_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE }; +- VdpVideoSurface futu_surfaces[1] = { VDP_INVALID_HANDLE }; +- +- if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME) +- { +- if (past[0]) +- past_surfaces[1] = past[0]->surface; +- if (past[1]) +- past_surfaces[0] = past[1]->surface; +- futu_surfaces[0] = future->surface; +- } +- else ++ if (!retval) + { +- if(m_mixerstep == 1) +- { // first field +- if (past[1]) +- { +- past_surfaces[1] = past[1]->surface; +- past_surfaces[0] = past[1]->surface; +- } +- futu_surfaces[0] = current->surface; +- } +- else +- { // second field +- if (past[1]) +- past_surfaces[1] = past[1]->surface; +- past_surfaces[0] = current->surface; +- futu_surfaces[0] = future->surface; +- } ++ CLog::Log(LOGERROR, "VDPAU::%s - timed out waiting for output message", __FUNCTION__); ++ m_DisplayState = VDPAU_ERROR; ++ retval |= VC_ERROR; + } + +- vdp_st = vdp_presentation_queue_block_until_surface_idle(vdp_flip_queue,outputSurface,&time); +- +- VdpRect sourceRect = {0,0,vid_width, vid_height}; +- +- vdp_st = vdp_video_mixer_render(videoMixer, +- VDP_INVALID_HANDLE, +- 0, +- m_mixerfield, +- 2, +- past_surfaces, +- current->surface, +- 1, +- futu_surfaces, +- &sourceRect, +- outputSurface, +- &(outRectVid), +- &(outRectVid), +- 0, +- NULL); +- CheckStatus(vdp_st, __LINE__); +- +- surfaceNum++; +- if (surfaceNum >= totalAvailableOutputSurfaces) surfaceNum = 0; ++ return retval; ++} + +- if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME) +- { +- ClearUsedForRender(&past[0]); +- return VC_BUFFER | VC_PICTURE; +- } +- else +- { +- // in order to clip top and bottom lines when de-interlacing +- // we black those lines as a work around for not working +- // background colour using the mixer +- // pixel perfect is preferred over overscanning or zooming ++bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) ++{ ++ CSingleLock lock(m_DecoderSection); + +- VdpRect clipRect = outRectVid; +- clipRect.y1 = clipRect.y0 + 2; +- uint32_t *data[] = {m_BlackBar}; +- uint32_t pitches[] = {outRectVid.x1}; +- vdp_st = vdp_output_surface_put_bits_native(outputSurface, +- (void**)data, +- pitches, +- &clipRect); +- CheckStatus(vdp_st, __LINE__); ++ if (m_DisplayState != VDPAU_OPEN) ++ return false; + +- clipRect = outRectVid; +- clipRect.y0 = clipRect.y1 - 2; +- vdp_st = vdp_output_surface_put_bits_native(outputSurface, +- (void**)data, +- pitches, +- &clipRect); +- CheckStatus(vdp_st, __LINE__); ++ *picture = m_presentPicture->DVDPic; ++ picture->vdpau = m_presentPicture; + +- if(m_mixerstep == 1) +- return VC_PICTURE; +- else +- { +- ClearUsedForRender(&past[1]); +- return VC_BUFFER | VC_PICTURE; +- } +- } ++ return true; + } + +-bool CVDPAU::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) ++void CDecoder::Reset() + { +- CSharedLock lock(m_DecoderSection); +- +- { CSharedLock dLock(m_DisplaySection); +- if (m_DisplayState != VDPAU_OPEN) +- return false; +- } ++ CSingleLock lock(m_DecoderSection); + +- *picture = m_DVDVideoPics.front(); +- // if this is the first field of an interlaced frame, we'll need +- // this same picture for the second field later +- if (m_mixerstep != 1) +- m_DVDVideoPics.pop(); +- +- picture->format = RENDER_FMT_VDPAU; +- picture->iFlags &= DVP_FLAG_DROPPED; +- picture->iWidth = OutWidth; +- picture->iHeight = OutHeight; +- picture->vdpau = this; ++ if (!m_vdpauConfigured) ++ return; + +- if(m_mixerstep) +- { +- picture->iRepeatPicture = -0.5; +- if(m_mixerstep > 1) ++ Message *reply; ++ if (m_vdpauOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH, ++ &reply, ++ 2000)) ++ { ++ bool success = reply->signal == COutputControlProtocol::ACC ? true : false; ++ reply->Release(); ++ if (!success) + { +- picture->dts = DVD_NOPTS_VALUE; +- picture->pts = DVD_NOPTS_VALUE; ++ CLog::Log(LOGERROR, "VDPAU::%s - flush returned error", __FUNCTION__); ++ m_DisplayState = VDPAU_ERROR; + } ++ else ++ m_bufferStats.Reset(); ++ } ++ else ++ { ++ CLog::Log(LOGERROR, "VDPAU::%s - flush timed out", __FUNCTION__); ++ m_DisplayState = VDPAU_ERROR; + } +- return true; + } + +-void CVDPAU::Reset() ++bool CDecoder::CanSkipDeint() + { +- // invalidate surfaces and picture queue when seeking +- ClearUsedForRender(&past[0]); +- ClearUsedForRender(&past[1]); +- ClearUsedForRender(¤t); +- ClearUsedForRender(&future); +- +- while (!m_DVDVideoPics.empty()) +- m_DVDVideoPics.pop(); ++ return m_bufferStats.CanSkipDeint(); + } + +-void CVDPAU::Present() ++void CDecoder::ReturnRenderPicture(CVdpauRenderPicture *renderPic) + { +- //CLog::Log(LOGNOTICE,"%s",__FUNCTION__); +- VdpStatus vdp_st; +- +- CSharedLock lock(m_DecoderSection); +- +- { CSharedLock dLock(m_DisplaySection); +- if (m_DisplayState != VDPAU_OPEN) +- return; +- } +- +- presentSurface = outputSurface; +- +- vdp_st = vdp_presentation_queue_display(vdp_flip_queue, +- presentSurface, +- 0, +- 0, +- 0); +- CheckStatus(vdp_st, __LINE__); ++ m_vdpauOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic)); + } + +-bool CVDPAU::CheckStatus(VdpStatus vdp_st, int line) ++bool CDecoder::CheckStatus(VdpStatus vdp_st, int line) + { + if (vdp_st != VDP_STATUS_OK) + { +- CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", vdp_get_error_string(vdp_st), vdp_st, __FILE__, line); +- +- CExclusiveLock lock(m_DisplaySection); ++ CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_vdpauConfig.vdpProcs.vdp_get_error_string(vdp_st), vdp_st, __FILE__, line); + + if(m_DisplayState == VDPAU_OPEN) + { +@@ -1671,4 +1082,2422 @@ + return false; + } + ++//----------------------------------------------------------------------------- ++// RenderPicture ++//----------------------------------------------------------------------------- ++ ++CVdpauRenderPicture* CVdpauRenderPicture::Acquire() ++{ ++ CSingleLock lock(*renderPicSection); ++ ++ if (refCount == 0) ++ vdpau->Acquire(); ++ ++ refCount++; ++ return this; ++} ++ ++long CVdpauRenderPicture::Release() ++{ ++ CSingleLock lock(*renderPicSection); ++ ++ refCount--; ++ if (refCount > 0) ++ return refCount; ++ ++ lock.Leave(); ++ vdpau->ReturnRenderPicture(this); ++ vdpau->ReleasePicReference(); ++ ++ return refCount; ++} ++ ++void CVdpauRenderPicture::ReturnUnused() ++{ ++ { CSingleLock lock(*renderPicSection); ++ if (refCount > 0) ++ return; ++ } ++ if (vdpau) ++ vdpau->ReturnRenderPicture(this); ++} ++//----------------------------------------------------------------------------- ++// Mixer ++//----------------------------------------------------------------------------- ++CMixer::CMixer(CEvent *inMsgEvent) : ++ CThread("Vdpau Mixer Thread"), ++ m_controlPort("ControlPort", inMsgEvent, &m_outMsgEvent), ++ m_dataPort("DataPort", inMsgEvent, &m_outMsgEvent) ++{ ++ m_inMsgEvent = inMsgEvent; ++} ++ ++CMixer::~CMixer() ++{ ++ Dispose(); ++} ++ ++void CMixer::Start() ++{ ++ Create(); ++} ++ ++void CMixer::Dispose() ++{ ++ m_bStop = true; ++ m_outMsgEvent.Set(); ++ StopThread(); ++ ++ m_controlPort.Purge(); ++ m_dataPort.Purge(); ++} ++ ++void CMixer::OnStartup() ++{ ++ CLog::Log(LOGNOTICE, "CMixer::OnStartup: Output Thread created"); ++} ++ ++void CMixer::OnExit() ++{ ++ CLog::Log(LOGNOTICE, "CMixer::OnExit: Output Thread terminated"); ++} ++ ++enum MIXER_STATES ++{ ++ M_TOP = 0, // 0 ++ M_TOP_ERROR, // 1 ++ M_TOP_UNCONFIGURED, // 2 ++ M_TOP_CONFIGURED, // 3 ++ M_TOP_CONFIGURED_WAIT1, // 4 ++ M_TOP_CONFIGURED_STEP1, // 5 ++ M_TOP_CONFIGURED_WAIT2, // 6 ++ M_TOP_CONFIGURED_STEP2, // 7 ++}; ++ ++int MIXER_parentStates[] = { ++ -1, ++ 0, //TOP_ERROR ++ 0, //TOP_UNCONFIGURED ++ 0, //TOP_CONFIGURED ++ 3, //TOP_CONFIGURED_WAIT1 ++ 3, //TOP_CONFIGURED_STEP1 ++ 3, //TOP_CONFIGURED_WAIT2 ++ 3, //TOP_CONFIGURED_STEP2 ++}; ++ ++void CMixer::StateMachine(int signal, Protocol *port, Message *msg) ++{ ++ for (int state = m_state; ; state = MIXER_parentStates[state]) ++ { ++ switch (state) ++ { ++ case M_TOP: // TOP ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case CMixerControlProtocol::FLUSH: ++ Flush(); ++ msg->Reply(CMixerControlProtocol::ACC); ++ return; ++ default: ++ break; ++ } ++ } ++ { ++ std::string portName = port == NULL ? "timer" : port->portName; ++ CLog::Log(LOGWARNING, "CMixer::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state); ++ } ++ return; ++ ++ case M_TOP_ERROR: // TOP ++ break; ++ ++ case M_TOP_UNCONFIGURED: ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case CMixerControlProtocol::INIT: ++ CVdpauConfig *data; ++ data = (CVdpauConfig*)msg->data; ++ if (data) ++ { ++ m_config = *data; ++ } ++ Init(); ++ if (!m_vdpError) ++ { ++ m_state = M_TOP_CONFIGURED_WAIT1; ++ msg->Reply(CMixerControlProtocol::ACC); ++ } ++ else ++ { ++ msg->Reply(CMixerControlProtocol::ERROR); ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case M_TOP_CONFIGURED: ++ if (port == &m_dataPort) ++ { ++ switch (signal) ++ { ++ case CMixerDataProtocol::FRAME: ++ CVdpauDecodedPicture *frame; ++ frame = (CVdpauDecodedPicture*)msg->data; ++ if (frame) ++ { ++ m_decodedPics.push(*frame); ++ } ++ m_extTimeout = 0; ++ return; ++ case CMixerDataProtocol::BUFFER: ++ VdpOutputSurface *surf; ++ surf = (VdpOutputSurface*)msg->data; ++ if (surf) ++ { ++ m_outputSurfaces.push(*surf); ++ } ++ m_extTimeout = 0; ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case M_TOP_CONFIGURED_WAIT1: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case CMixerControlProtocol::TIMEOUT: ++ if (!m_decodedPics.empty() && !m_outputSurfaces.empty()) ++ { ++ m_state = M_TOP_CONFIGURED_STEP1; ++ m_bStateMachineSelfTrigger = true; ++ } ++ else ++ { ++// if (m_extTimeout != 0) ++// { ++// SetPostProcFeatures(false); ++// CLog::Log(LOGWARNING,"CVDPAU::Mixer timeout - decoded: %d, outputSurf: %d", (int)m_decodedPics.size(), (int)m_outputSurfaces.size()); ++// } ++ m_extTimeout = 100; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case M_TOP_CONFIGURED_STEP1: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case CMixerControlProtocol::TIMEOUT: ++ m_mixerInput.push_front(m_decodedPics.front()); ++ m_decodedPics.pop(); ++ if (m_mixerInput.size() < 2) ++ { ++ m_state = M_TOP_CONFIGURED_WAIT1; ++ m_extTimeout = 0; ++ return; ++ } ++ InitCycle(); ++ ProcessPicture(); ++ if (m_vdpError) ++ { ++ m_state = M_TOP_CONFIGURED_WAIT1; ++ m_extTimeout = 1000; ++ return; ++ } ++ if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420) ++ m_outputSurfaces.pop(); ++ m_config.stats->IncProcessed(); ++ m_config.stats->DecDecoded(); ++ m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture)); ++ if (m_mixersteps > 1) ++ { ++ m_state = M_TOP_CONFIGURED_WAIT2; ++ m_extTimeout = 0; ++ } ++ else ++ { ++ FiniCycle(); ++ m_state = M_TOP_CONFIGURED_WAIT1; ++ m_extTimeout = 0; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case M_TOP_CONFIGURED_WAIT2: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case CMixerControlProtocol::TIMEOUT: ++ if (!m_outputSurfaces.empty()) ++ { ++ m_state = M_TOP_CONFIGURED_STEP2; ++ m_bStateMachineSelfTrigger = true; ++ } ++ else ++ { ++// if (m_extTimeout != 0) ++// { ++// SetPostProcFeatures(false); ++// CLog::Log(LOGNOTICE,"---mixer wait2 decoded: %d, outputSurf: %d", (int)m_decodedPics.size(), (int)m_outputSurfaces.size()); ++// } ++ m_extTimeout = 100; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case M_TOP_CONFIGURED_STEP2: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case CMixerControlProtocol::TIMEOUT: ++ m_processPicture.outputSurface = m_outputSurfaces.front(); ++ m_mixerstep = 1; ++ ProcessPicture(); ++ if (m_vdpError) ++ { ++ m_state = M_TOP_CONFIGURED_WAIT1; ++ m_extTimeout = 1000; ++ return; ++ } ++ if (m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420) ++ m_outputSurfaces.pop(); ++ m_config.stats->IncProcessed(); ++ m_dataPort.SendInMessage(CMixerDataProtocol::PICTURE,&m_processPicture,sizeof(m_processPicture)); ++ FiniCycle(); ++ m_state = M_TOP_CONFIGURED_WAIT1; ++ m_extTimeout = 0; ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ default: // we are in no state, should not happen ++ CLog::Log(LOGERROR, "CMixer::%s - no valid state: %d", __FUNCTION__, m_state); ++ return; ++ } ++ } // for ++} ++ ++void CMixer::Process() ++{ ++ Message *msg; ++ Protocol *port; ++ bool gotMsg; ++ ++ m_state = M_TOP_UNCONFIGURED; ++ m_extTimeout = 1000; ++ m_bStateMachineSelfTrigger = false; ++ ++ while (!m_bStop) ++ { ++ gotMsg = false; ++ ++ if (m_bStateMachineSelfTrigger) ++ { ++ m_bStateMachineSelfTrigger = false; ++ // self trigger state machine ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ continue; ++ } ++ // check control port ++ else if (m_controlPort.ReceiveOutMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_controlPort; ++ } ++ // check data port ++ else if (m_dataPort.ReceiveOutMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_dataPort; ++ } ++ ++ if (gotMsg) ++ { ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ continue; ++ } ++ ++ // wait for message ++ else if (m_outMsgEvent.WaitMSec(m_extTimeout)) ++ { ++ continue; ++ } ++ // time out ++ else ++ { ++ msg = m_controlPort.GetMessage(); ++ msg->signal = CMixerControlProtocol::TIMEOUT; ++ port = 0; ++ // signal timeout to state machine ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ } ++ } ++ Uninit(); ++} ++ ++void CMixer::CreateVdpauMixer() ++{ ++ CLog::Log(LOGNOTICE, " (VDPAU) Creating the video mixer"); ++ ++ InitCSCMatrix(m_config.vidWidth); ++ ++ VdpVideoMixerParameter parameters[] = { ++ VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH, ++ VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT, ++ VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE}; ++ ++ void const * parameter_values[] = { ++ &m_config.surfaceWidth, ++ &m_config.surfaceHeight, ++ &m_config.vdpChromaType}; ++ ++ VdpStatus vdp_st = VDP_STATUS_ERROR; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_create(m_config.vdpDevice, ++ m_config.featureCount, ++ m_config.vdpFeatures, ++ ARSIZE(parameters), ++ parameters, ++ parameter_values, ++ &m_videoMixer); ++ CheckStatus(vdp_st, __LINE__); ++ ++ // create 3 pitches of black lines needed for clipping top ++ // and bottom lines when de-interlacing ++ m_BlackBar = new uint32_t[3*m_config.outWidth]; ++ memset(m_BlackBar, 0, 3*m_config.outWidth*sizeof(uint32_t)); ++ ++} ++ ++void CMixer::InitCSCMatrix(int Width) ++{ ++ VdpStatus vdp_st; ++ m_Procamp.struct_version = VDP_PROCAMP_VERSION; ++ m_Procamp.brightness = 0.0; ++ m_Procamp.contrast = 1.0; ++ m_Procamp.saturation = 1.0; ++ m_Procamp.hue = 0; ++ vdp_st = m_config.vdpProcs.vdp_generate_csc_matrix(&m_Procamp, ++ (Width < 1000)? VDP_COLOR_STANDARD_ITUR_BT_601 : VDP_COLOR_STANDARD_ITUR_BT_709, ++ &m_CSCMatrix); ++ CheckStatus(vdp_st, __LINE__); ++} ++ ++void CMixer::CheckFeatures() ++{ ++ if (m_Upscale != m_config.upscale) ++ { ++ SetHWUpscaling(); ++ m_Upscale = m_config.upscale; ++ } ++ if (m_Brightness != g_settings.m_currentVideoSettings.m_Brightness || ++ m_Contrast != g_settings.m_currentVideoSettings.m_Contrast) ++ { ++ SetColor(); ++ m_Brightness = g_settings.m_currentVideoSettings.m_Brightness; ++ m_Contrast = g_settings.m_currentVideoSettings.m_Contrast; ++ } ++ if (m_NoiseReduction != g_settings.m_currentVideoSettings.m_NoiseReduction) ++ { ++ m_NoiseReduction = g_settings.m_currentVideoSettings.m_NoiseReduction; ++ SetNoiseReduction(); ++ } ++ if (m_Sharpness != g_settings.m_currentVideoSettings.m_Sharpness) ++ { ++ m_Sharpness = g_settings.m_currentVideoSettings.m_Sharpness; ++ SetSharpness(); ++ } ++ if (m_DeintMode != g_settings.m_currentVideoSettings.m_DeinterlaceMode || ++ m_Deint != g_settings.m_currentVideoSettings.m_InterlaceMethod) ++ { ++ m_DeintMode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; ++ m_Deint = g_settings.m_currentVideoSettings.m_InterlaceMethod; ++ SetDeinterlacing(); ++ } ++} ++ ++void CMixer::SetPostProcFeatures(bool postProcEnabled) ++{ ++ if (m_PostProc != postProcEnabled) ++ { ++ if (postProcEnabled) ++ { ++ SetNoiseReduction(); ++ SetSharpness(); ++ SetDeinterlacing(); ++ SetHWUpscaling(); ++ } ++ else ++ PostProcOff(); ++ m_PostProc = postProcEnabled; ++ } ++} ++ ++void CMixer::PostProcOff() ++{ ++ VdpStatus vdp_st; ++ ++ if (m_videoMixer == VDP_INVALID_HANDLE) ++ return; ++ ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL, ++ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL, ++ VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE}; ++ ++ VdpBool enabled[]={0,0,0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION}; ++ ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS}; ++ ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ DisableHQScaling(); ++} ++ ++ ++bool CMixer::GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix) ++{ ++ // instead use studioCSCKCoeffs601[3], studioCSCKCoeffs709[3] to generate float[3][4] matrix (float studioCSC[3][4]) ++ // m00 = mRY = red: luma factor (contrast factor) (1.0) ++ // m10 = mGY = green: luma factor (contrast factor) (1.0) ++ // m20 = mBY = blue: luma factor (contrast factor) (1.0) ++ // ++ // m01 = mRB = red: blue color diff coeff (0.0) ++ // m11 = mGB = green: blue color diff coeff (-2Kb(1-Kb)/(Kg)) ++ // m21 = mBB = blue: blue color diff coeff ((1-Kb)/0.5) ++ // ++ // m02 = mRR = red: red color diff coeff ((1-Kr)/0.5) ++ // m12 = mGR = green: red color diff coeff (-2Kr(1-Kr)/(Kg)) ++ // m22 = mBR = blue: red color diff coeff (0.0) ++ // ++ // m03 = mRC = red: colour zero offset (brightness factor) (-(1-Kr)/0.5 * (128/255)) ++ // m13 = mGC = green: colour zero offset (brightness factor) ((256/255) * (Kb(1-Kb) + Kr(1-Kr)) / Kg) ++ // m23 = mBC = blue: colour zero offset (brightness factor) (-(1-Kb)/0.5 * (128/255)) ++ ++ // columns ++ int Y = 0; ++ int Cb = 1; ++ int Cr = 2; ++ int C = 3; ++ // rows ++ int R = 0; ++ int G = 1; ++ int B = 2; ++ // colour standard coefficients for red, geen, blue ++ double Kr, Kg, Kb; ++ // colour diff zero position (use standard 8-bit coding precision) ++ double CDZ = 128; //256*0.5 ++ // range excursion (use standard 8-bit coding precision) ++ double EXC = 255; //256-1 ++ ++ if (colorStandard == VDP_COLOR_STANDARD_ITUR_BT_601) ++ { ++ Kr = studioCSCKCoeffs601[0]; ++ Kg = studioCSCKCoeffs601[1]; ++ Kb = studioCSCKCoeffs601[2]; ++ } ++ else // assume VDP_COLOR_STANDARD_ITUR_BT_709 ++ { ++ Kr = studioCSCKCoeffs709[0]; ++ Kg = studioCSCKCoeffs709[1]; ++ Kb = studioCSCKCoeffs709[2]; ++ } ++ // we keep luma unscaled to retain the levels present in source so that 16-235 luma is converted to RGB 16-235 ++ studioCSCMatrix[R][Y] = 1.0; ++ studioCSCMatrix[G][Y] = 1.0; ++ studioCSCMatrix[B][Y] = 1.0; ++ ++ studioCSCMatrix[R][Cb] = 0.0; ++ studioCSCMatrix[G][Cb] = (double)-2 * Kb * (1 - Kb) / Kg; ++ studioCSCMatrix[B][Cb] = (double)(1 - Kb) / 0.5; ++ ++ studioCSCMatrix[R][Cr] = (double)(1 - Kr) / 0.5; ++ studioCSCMatrix[G][Cr] = (double)-2 * Kr * (1 - Kr) / Kg; ++ studioCSCMatrix[B][Cr] = 0.0; ++ ++ studioCSCMatrix[R][C] = (double)-1 * studioCSCMatrix[R][Cr] * CDZ/EXC; ++ studioCSCMatrix[G][C] = (double)-1 * (studioCSCMatrix[G][Cb] + studioCSCMatrix[G][Cr]) * CDZ/EXC; ++ studioCSCMatrix[B][C] = (double)-1 * studioCSCMatrix[B][Cb] * CDZ/EXC; ++ ++ return true; ++} ++ ++void CMixer::SetColor() ++{ ++ VdpStatus vdp_st; ++ ++ if (m_Brightness != g_settings.m_currentVideoSettings.m_Brightness) ++ m_Procamp.brightness = (float)((g_settings.m_currentVideoSettings.m_Brightness)-50) / 100; ++ if (m_Contrast != g_settings.m_currentVideoSettings.m_Contrast) ++ m_Procamp.contrast = (float)((g_settings.m_currentVideoSettings.m_Contrast)+50) / 100; ++ ++ VdpColorStandard colorStandard; ++// if(vid_height >= 600 || vid_width > 1024) ++ if(m_config.surfaceWidth > 1000) ++ colorStandard = VDP_COLOR_STANDARD_ITUR_BT_709; ++ //vdp_st = vdp_generate_csc_matrix(&m_Procamp, VDP_COLOR_STANDARD_ITUR_BT_709, &m_CSCMatrix); ++ else ++ colorStandard = VDP_COLOR_STANDARD_ITUR_BT_601; ++ //vdp_st = vdp_generate_csc_matrix(&m_Procamp, VDP_COLOR_STANDARD_ITUR_BT_601, &m_CSCMatrix); ++ ++ VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX }; ++ if (g_guiSettings.GetBool("videoplayer.vdpaustudiolevel")) ++ { ++ float studioCSC[3][4]; ++ GenerateStudioCSCMatrix(colorStandard, studioCSC); ++ void const * pm_CSCMatix[] = { &studioCSC }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix); ++ } ++ else ++ { ++ vdp_st = m_config.vdpProcs.vdp_generate_csc_matrix(&m_Procamp, colorStandard, &m_CSCMatrix); ++ void const * pm_CSCMatix[] = { &m_CSCMatrix }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, pm_CSCMatix); ++ } ++ CheckStatus(vdp_st, __LINE__); ++} ++ ++void CMixer::SetNoiseReduction() ++{ ++ if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION)) ++ return; ++ ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION }; ++ VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL }; ++ VdpStatus vdp_st; ++ ++ if (!g_settings.m_currentVideoSettings.m_NoiseReduction) ++ { ++ VdpBool enabled[]= {0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ return; ++ } ++ VdpBool enabled[]={1}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ void* nr[] = { &g_settings.m_currentVideoSettings.m_NoiseReduction }; ++ CLog::Log(LOGNOTICE,"Setting Noise Reduction to %f",g_settings.m_currentVideoSettings.m_NoiseReduction); ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, nr); ++ CheckStatus(vdp_st, __LINE__); ++} ++ ++void CMixer::SetSharpness() ++{ ++ if(!m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_SHARPNESS)) ++ return; ++ ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_SHARPNESS }; ++ VdpVideoMixerAttribute attributes[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL }; ++ VdpStatus vdp_st; ++ ++ if (!g_settings.m_currentVideoSettings.m_Sharpness) ++ { ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ return; ++ } ++ VdpBool enabled[]={1}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ void* sh[] = { &g_settings.m_currentVideoSettings.m_Sharpness }; ++ CLog::Log(LOGNOTICE,"Setting Sharpness to %f",g_settings.m_currentVideoSettings.m_Sharpness); ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attributes), attributes, sh); ++ CheckStatus(vdp_st, __LINE__); ++} ++ ++EINTERLACEMETHOD CMixer::GetDeinterlacingMethod(bool log /* = false */) ++{ ++ EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; ++ if (method == VS_INTERLACEMETHOD_AUTO) ++ { ++ int deint = -1; ++ if (m_config.outHeight >= 720) ++ deint = g_advancedSettings.m_videoVDPAUdeintHD; ++ else ++ deint = g_advancedSettings.m_videoVDPAUdeintSD; ++ ++ if (deint != -1) ++ { ++ if (m_config.vdpau->Supports(EINTERLACEMETHOD(deint))) ++ { ++ method = EINTERLACEMETHOD(deint); ++ if (log) ++ CLog::Log(LOGNOTICE, "CVDPAU::GetDeinterlacingMethod: set de-interlacing to %d", deint); ++ } ++ else ++ { ++ if (log) ++ CLog::Log(LOGWARNING, "CVDPAU::GetDeinterlacingMethod: method for de-interlacing (advanced settings) not supported"); ++ } ++ } ++ } ++ return method; ++} ++ ++void CMixer::SetDeinterlacing() ++{ ++ VdpStatus vdp_st; ++ ++ if (m_videoMixer == VDP_INVALID_HANDLE) ++ return; ++ ++ EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; ++ EINTERLACEMETHOD method = GetDeinterlacingMethod(true); ++ ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL, ++ VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL, ++ VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE }; ++ ++ if (mode == VS_DEINTERLACEMODE_OFF) ++ { ++ VdpBool enabled[] = {0,0,0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ } ++ else ++ { ++ if (method == VS_INTERLACEMETHOD_AUTO) ++ { ++ VdpBool enabled[] = {1,0,0}; ++ if (g_advancedSettings.m_videoVDPAUtelecine) ++ enabled[2] = 1; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ } ++ else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF) ++ { ++ VdpBool enabled[] = {1,0,0}; ++ if (g_advancedSettings.m_videoVDPAUtelecine) ++ enabled[2] = 1; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ } ++ else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF) ++ { ++ VdpBool enabled[] = {1,1,0}; ++ if (g_advancedSettings.m_videoVDPAUtelecine) ++ enabled[2] = 1; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ } ++ else ++ { ++ VdpBool enabled[]={0,0,0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ } ++ } ++ CheckStatus(vdp_st, __LINE__); ++ ++ SetDeintSkipChroma(); ++ ++ m_config.useInteropYuv = g_guiSettings.GetBool("videoplayer.usevdpauinteropyuv"); ++} ++ ++void CMixer::SetDeintSkipChroma() ++{ ++ VdpVideoMixerAttribute attribute[] = { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE}; ++ VdpStatus vdp_st; ++ ++ uint8_t val; ++ if (g_advancedSettings.m_videoVDPAUdeintSkipChromaHD && m_config.outHeight >= 720) ++ val = 1; ++ else ++ val = 0; ++ ++ void const *values[]={&val}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_attribute_values(m_videoMixer, ARSIZE(attribute), attribute, values); ++ ++ CheckStatus(vdp_st, __LINE__); ++} ++ ++void CMixer::SetHWUpscaling() ++{ ++#ifdef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 ++ ++ VdpStatus vdp_st; ++ VdpBool enabled[]={1}; ++ switch (m_config.upscale) ++ { ++ case 9: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 8: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 7: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 6: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 5: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 4: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 3: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 2: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ case 1: ++ if (m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 }; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ break; ++ } ++ default: ++ DisableHQScaling(); ++ return; ++ } ++ CheckStatus(vdp_st, __LINE__); ++#endif ++} ++ ++void CMixer::DisableHQScaling() ++{ ++ VdpStatus vdp_st; ++ ++ if (m_videoMixer == VDP_INVALID_HANDLE) ++ return; ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L2 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L3 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L4 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L5 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L6 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L7 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L8 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ ++ if(m_config.vdpau->Supports(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9)) ++ { ++ VdpVideoMixerFeature feature[] = { VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L9 }; ++ VdpBool enabled[]={0}; ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_set_feature_enables(m_videoMixer, ARSIZE(feature), feature, enabled); ++ CheckStatus(vdp_st, __LINE__); ++ } ++} ++ ++ ++void CMixer::Init() ++{ ++ m_Brightness = 0.0; ++ m_Contrast = 0.0; ++ m_NoiseReduction = 0.0; ++ m_Sharpness = 0.0; ++ m_DeintMode = 0; ++ m_Deint = 0; ++ m_PostProc = false; ++ m_vdpError = false; ++ ++ m_config.upscale = g_advancedSettings.m_videoVDPAUScaling; ++ m_config.useInteropYuv = g_guiSettings.GetBool("videoplayer.usevdpauinteropyuv"); ++ ++ CreateVdpauMixer(); ++} ++ ++void CMixer::Uninit() ++{ ++ Flush(); ++ while (!m_outputSurfaces.empty()) ++ { ++ m_outputSurfaces.pop(); ++ } ++ m_config.vdpProcs.vdp_video_mixer_destroy(m_videoMixer); ++} ++ ++void CMixer::Flush() ++{ ++ while (!m_mixerInput.empty()) ++ { ++ CVdpauDecodedPicture pic = m_mixerInput.back(); ++ m_mixerInput.pop_back(); ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ while (!m_decodedPics.empty()) ++ { ++ CVdpauDecodedPicture pic = m_decodedPics.front(); ++ m_decodedPics.pop(); ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ Message *msg; ++ while (m_dataPort.ReceiveOutMessage(&msg)) ++ { ++ if (msg->signal == CMixerDataProtocol::FRAME) ++ { ++ CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data; ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ else if (msg->signal == CMixerDataProtocol::BUFFER) ++ { ++ VdpOutputSurface *surf; ++ surf = (VdpOutputSurface*)msg->data; ++ m_outputSurfaces.push(*surf); ++ } ++ msg->Release(); ++ } ++} ++ ++void CMixer::InitCycle() ++{ ++ CheckFeatures(); ++ uint64_t latency; ++ int flags; ++ m_config.stats->GetParams(latency, flags); ++ latency = (latency*1000)/CurrentHostFrequency(); ++ if (flags & DVP_FLAG_NO_POSTPROC) ++ SetPostProcFeatures(false); ++ else ++ SetPostProcFeatures(true); ++ ++ m_config.stats->SetCanSkipDeint(false); ++ ++ EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; ++ EINTERLACEMETHOD method = GetDeinterlacingMethod(); ++ bool interlaced = m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_INTERLACED; ++ ++ if (mode == VS_DEINTERLACEMODE_FORCE || ++ (mode == VS_DEINTERLACEMODE_AUTO && interlaced)) ++ { ++ if((method == VS_INTERLACEMETHOD_AUTO && interlaced) ++ || method == VS_INTERLACEMETHOD_VDPAU_BOB ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF ++ || method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE ) ++ { ++ if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF ++ || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF ++ || !g_graphicsContext.IsFullScreenVideo()) ++ m_mixersteps = 1; ++ else ++ { ++ m_mixersteps = 2; ++ m_config.stats->SetCanSkipDeint(true); ++ } ++ ++ if (m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_DROPDEINT) ++ { ++ m_mixersteps = 1; ++ } ++ ++ if(m_mixerInput[1].DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST) ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; ++ else ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD; ++ ++ m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU; ++ m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST | ++ DVP_FLAG_REPEAT_TOP_FIELD | ++ DVP_FLAG_INTERLACED); ++ m_config.useInteropYuv = false; ++ } ++ else if (method == VS_INTERLACEMETHOD_RENDER_BOB && m_config.useInteropYuv) ++ { ++ m_mixersteps = 1; ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; ++ m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420; ++ } ++ else ++ { ++ CLog::Log(LOGERROR, "CMixer::%s - interlace method not supported", __FUNCTION__); ++ m_mixersteps = 1; ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; ++ m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU; ++ m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST | ++ DVP_FLAG_REPEAT_TOP_FIELD | ++ DVP_FLAG_INTERLACED); ++ } ++ } ++ else ++ { ++ m_mixersteps = 1; ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; ++ ++ if (m_config.useInteropYuv) ++ m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU_420; ++ else ++ { ++ m_mixerInput[1].DVDPic.format = RENDER_FMT_VDPAU; ++ m_mixerInput[1].DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST | ++ DVP_FLAG_REPEAT_TOP_FIELD | ++ DVP_FLAG_INTERLACED); ++ } ++ } ++ m_mixerstep = 0; ++ ++ if (m_mixerInput[1].DVDPic.format == RENDER_FMT_VDPAU) ++ { ++ m_processPicture.outputSurface = m_outputSurfaces.front(); ++ m_mixerInput[1].DVDPic.iWidth = m_config.outWidth; ++ m_mixerInput[1].DVDPic.iHeight = m_config.outHeight; ++ } ++ else ++ { ++ m_mixerInput[1].DVDPic.iWidth = m_config.vidWidth; ++ m_mixerInput[1].DVDPic.iHeight = m_config.vidHeight; ++ } ++ ++ m_processPicture.DVDPic = m_mixerInput[1].DVDPic; ++ m_processPicture.render = m_mixerInput[1].render; ++} ++ ++void CMixer::FiniCycle() ++{ ++ while (m_mixerInput.size() > 3) ++ { ++ CVdpauDecodedPicture &tmp = m_mixerInput.back(); ++ if (tmp.render && m_processPicture.DVDPic.format != RENDER_FMT_VDPAU_420) ++ { ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ tmp.render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ m_mixerInput.pop_back(); ++// m_config.stats->DecDecoded(); ++ } ++} ++ ++void CMixer::ProcessPicture() ++{ ++ if (m_processPicture.DVDPic.format == RENDER_FMT_VDPAU_420) ++ return; ++ ++ VdpStatus vdp_st; ++ ++ if (m_mixerstep == 1) ++ { ++ if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD) ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD; ++ else ++ m_mixerfield = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD; ++ } ++ ++ VdpVideoSurface past_surfaces[4] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE, VDP_INVALID_HANDLE }; ++ VdpVideoSurface futu_surfaces[2] = { VDP_INVALID_HANDLE, VDP_INVALID_HANDLE }; ++ uint32_t pastCount = 4; ++ uint32_t futuCount = 2; ++ ++ if(m_mixerfield == VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME) ++ { ++ // use only 2 past 1 future for progressive/weave ++ // (only used for postproc anyway eg noise reduction) ++ if (m_mixerInput.size() > 3) ++ past_surfaces[1] = m_mixerInput[3].render->surface; ++ if (m_mixerInput.size() > 2) ++ past_surfaces[0] = m_mixerInput[2].render->surface; ++ futu_surfaces[0] = m_mixerInput[0].render->surface; ++ pastCount = 2; ++ futuCount = 1; ++ } ++ else ++ { ++ if(m_mixerstep == 0) ++ { // first field ++ if (m_mixerInput.size() > 3) ++ { ++ past_surfaces[3] = m_mixerInput[3].render->surface; ++ past_surfaces[2] = m_mixerInput[3].render->surface; ++ } ++ if (m_mixerInput.size() > 2) ++ { ++ past_surfaces[1] = m_mixerInput[2].render->surface; ++ past_surfaces[0] = m_mixerInput[2].render->surface; ++ } ++ futu_surfaces[0] = m_mixerInput[1].render->surface; ++ futu_surfaces[1] = m_mixerInput[0].render->surface;; ++ } ++ else ++ { // second field ++ if (m_mixerInput.size() > 3) ++ { ++ past_surfaces[3] = m_mixerInput[3].render->surface; ++ } ++ if (m_mixerInput.size() > 2) ++ { ++ past_surfaces[2] = m_mixerInput[2].render->surface; ++ past_surfaces[1] = m_mixerInput[2].render->surface; ++ } ++ past_surfaces[0] = m_mixerInput[1].render->surface; ++ futu_surfaces[0] = m_mixerInput[1].render->surface; ++ futu_surfaces[1] = m_mixerInput[1].render->surface; ++ ++ m_processPicture.DVDPic.pts = DVD_NOPTS_VALUE; ++ m_processPicture.DVDPic.dts = DVD_NOPTS_VALUE; ++ } ++ m_processPicture.DVDPic.iRepeatPicture = 0.0; ++ } // interlaced ++ ++ VdpRect sourceRect; ++ sourceRect.x0 = 0; ++ sourceRect.y0 = 0; ++ sourceRect.x1 = m_config.vidWidth; ++ sourceRect.y1 = m_config.vidHeight; ++ ++ VdpRect destRect; ++ destRect.x0 = 0; ++ destRect.y0 = 0; ++ destRect.x1 = m_config.outWidth; ++ destRect.y1 = m_config.outHeight; ++ ++ // start vdpau video mixer ++ vdp_st = m_config.vdpProcs.vdp_video_mixer_render(m_videoMixer, ++ VDP_INVALID_HANDLE, ++ 0, ++ m_mixerfield, ++ pastCount, ++ past_surfaces, ++ m_mixerInput[1].render->surface, ++ futuCount, ++ futu_surfaces, ++ &sourceRect, ++ m_processPicture.outputSurface, ++ &destRect, ++ &destRect, ++ 0, ++ NULL); ++ CheckStatus(vdp_st, __LINE__); ++ ++ if (m_mixerfield != VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME) ++ { ++ // in order to clip top and bottom lines when de-interlacing ++ // we black those lines as a work around for not working ++ // background colour using the mixer ++ // pixel perfect is preferred over overscanning or zooming ++ ++ VdpRect clipRect = destRect; ++ clipRect.y1 = clipRect.y0 + 2; ++ uint32_t *data[] = {m_BlackBar}; ++ uint32_t pitches[] = {destRect.x1}; ++ vdp_st = m_config.vdpProcs.vdp_output_surface_put_bits_native(m_processPicture.outputSurface, ++ (void**)data, ++ pitches, ++ &clipRect); ++ CheckStatus(vdp_st, __LINE__); ++ ++ clipRect = destRect; ++ clipRect.y0 = clipRect.y1 - 2; ++ vdp_st = m_config.vdpProcs.vdp_output_surface_put_bits_native(m_processPicture.outputSurface, ++ (void**)data, ++ pitches, ++ &clipRect); ++ CheckStatus(vdp_st, __LINE__); ++ } ++} ++ ++ ++bool CMixer::CheckStatus(VdpStatus vdp_st, int line) ++{ ++ if (vdp_st != VDP_STATUS_OK) ++ { ++ CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_config.vdpProcs.vdp_get_error_string(vdp_st), vdp_st, __FILE__, line); ++ m_vdpError = true; ++ return true; ++ } ++ return false; ++} ++ ++//----------------------------------------------------------------------------- ++// Output ++//----------------------------------------------------------------------------- ++COutput::COutput(CEvent *inMsgEvent) : ++ CThread("Vdpau Output Thread"), ++ m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent), ++ m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent), ++ m_mixer(&m_outMsgEvent) ++{ ++ m_inMsgEvent = inMsgEvent; ++ ++ CVdpauRenderPicture pic; ++ pic.renderPicSection = &m_bufferPool.renderPicSec; ++ pic.refCount = 0; ++ for (unsigned int i = 0; i < NUM_RENDER_PICS; i++) ++ { ++ m_bufferPool.allRenderPics.push_back(pic); ++ } ++ for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i) ++ { ++ m_bufferPool.freeRenderPics.push_back(&m_bufferPool.allRenderPics[i]); ++ } ++} ++ ++void COutput::Start() ++{ ++ Create(); ++} ++ ++COutput::~COutput() ++{ ++ Dispose(); ++ ++ m_bufferPool.freeRenderPics.clear(); ++ m_bufferPool.usedRenderPics.clear(); ++ m_bufferPool.allRenderPics.clear(); ++} ++ ++void COutput::Dispose() ++{ ++ CSingleLock lock(g_graphicsContext); ++ m_bStop = true; ++ m_outMsgEvent.Set(); ++ StopThread(); ++ m_controlPort.Purge(); ++ m_dataPort.Purge(); ++} ++ ++void COutput::OnStartup() ++{ ++ CLog::Log(LOGNOTICE, "COutput::OnStartup: Output Thread created"); ++} ++ ++void COutput::OnExit() ++{ ++ CLog::Log(LOGNOTICE, "COutput::OnExit: Output Thread terminated"); ++} ++ ++enum OUTPUT_STATES ++{ ++ O_TOP = 0, // 0 ++ O_TOP_ERROR, // 1 ++ O_TOP_UNCONFIGURED, // 2 ++ O_TOP_CONFIGURED, // 3 ++ O_TOP_CONFIGURED_IDLE, // 4 ++ O_TOP_CONFIGURED_WORK, // 5 ++}; ++ ++int VDPAU_OUTPUT_parentStates[] = { ++ -1, ++ 0, //TOP_ERROR ++ 0, //TOP_UNCONFIGURED ++ 0, //TOP_CONFIGURED ++ 3, //TOP_CONFIGURED_IDLE ++ 3, //TOP_CONFIGURED_WORK ++}; ++ ++void COutput::StateMachine(int signal, Protocol *port, Message *msg) ++{ ++ for (int state = m_state; ; state = VDPAU_OUTPUT_parentStates[state]) ++ { ++ switch (state) ++ { ++ case O_TOP: // TOP ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::FLUSH: ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ case COutputControlProtocol::PRECLEANUP: ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ default: ++ break; ++ } ++ } ++ else if (port == &m_dataPort) ++ { ++ switch (signal) ++ { ++ case COutputDataProtocol::RETURNPIC: ++ CVdpauRenderPicture *pic; ++ pic = *((CVdpauRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ return; ++ default: ++ break; ++ } ++ } ++ { ++ std::string portName = port == NULL ? "timer" : port->portName; ++ CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state); ++ } ++ return; ++ ++ case O_TOP_ERROR: ++ break; ++ ++ case O_TOP_UNCONFIGURED: ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::INIT: ++ CVdpauConfig *data; ++ data = (CVdpauConfig*)msg->data; ++ if (data) ++ { ++ m_config = *data; ++ } ++ Init(); ++ Message *reply; ++ if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::INIT, ++ &reply, 1000, &m_config, sizeof(m_config))) ++ { ++ if (reply->signal != CMixerControlProtocol::ACC) ++ m_vdpError = true; ++ reply->Release(); ++ } ++ ++ // set initial number of ++ m_bufferPool.numOutputSurfaces = 4; ++ EnsureBufferPool(); ++ if (!m_vdpError) ++ { ++ m_state = O_TOP_CONFIGURED_IDLE; ++ msg->Reply(COutputControlProtocol::ACC); ++ } ++ else ++ { ++ m_state = O_TOP_ERROR; ++ msg->Reply(COutputControlProtocol::ERROR); ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED: ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::FLUSH: ++ Flush(); ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ case COutputControlProtocol::PRECLEANUP: ++ Flush(); ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ default: ++ break; ++ } ++ } ++ else if (port == &m_dataPort) ++ { ++ switch (signal) ++ { ++ case COutputDataProtocol::NEWFRAME: ++ CVdpauDecodedPicture *frame; ++ frame = (CVdpauDecodedPicture*)msg->data; ++ if (frame) ++ { ++ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::FRAME, ++ frame,sizeof(CVdpauDecodedPicture)); ++ } ++ return; ++ case COutputDataProtocol::RETURNPIC: ++ CVdpauRenderPicture *pic; ++ pic = *((CVdpauRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ m_controlPort.SendInMessage(COutputControlProtocol::STATS); ++ m_state = O_TOP_CONFIGURED_WORK; ++ m_extTimeout = 0; ++ return; ++ default: ++ break; ++ } ++ } ++ else if (port == &m_mixer.m_dataPort) ++ { ++ switch (signal) ++ { ++ case CMixerDataProtocol::PICTURE: ++ CVdpauProcessedPicture *pic; ++ pic = (CVdpauProcessedPicture*)msg->data; ++ m_bufferPool.processedPics.push(*pic); ++ m_state = O_TOP_CONFIGURED_WORK; ++ m_extTimeout = 0; ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_IDLE: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++// uint16_t decoded, processed, render; ++// m_config.stats->Get(decoded, processed, render); ++// CLog::Log(LOGDEBUG, "CVDPAU::COutput - timeout idle: decoded: %d, proc: %d, render: %d", decoded, processed, render); ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_WORK: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++ if (HasWork()) ++ { ++ CVdpauRenderPicture *pic; ++ pic = ProcessMixerPicture(); ++ if (pic) ++ { ++ m_config.stats->DecProcessed(); ++ m_config.stats->IncRender(); ++ m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic)); ++ } ++ m_extTimeout = 1; ++ } ++ else ++ { ++ m_state = O_TOP_CONFIGURED_IDLE; ++ m_extTimeout = 100; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ default: // we are in no state, should not happen ++ CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state); ++ return; ++ } ++ } // for ++} ++ ++void COutput::Process() ++{ ++ Message *msg; ++ Protocol *port; ++ bool gotMsg; ++ ++ m_state = O_TOP_UNCONFIGURED; ++ m_extTimeout = 1000; ++ m_bStateMachineSelfTrigger = false; ++ ++ while (!m_bStop) ++ { ++ gotMsg = false; ++ ++ if (m_bStateMachineSelfTrigger) ++ { ++ m_bStateMachineSelfTrigger = false; ++ // self trigger state machine ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ continue; ++ } ++ // check control port ++ else if (m_controlPort.ReceiveOutMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_controlPort; ++ } ++ // check data port ++ else if (m_dataPort.ReceiveOutMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_dataPort; ++ } ++ // check mixer data port ++ else if (m_mixer.m_dataPort.ReceiveInMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_mixer.m_dataPort; ++ } ++ if (gotMsg) ++ { ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ continue; ++ } ++ ++ // wait for message ++ else if (m_outMsgEvent.WaitMSec(m_extTimeout)) ++ { ++ continue; ++ } ++ // time out ++ else ++ { ++ msg = m_controlPort.GetMessage(); ++ msg->signal = COutputControlProtocol::TIMEOUT; ++ port = 0; ++ // signal timeout to state machine ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ } ++ } ++ Flush(); ++ Uninit(); ++} ++ ++bool COutput::Init() ++{ ++ if (!CreateGlxContext()) ++ return false; ++ ++ if (!GLInit()) ++ return false; ++ ++ m_mixer.Start(); ++ m_vdpError = false; ++ ++ return true; ++} ++ ++bool COutput::Uninit() ++{ ++ m_mixer.Dispose(); ++ GLUnmapSurfaces(); ++ GLUnbindPixmaps(); ++ ReleaseBufferPool(); ++ DestroyGlxContext(); ++ return true; ++} ++ ++void COutput::Flush() ++{ ++ Message *reply; ++ if (m_mixer.m_controlPort.SendOutMessageSync(CMixerControlProtocol::FLUSH, ++ &reply, ++ 2000)) ++ { ++ reply->Release(); ++ } ++ else ++ CLog::Log(LOGERROR, "Coutput::%s - failed to flush mixer", __FUNCTION__); ++ ++ Message *msg; ++ while (m_mixer.m_dataPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == CMixerDataProtocol::PICTURE) ++ { ++ CVdpauProcessedPicture pic = *(CVdpauProcessedPicture*)msg->data; ++ if (pic.DVDPic.format == RENDER_FMT_VDPAU_420) ++ { ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ } ++ msg->Release(); ++ } ++ ++ while (m_dataPort.ReceiveOutMessage(&msg)) ++ { ++ if (msg->signal == COutputDataProtocol::NEWFRAME) ++ { ++ CVdpauDecodedPicture pic = *(CVdpauDecodedPicture*)msg->data; ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ else if (msg->signal == COutputDataProtocol::RETURNPIC) ++ { ++ CVdpauRenderPicture *pic; ++ pic = *((CVdpauRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ } ++ msg->Release(); ++ } ++ ++ while (m_dataPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputDataProtocol::PICTURE) ++ { ++ CVdpauRenderPicture *pic; ++ pic = *((CVdpauRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ } ++ } ++ ++ // reset used render flag which was cleared on mixer flush ++ std::deque::iterator it; ++ for (it = m_bufferPool.usedRenderPics.begin(); it != m_bufferPool.usedRenderPics.end(); ++it) ++ { ++ if ((*it)->DVDPic.format == RENDER_FMT_VDPAU_420) ++ { ++ std::map::iterator it2; ++ it2 = m_bufferPool.glVideoSurfaceMap.find((*it)->sourceIdx); ++ if (it2 == m_bufferPool.glVideoSurfaceMap.end()) ++ { ++ CLog::Log(LOGDEBUG, "COutput::Flush - gl surface not found"); ++ continue; ++ } ++ vdpau_render_state *render = it2->second.sourceVuv; ++ if (render) ++ render->state |= FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ } ++} ++ ++bool COutput::HasWork() ++{ ++ if (m_config.usePixmaps) ++ { ++ if (!m_bufferPool.processedPics.empty() && FindFreePixmap() >= 0) ++ return true; ++ if (!m_bufferPool.notVisiblePixmaps.empty() && !m_bufferPool.freeRenderPics.empty()) ++ return true; ++ return false; ++ } ++ else ++ { ++ if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty()) ++ return true; ++ return false; ++ } ++} ++ ++CVdpauRenderPicture* COutput::ProcessMixerPicture() ++{ ++ CVdpauRenderPicture *retPic = 0; ++ ++ if (m_config.usePixmaps) ++ { ++ if (!m_bufferPool.processedPics.empty() && FindFreePixmap() >= 0) ++ { ++ unsigned int i = FindFreePixmap(); ++ VdpauBufferPool::Pixmaps *pixmap = &m_bufferPool.pixmaps[i]; ++ pixmap->used = true; ++ CVdpauProcessedPicture pic = m_bufferPool.processedPics.front(); ++ m_bufferPool.processedPics.pop(); ++ pixmap->surface = pic.outputSurface; ++ pixmap->DVDPic = pic.DVDPic; ++ pixmap->id = i; ++ m_bufferPool.notVisiblePixmaps.push_back(pixmap); ++ VdpStatus vdp_st; ++ m_config.vdpProcs.vdp_presentation_queue_display(pixmap->vdp_flip_queue, ++ pixmap->surface,0,0,0); ++ } ++ if (!m_bufferPool.notVisiblePixmaps.empty() && !m_bufferPool.freeRenderPics.empty()) ++ { ++ VdpStatus vdp_st; ++ VdpTime time; ++ VdpPresentationQueueStatus status; ++ VdpauBufferPool::Pixmaps *pixmap = m_bufferPool.notVisiblePixmaps.front(); ++ vdp_st = m_config.vdpProcs.vdp_presentation_queue_query_surface_status( ++ pixmap->vdp_flip_queue, pixmap->surface, &status, &time); ++ ++ if (vdp_st == VDP_STATUS_OK && status == VDP_PRESENTATION_QUEUE_STATUS_VISIBLE) ++ { ++ retPic = m_bufferPool.freeRenderPics.front(); ++ m_bufferPool.freeRenderPics.pop_front(); ++ m_bufferPool.usedRenderPics.push_back(retPic); ++ retPic->sourceIdx = pixmap->id; ++ retPic->DVDPic = pixmap->DVDPic; ++ retPic->valid = true; ++ retPic->texture[0] = pixmap->texture; ++ retPic->crop = CRect(0,0,0,0); ++ m_bufferPool.notVisiblePixmaps.pop_front(); ++ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &pixmap->surface, sizeof(pixmap->surface)); ++ } ++ } ++ } // pixmap ++ else if (!m_bufferPool.processedPics.empty() && !m_bufferPool.freeRenderPics.empty()) ++ { ++ retPic = m_bufferPool.freeRenderPics.front(); ++ m_bufferPool.freeRenderPics.pop_front(); ++ m_bufferPool.usedRenderPics.push_back(retPic); ++ CVdpauProcessedPicture procPic = m_bufferPool.processedPics.front(); ++ m_bufferPool.processedPics.pop(); ++ ++ retPic->DVDPic = procPic.DVDPic; ++ retPic->valid = true; ++ if (retPic->DVDPic.format == RENDER_FMT_VDPAU) ++ { ++ m_config.useInteropYuv = false; ++ m_bufferPool.numOutputSurfaces = NUM_RENDER_PICS; ++ EnsureBufferPool(); ++ GLMapSurfaces(); ++ retPic->sourceIdx = procPic.outputSurface; ++ retPic->texture[0] = m_bufferPool.glOutputSurfaceMap[procPic.outputSurface].texture[0]; ++ retPic->crop = CRect(0,0,0,0); ++ } ++ else ++ { ++ m_config.useInteropYuv = true; ++ GLMapSurfaces(); ++ retPic->sourceIdx = procPic.render->surface; ++ for (unsigned int i=0; i<4; ++i) ++ retPic->texture[i] = m_bufferPool.glVideoSurfaceMap[procPic.render->surface].texture[i]; ++ retPic->texWidth = m_config.surfaceWidth; ++ retPic->texHeight = m_config.surfaceHeight; ++ retPic->crop.x1 = 0; ++ retPic->crop.y1 = 0; ++ retPic->crop.x2 = m_config.surfaceWidth - m_config.vidWidth; ++ retPic->crop.y2 = m_config.surfaceHeight - m_config.vidHeight; ++ } ++ } ++ return retPic; ++} ++ ++void COutput::ProcessReturnPicture(CVdpauRenderPicture *pic) ++{ ++ std::deque::iterator it; ++ it = std::find(m_bufferPool.usedRenderPics.begin(), m_bufferPool.usedRenderPics.end(), pic); ++ if (it == m_bufferPool.usedRenderPics.end()) ++ { ++ CLog::Log(LOGWARNING, "COutput::ProcessReturnPicture - pic not found"); ++ return; ++ } ++ m_bufferPool.usedRenderPics.erase(it); ++ m_bufferPool.freeRenderPics.push_back(pic); ++ if (!pic->valid) ++ { ++ CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__); ++ return; ++ } ++ ++ if (m_config.usePixmaps) ++ { ++ m_bufferPool.pixmaps[pic->sourceIdx].used = false; ++ return; ++ } ++ else if (pic->DVDPic.format == RENDER_FMT_VDPAU_420) ++ { ++ std::map::iterator it; ++ it = m_bufferPool.glVideoSurfaceMap.find(pic->sourceIdx); ++ if (it == m_bufferPool.glVideoSurfaceMap.end()) ++ { ++ CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found"); ++ return; ++ } ++ vdpau_render_state *render = it->second.sourceVuv; ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ render->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; ++ } ++ else if (pic->DVDPic.format == RENDER_FMT_VDPAU) ++ { ++ std::map::iterator it; ++ it = m_bufferPool.glOutputSurfaceMap.find(pic->sourceIdx); ++ if (it == m_bufferPool.glOutputSurfaceMap.end()) ++ { ++ CLog::Log(LOGDEBUG, "COutput::ProcessReturnPicture - gl surface not found"); ++ return; ++ } ++ VdpOutputSurface outSurf = it->second.sourceRgb; ++ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, &outSurf, sizeof(outSurf)); ++ } ++} ++ ++int COutput::FindFreePixmap() ++{ ++ // find free pixmap ++ unsigned int i; ++ for (i = 0; i < m_bufferPool.pixmaps.size(); ++i) ++ { ++ if (!m_bufferPool.pixmaps[i].used) ++ break; ++ } ++ if (i == m_bufferPool.pixmaps.size()) ++ return -1; ++ else ++ return i; ++} ++ ++bool COutput::EnsureBufferPool() ++{ ++ VdpStatus vdp_st; ++ ++ // Creation of outputSurfaces ++ VdpOutputSurface outputSurface; ++ for (int i = m_bufferPool.outputSurfaces.size(); i < m_bufferPool.numOutputSurfaces; i++) ++ { ++ vdp_st = m_config.vdpProcs.vdp_output_surface_create(m_config.vdpDevice, ++ VDP_RGBA_FORMAT_B8G8R8A8, ++ m_config.outWidth, ++ m_config.outHeight, ++ &outputSurface); ++ if (CheckStatus(vdp_st, __LINE__)) ++ return false; ++ m_bufferPool.outputSurfaces.push_back(outputSurface); ++ ++ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, ++ &outputSurface, ++ sizeof(VdpOutputSurface)); ++ CLog::Log(LOGNOTICE, "VDPAU::COutput::InitBufferPool - Output Surface created"); ++ } ++ ++ ++ if (m_config.usePixmaps && m_bufferPool.pixmaps.empty()) ++ { ++ // create pixmpas ++ VdpauBufferPool::Pixmaps pixmap; ++ int numPixmaps = NUM_RENDER_PICS; ++ for (unsigned int i = 0; i < numPixmaps; i++) ++ { ++ pixmap.pixmap = None; ++ pixmap.glPixmap = None; ++ pixmap.vdp_flip_queue = VDP_INVALID_HANDLE; ++ pixmap.vdp_flip_target = VDP_INVALID_HANDLE; ++ MakePixmap(pixmap); ++ glXMakeCurrent(m_Display, None, NULL); ++ vdp_st = m_config.vdpProcs.vdp_presentation_queue_target_create_x11(m_config.vdpDevice, ++ pixmap.pixmap, //x_window, ++ &pixmap.vdp_flip_target); ++ ++ CheckStatus(vdp_st, __LINE__); ++ ++ vdp_st = m_config.vdpProcs.vdp_presentation_queue_create(m_config.vdpDevice, ++ pixmap.vdp_flip_target, ++ &pixmap.vdp_flip_queue); ++ CheckStatus(vdp_st, __LINE__); ++ glXMakeCurrent(m_Display, m_glPixmap, m_glContext); ++ ++ pixmap.id = i; ++ pixmap.used = false; ++ m_bufferPool.pixmaps.push_back(pixmap); ++ } ++ GLBindPixmaps(); ++ } ++ ++ return true; ++} ++ ++void COutput::ReleaseBufferPool() ++{ ++ VdpStatus vdp_st; ++ ++ CSingleLock lock(m_bufferPool.renderPicSec); ++ ++ if (m_config.usePixmaps) ++ { ++ for (unsigned int i = 0; i < m_bufferPool.pixmaps.size(); ++i) ++ { ++ if (m_bufferPool.pixmaps[i].vdp_flip_queue != VDP_INVALID_HANDLE) ++ { ++ vdp_st = m_config.vdpProcs.vdp_presentation_queue_destroy(m_bufferPool.pixmaps[i].vdp_flip_queue); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ if (m_bufferPool.pixmaps[i].vdp_flip_target != VDP_INVALID_HANDLE) ++ { ++ vdp_st = m_config.vdpProcs.vdp_presentation_queue_target_destroy(m_bufferPool.pixmaps[i].vdp_flip_target); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ if (m_bufferPool.pixmaps[i].glPixmap) ++ { ++ glXDestroyPixmap(m_Display, m_bufferPool.pixmaps[i].glPixmap); ++ } ++ if (m_bufferPool.pixmaps[i].pixmap) ++ { ++ XFreePixmap(m_Display, m_bufferPool.pixmaps[i].pixmap); ++ } ++ } ++ m_bufferPool.pixmaps.clear(); ++ } ++ ++ // release all output surfaces ++ for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i) ++ { ++ if (m_bufferPool.outputSurfaces[i] == VDP_INVALID_HANDLE) ++ continue; ++ vdp_st = m_config.vdpProcs.vdp_output_surface_destroy(m_bufferPool.outputSurfaces[i]); ++ CheckStatus(vdp_st, __LINE__); ++ } ++ m_bufferPool.outputSurfaces.clear(); ++ ++ // invalidate all used render pictures ++ for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i) ++ { ++ m_bufferPool.usedRenderPics[i]->valid = false; ++ } ++} ++ ++void COutput::InitMixer() ++{ ++ for (unsigned int i = 0; i < m_bufferPool.outputSurfaces.size(); ++i) ++ { ++ m_mixer.m_dataPort.SendOutMessage(CMixerDataProtocol::BUFFER, ++ &m_bufferPool.outputSurfaces[i], ++ sizeof(VdpOutputSurface)); ++ } ++} ++ ++bool COutput::MakePixmap(VdpauBufferPool::Pixmaps &pixmap) ++{ ++ CLog::Log(LOGNOTICE,"Creating %ix%i pixmap", m_config.outWidth, m_config.outHeight); ++ ++ // Get our window attribs. ++ XWindowAttributes wndattribs; ++ XGetWindowAttributes(m_Display, g_Windowing.GetWindow(), &wndattribs); ++ ++ pixmap.pixmap = XCreatePixmap(m_Display, ++ g_Windowing.GetWindow(), ++ m_config.outWidth, ++ m_config.outHeight, ++ wndattribs.depth); ++ if (!pixmap.pixmap) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COUtput::MakePixmap - GLX Error: MakePixmap: Unable to create XPixmap"); ++ return false; ++ } ++ ++// XGCValues values = {}; ++// GC xgc; ++// values.foreground = BlackPixel (m_Display, DefaultScreen (m_Display)); ++// xgc = XCreateGC(m_Display, pixmap.pixmap, GCForeground, &values); ++// XFillRectangle(m_Display, pixmap.pixmap, xgc, 0, 0, m_config.outWidth, m_config.outHeight); ++// XFreeGC(m_Display, xgc); ++ ++ if(!MakePixmapGL(pixmap)) ++ return false; ++ ++ return true; ++} ++ ++bool COutput::MakePixmapGL(VdpauBufferPool::Pixmaps &pixmap) ++{ ++ int num=0; ++ int fbConfigIndex = 0; ++ ++ int doubleVisAttributes[] = { ++ GLX_RENDER_TYPE, GLX_RGBA_BIT, ++ GLX_RED_SIZE, 8, ++ GLX_GREEN_SIZE, 8, ++ GLX_BLUE_SIZE, 8, ++ GLX_ALPHA_SIZE, 8, ++ GLX_DEPTH_SIZE, 8, ++ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, ++ GLX_BIND_TO_TEXTURE_RGBA_EXT, True, ++ GLX_DOUBLEBUFFER, False, ++ GLX_Y_INVERTED_EXT, True, ++ GLX_X_RENDERABLE, True, ++ None ++ }; ++ ++ int pixmapAttribs[] = { ++ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, ++ GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, ++ None ++ }; ++ ++ GLXFBConfig *fbConfigs; ++ fbConfigs = glXChooseFBConfig(m_Display, g_Windowing.GetCurrentScreen(), doubleVisAttributes, &num); ++ if (fbConfigs==NULL) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput::MakPixmapGL - No compatible framebuffers found"); ++ return false; ++ } ++ fbConfigIndex = 0; ++ ++ pixmap.glPixmap = glXCreatePixmap(m_Display, fbConfigs[fbConfigIndex], pixmap.pixmap, pixmapAttribs); ++ ++ if (!pixmap.glPixmap) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput::MakPixmapGL - Could not create Pixmap"); ++ XFree(fbConfigs); ++ return false; ++ } ++ XFree(fbConfigs); ++ return true; ++} ++ ++bool COutput::GLInit() ++{ ++ glXBindTexImageEXT = NULL; ++ glXReleaseTexImageEXT = NULL; ++#ifdef GL_NV_vdpau_interop ++ glVDPAUInitNV = NULL; ++ glVDPAUFiniNV = NULL; ++ glVDPAURegisterOutputSurfaceNV = NULL; ++ glVDPAURegisterVideoSurfaceNV = NULL; ++ glVDPAUIsSurfaceNV = NULL; ++ glVDPAUUnregisterSurfaceNV = NULL; ++ glVDPAUSurfaceAccessNV = NULL; ++ glVDPAUMapSurfacesNV = NULL; ++ glVDPAUUnmapSurfacesNV = NULL; ++ glVDPAUGetSurfaceivNV = NULL; ++#endif ++ ++ m_config.usePixmaps = !g_guiSettings.GetBool("videoplayer.usevdpauinterop"); ++ ++#ifdef GL_NV_vdpau_interop ++ if (glewIsSupported("GL_NV_vdpau_interop")) ++ { ++ if (!glVDPAUInitNV) ++ glVDPAUInitNV = (PFNGLVDPAUINITNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUInitNV"); ++ if (!glVDPAUFiniNV) ++ glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)glXGetProcAddress((GLubyte *) "glVDPAUFiniNV"); ++ if (!glVDPAURegisterOutputSurfaceNV) ++ glVDPAURegisterOutputSurfaceNV = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterOutputSurfaceNV"); ++ if (!glVDPAURegisterVideoSurfaceNV) ++ glVDPAURegisterVideoSurfaceNV = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAURegisterVideoSurfaceNV"); ++ if (!glVDPAUIsSurfaceNV) ++ glVDPAUIsSurfaceNV = (PFNGLVDPAUISSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUIsSurfaceNV"); ++ if (!glVDPAUUnregisterSurfaceNV) ++ glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnregisterSurfaceNV"); ++ if (!glVDPAUSurfaceAccessNV) ++ glVDPAUSurfaceAccessNV = (PFNGLVDPAUSURFACEACCESSNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUSurfaceAccessNV"); ++ if (!glVDPAUMapSurfacesNV) ++ glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUMapSurfacesNV"); ++ if (!glVDPAUUnmapSurfacesNV) ++ glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUUnmapSurfacesNV"); ++ if (!glVDPAUGetSurfaceivNV) ++ glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)glXGetProcAddress((GLubyte *) "glVDPAUGetSurfaceivNV"); ++ ++ CLog::Log(LOGNOTICE, "VDPAU::COutput GL interop supported"); ++ } ++ else ++#endif ++ { ++ m_config.usePixmaps = true; ++ g_guiSettings.SetBool("videoplayer.usevdpauinterop",false); ++ g_guiSettings.SetBool("videoplayer.usevdpauinteropyuv",false); ++ } ++ if (!glXBindTexImageEXT) ++ glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXBindTexImageEXT"); ++ if (!glXReleaseTexImageEXT) ++ glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC)glXGetProcAddress((GLubyte *) "glXReleaseTexImageEXT"); ++ ++#ifdef GL_NV_vdpau_interop ++ if (!m_config.usePixmaps) ++ { ++ while (glGetError() != GL_NO_ERROR); ++ glVDPAUInitNV(reinterpret_cast(m_config.vdpDevice), reinterpret_cast(m_config.vdpProcs.vdp_get_proc_address)); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput - GLInitInterop glVDPAUInitNV failed"); ++ m_vdpError = true; ++ return false; ++ } ++ CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop initialized"); ++ } ++#endif ++ return true; ++} ++ ++void COutput::GLMapSurfaces() ++{ ++#ifdef GL_NV_vdpau_interop ++ if (m_config.usePixmaps) ++ return; ++ ++ if (m_config.useInteropYuv) ++ { ++ VdpauBufferPool::GLVideoSurface glSurface; ++ if (m_config.videoSurfaces->size() != m_bufferPool.glVideoSurfaceMap.size()) ++ { ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ for (int i = 0; i < m_config.videoSurfaces->size(); i++) ++ { ++ if ((*m_config.videoSurfaces)[i]->surface == VDP_INVALID_HANDLE) ++ continue; ++ ++ if (m_bufferPool.glVideoSurfaceMap.find((*m_config.videoSurfaces)[i]->surface) == m_bufferPool.glVideoSurfaceMap.end()) ++ { ++ glSurface.sourceVuv = (*m_config.videoSurfaces)[i]; ++ while (glGetError() != GL_NO_ERROR) ; ++ glGenTextures(4, glSurface.texture); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error creating texture"); ++ m_vdpError = true; ++ } ++ glSurface.glVdpauSurface = glVDPAURegisterVideoSurfaceNV(reinterpret_cast((*m_config.videoSurfaces)[i]->surface), ++ GL_TEXTURE_2D, 4, glSurface.texture); ++ ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error register video surface"); ++ m_vdpError = true; ++ } ++ glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error setting access"); ++ m_vdpError = true; ++ } ++ glVDPAUMapSurfacesNV(1, &glSurface.glVdpauSurface); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface"); ++ m_vdpError = true; ++ } ++ m_bufferPool.glVideoSurfaceMap[(*m_config.videoSurfaces)[i]->surface] = glSurface; ++ if (m_vdpError) ++ return; ++ CLog::Log(LOGNOTICE, "VDPAU::COutput registered surface"); ++ } ++ } ++ } ++ } ++ else ++ { ++ if (m_bufferPool.glOutputSurfaceMap.size() != m_bufferPool.numOutputSurfaces) ++ { ++ VdpauBufferPool::GLVideoSurface glSurface; ++ for (int i=m_bufferPool.glOutputSurfaceMap.size(); i(m_bufferPool.outputSurfaces[i]), ++ GL_TEXTURE_2D, 1, glSurface.texture); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error register output surface"); ++ m_vdpError = true; ++ } ++ glVDPAUSurfaceAccessNV(glSurface.glVdpauSurface, GL_READ_ONLY); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error setting access"); ++ m_vdpError = true; ++ } ++ glVDPAUMapSurfacesNV(1, &glSurface.glVdpauSurface); ++ if (glGetError() != GL_NO_ERROR) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput error mapping surface"); ++ m_vdpError = true; ++ } ++ m_bufferPool.glOutputSurfaceMap[m_bufferPool.outputSurfaces[i]] = glSurface; ++ if (m_vdpError) ++ return; ++ } ++ CLog::Log(LOGNOTICE, "VDPAU::COutput registered output surfaces"); ++ } ++ } ++#endif ++} ++ ++void COutput::GLUnmapSurfaces() ++{ ++#ifdef GL_NV_vdpau_interop ++ if (m_config.usePixmaps) ++ return; ++ ++ { CSingleLock lock(*m_config.videoSurfaceSec); ++ std::map::iterator it; ++ for (it = m_bufferPool.glVideoSurfaceMap.begin(); it != m_bufferPool.glVideoSurfaceMap.end(); ++it) ++ { ++ glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface); ++ glDeleteTextures(4, it->second.texture); ++ } ++ m_bufferPool.glVideoSurfaceMap.clear(); ++ } ++ ++ std::map::iterator it; ++ for (it = m_bufferPool.glOutputSurfaceMap.begin(); it != m_bufferPool.glOutputSurfaceMap.end(); ++it) ++ { ++ glVDPAUUnregisterSurfaceNV(it->second.glVdpauSurface); ++ glDeleteTextures(1, it->second.texture); ++ } ++ m_bufferPool.glOutputSurfaceMap.clear(); ++ ++ glVDPAUFiniNV(); ++ ++ CLog::Log(LOGNOTICE, "VDPAU::COutput: vdpau gl interop finished"); ++ ++#endif ++} ++ ++void COutput::GLBindPixmaps() ++{ ++ if (!m_config.usePixmaps) ++ return; ++ ++ for (unsigned int i = 0; i < m_bufferPool.pixmaps.size(); i++) ++ { ++ // create texture ++ glGenTextures(1, &m_bufferPool.pixmaps[i].texture); ++ ++ //bind texture ++ glBindTexture(GL_TEXTURE_2D, m_bufferPool.pixmaps[i].texture); ++ ++ // bind pixmap ++ glXBindTexImageEXT(m_Display, m_bufferPool.pixmaps[i].glPixmap, GLX_FRONT_LEFT_EXT, NULL); ++ ++ glBindTexture(GL_TEXTURE_2D, 0); ++ } ++ ++ CLog::Log(LOGNOTICE, "VDPAU::COutput: bound pixmaps"); ++} ++ ++void COutput::GLUnbindPixmaps() ++{ ++ if (!m_config.usePixmaps) ++ return; ++ ++ for (unsigned int i = 0; i < m_bufferPool.pixmaps.size(); i++) ++ { ++ // create texture ++ if (!glIsTexture(m_bufferPool.pixmaps[i].texture)) ++ continue; ++ ++ //bind texture ++ glBindTexture(GL_TEXTURE_2D, m_bufferPool.pixmaps[i].texture); ++ ++ // release pixmap ++ glXReleaseTexImageEXT(m_Display, m_bufferPool.pixmaps[i].glPixmap, GLX_FRONT_LEFT_EXT); ++ ++ glBindTexture(GL_TEXTURE_2D, 0); ++ ++ glDeleteTextures(1, &m_bufferPool.pixmaps[i].texture); ++ } ++ CLog::Log(LOGNOTICE, "VDPAU::COutput: unbound pixmaps"); ++} ++ ++bool COutput::CheckStatus(VdpStatus vdp_st, int line) ++{ ++ if (vdp_st != VDP_STATUS_OK) ++ { ++ CLog::Log(LOGERROR, " (VDPAU) Error: %s(%d) at %s:%d\n", m_config.vdpProcs.vdp_get_error_string(vdp_st), vdp_st, __FILE__, line); ++ m_vdpError = true; ++ return true; ++ } ++ return false; ++} ++ ++bool COutput::CreateGlxContext() ++{ ++ GLXContext glContext; ++ Window window; ++ ++ m_Display = g_Windowing.GetDisplay(); ++ glContext = g_Windowing.GetGlxContext(); ++ m_Window = g_Windowing.GetWindow(); ++ ++ // Get our window attribs. ++ XWindowAttributes wndattribs; ++ XGetWindowAttributes(m_Display, m_Window, &wndattribs); ++ ++ // Get visual Info ++ XVisualInfo visInfo; ++ visInfo.visualid = wndattribs.visual->visualid; ++ int nvisuals = 0; ++ XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals); ++ if (nvisuals != 1) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - could not find visual"); ++ return false; ++ } ++ visInfo = visuals[0]; ++ XFree(visuals); ++ ++ m_pixmap = XCreatePixmap(m_Display, ++ m_Window, ++ 192, ++ 108, ++ visInfo.depth); ++ if (!m_pixmap) ++ { ++ CLog::Log(LOGERROR, "VDPAU::COutput::CreateGlxContext - Unable to create XPixmap"); ++ return false; ++ } ++ ++ // create gl pixmap ++ m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap); ++ ++ if (!m_glPixmap) ++ { ++ CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not create glPixmap"); ++ return false; ++ } ++ ++ m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True); ++ ++ if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext)) ++ { ++ CLog::Log(LOGINFO, "VDPAU::COutput::CreateGlxContext - Could not make Pixmap current"); ++ return false; ++ } ++ ++ CLog::Log(LOGNOTICE, "VDPAU::COutput::CreateGlxContext - created context"); ++ return true; ++} ++ ++bool COutput::DestroyGlxContext() ++{ ++ if (m_glContext) ++ { ++ glXMakeCurrent(m_Display, None, NULL); ++ glXDestroyContext(m_Display, m_glContext); ++ } ++ m_glContext = 0; ++ ++ if (m_glPixmap) ++ glXDestroyPixmap(m_Display, m_glPixmap); ++ m_glPixmap = 0; ++ ++ if (m_pixmap) ++ XFreePixmap(m_Display, m_pixmap); ++ m_pixmap = 0; ++ ++ return true; ++} ++ + #endif +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.h 2012-10-03 03:02:11.382941358 +0200 +@@ -1,4 +1,3 @@ +- + #pragma once + /* + * Copyright (C) 2005-2012 Team XBMC +@@ -20,9 +19,30 @@ + * + */ + ++/** ++ * design goals: ++ * - improve performance ++ * max out hw resources: e.g. make 1080p60 play on ION2 ++ * allow advanced de-interlacing on ION ++ * ++ * - add vdpau/opengl interop ++ * ++ * - remove tight dependency to render thread ++ * prior design needed to hijack render thread in order to do ++ * gl interop functions. In particular this was a problem for ++ * init and clear down. Introduction of GL_NV_vdpau_interop ++ * increased the need to be independent from render thread ++ * ++ * - move to an actor based design in order to reduce the number ++ * of locks needed. ++ */ ++ + #include "system_gl.h" + +-#include ++#include "DllAvUtil.h" ++#include "DVDVideoCodec.h" ++#include "DVDVideoCodecFFmpeg.h" ++#include "libavcodec/vdpau.h" + #include + #include + #define GLX_GLXEXT_PROTOTYPES +@@ -37,118 +57,31 @@ + #include "settings/VideoSettings.h" + #include "guilib/DispResource.h" + #include "threads/Event.h" +-namespace Surface { class CSurface; } ++#include "threads/Thread.h" ++#include "utils/ActorProtocol.h" + +-#define NUM_OUTPUT_SURFACES 4 +-#define NUM_VIDEO_SURFACES_MPEG2 10 // (1 frame being decoded, 2 reference) +-#define NUM_VIDEO_SURFACES_H264 32 // (1 frame being decoded, up to 16 references) +-#define NUM_VIDEO_SURFACES_VC1 10 // (same as MPEG-2) +-#define NUM_OUTPUT_SURFACES_FOR_FULLHD 2 +-#define FULLHD_WIDTH 1920 ++using namespace Actor; + +-class CVDPAU +- : public CDVDVideoCodecFFmpeg::IHardwareDecoder +- , public IDispResource +-{ +-public: +- +- struct pictureAge +- { +- int b_age; +- int ip_age[2]; +- }; +- +- struct Desc +- { +- const char *name; +- uint32_t id; +- uint32_t aux; /* optional extra parameter... */ +- }; + +- CVDPAU(); +- virtual ~CVDPAU(); +- +- virtual bool Open (AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces = 0); +- virtual int Decode (AVCodecContext* avctx, AVFrame* frame); +- virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture); +- virtual void Reset(); +- virtual void Close(); +- +- virtual int Check(AVCodecContext* avctx); +- +- virtual const std::string Name() { return "vdpau"; } +- +- bool MakePixmap(int width, int height); +- bool MakePixmapGL(); +- +- void ReleasePixmap(); +- void BindPixmap(); +- +- PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT; +- PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT; +- GLXPixmap m_glPixmap; +- Pixmap m_Pixmap; +- +- static void FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic); +- static void FFDrawSlice(struct AVCodecContext *s, +- const AVFrame *src, int offset[4], +- int y, int type, int height); +- static int FFGetBuffer(AVCodecContext *avctx, AVFrame *pic); +- +- void Present(); +- bool ConfigVDPAU(AVCodecContext *avctx, int ref_frames); +- void SpewHardwareAvailable(); +- void InitCSCMatrix(int Height); +- bool CheckStatus(VdpStatus vdp_st, int line); +- bool IsSurfaceValid(vdpau_render_state *render); +- +- void CheckFeatures(); +- void SetColor(); +- void SetNoiseReduction(); +- void SetSharpness(); +- void SetDeinterlacing(); +- void SetHWUpscaling(); ++#define FULLHD_WIDTH 1920 ++#define MAX_PIC_Q_LENGTH 20 //for non-interop_yuv this controls the max length of the decoded pic to render completion Q + +- pictureAge picAge; +- vdpau_render_state *past[2], *current, *future; +- int tmpDeintMode, tmpDeintGUI, tmpDeint; +- float tmpNoiseReduction, tmpSharpness; +- float tmpBrightness, tmpContrast; +- int OutWidth, OutHeight; +- bool upScale; +- std::queue m_DVDVideoPics; +- +- static inline void ClearUsedForRender(vdpau_render_state **st) +- { +- if (*st) { +- (*st)->state &= ~FF_VDPAU_STATE_USED_FOR_RENDER; +- *st = NULL; +- } +- } ++namespace VDPAU ++{ + +- VdpProcamp m_Procamp; +- VdpCSCMatrix m_CSCMatrix; +- VdpDevice HasDevice() { return vdp_device != VDP_INVALID_HANDLE; }; +- VdpChromaType vdp_chroma_type; ++/** ++ * VDPAU interface to driver ++ */ + ++struct VDPAU_procs ++{ ++ VdpGetProcAddress * vdp_get_proc_address; ++ VdpDeviceDestroy * vdp_device_destroy; + +- // protected: +- void InitVDPAUProcs(); +- void FiniVDPAUProcs(); +- void FiniVDPAUOutput(); +- bool ConfigOutputMethod(AVCodecContext *avctx, AVFrame *pFrame); +- bool FiniOutputMethod(); +- +- VdpDevice vdp_device; +- VdpGetProcAddress * vdp_get_proc_address; +- VdpPresentationQueueTarget vdp_flip_target; +- VdpPresentationQueue vdp_flip_queue; +- VdpDeviceDestroy * vdp_device_destroy; +- +- VdpVideoSurfaceCreate * vdp_video_surface_create; +- VdpVideoSurfaceDestroy * vdp_video_surface_destroy; +- VdpVideoSurfacePutBitsYCbCr * vdp_video_surface_put_bits_y_cb_cr; +- VdpVideoSurfaceGetBitsYCbCr * vdp_video_surface_get_bits_y_cb_cr; ++ VdpVideoSurfaceCreate * vdp_video_surface_create; ++ VdpVideoSurfaceDestroy * vdp_video_surface_destroy; ++ VdpVideoSurfacePutBitsYCbCr * vdp_video_surface_put_bits_y_cb_cr; ++ VdpVideoSurfaceGetBitsYCbCr * vdp_video_surface_get_bits_y_cb_cr; + + VdpOutputSurfacePutBitsYCbCr * vdp_output_surface_put_bits_y_cb_cr; + VdpOutputSurfacePutBitsNative * vdp_output_surface_put_bits_native; +@@ -158,15 +91,15 @@ + VdpOutputSurfaceRenderOutputSurface * vdp_output_surface_render_output_surface; + VdpOutputSurfacePutBitsIndexed * vdp_output_surface_put_bits_indexed; + +- VdpVideoMixerCreate * vdp_video_mixer_create; +- VdpVideoMixerSetFeatureEnables * vdp_video_mixer_set_feature_enables; +- VdpVideoMixerQueryParameterSupport * vdp_video_mixer_query_parameter_support; +- VdpVideoMixerQueryFeatureSupport * vdp_video_mixer_query_feature_support; +- VdpVideoMixerDestroy * vdp_video_mixer_destroy; +- VdpVideoMixerRender * vdp_video_mixer_render; +- VdpVideoMixerSetAttributeValues * vdp_video_mixer_set_attribute_values; ++ VdpVideoMixerCreate * vdp_video_mixer_create; ++ VdpVideoMixerSetFeatureEnables * vdp_video_mixer_set_feature_enables; ++ VdpVideoMixerQueryParameterSupport * vdp_video_mixer_query_parameter_support; ++ VdpVideoMixerQueryFeatureSupport * vdp_video_mixer_query_feature_support; ++ VdpVideoMixerDestroy * vdp_video_mixer_destroy; ++ VdpVideoMixerRender * vdp_video_mixer_render; ++ VdpVideoMixerSetAttributeValues * vdp_video_mixer_set_attribute_values; + +- VdpGenerateCSCMatrix * vdp_generate_csc_matrix; ++ VdpGenerateCSCMatrix * vdp_generate_csc_matrix; + + VdpPresentationQueueTargetDestroy * vdp_presentation_queue_target_destroy; + VdpPresentationQueueCreate * vdp_presentation_queue_create; +@@ -179,64 +112,460 @@ + + VdpGetErrorString * vdp_get_error_string; + +- VdpDecoderCreate * vdp_decoder_create; +- VdpDecoderDestroy * vdp_decoder_destroy; +- VdpDecoderRender * vdp_decoder_render; +- VdpDecoderQueryCapabilities * vdp_decoder_query_caps; ++ VdpDecoderCreate * vdp_decoder_create; ++ VdpDecoderDestroy * vdp_decoder_destroy; ++ VdpDecoderRender * vdp_decoder_render; ++ VdpDecoderQueryCapabilities * vdp_decoder_query_caps; + + VdpPreemptionCallbackRegister * vdp_preemption_callback_register; + +- VdpOutputSurface outputSurfaces[NUM_OUTPUT_SURFACES]; +- VdpOutputSurface outputSurface; +- VdpOutputSurface presentSurface; +- +- VdpDecoder decoder; +- VdpVideoMixer videoMixer; +- VdpRect outRect; +- VdpRect outRectVid; ++}; + +- static void* dl_handle; +- VdpStatus (*dl_vdp_device_create_x11)(Display* display, int screen, VdpDevice* device, VdpGetProcAddress **get_proc_address); +- VdpStatus (*dl_vdp_get_proc_address)(VdpDevice device, VdpFuncId function_id, void** function_pointer); +- VdpStatus (*dl_vdp_preemption_callback_register)(VdpDevice device, VdpPreemptionCallback callback, void* context); ++//----------------------------------------------------------------------------- ++// VDPAU data structs ++//----------------------------------------------------------------------------- + +- int surfaceNum; +- int presentSurfaceNum; +- int totalAvailableOutputSurfaces; +- uint32_t vid_width, vid_height; +- int surface_width, surface_height; +- uint32_t max_references; +- Display* m_Display; +- bool vdpauConfigured; +- uint32_t *m_BlackBar; ++class CDecoder; + ++/** ++ * Buffer statistics used to control number of frames in queue ++ */ + ++class CVdpauBufferStats ++{ ++public: ++ uint16_t decodedPics; ++ uint16_t processedPics; ++ uint16_t renderPics; ++ uint64_t latency; // time decoder has waited for a frame, ideally there is no latency ++ int playSpeed; ++ bool canSkipDeint; ++ int processCmd; ++ ++ void IncDecoded() { CSingleLock l(m_sec); decodedPics++;} ++ void DecDecoded() { CSingleLock l(m_sec); decodedPics--;} ++ void IncProcessed() { CSingleLock l(m_sec); processedPics++;} ++ void DecProcessed() { CSingleLock l(m_sec); processedPics--;} ++ void IncRender() { CSingleLock l(m_sec); renderPics++;} ++ void DecRender() { CSingleLock l(m_sec); renderPics--;} ++ void Reset() { CSingleLock l(m_sec); decodedPics=0; processedPics=0;renderPics=0;latency=0;} ++ void Get(uint16_t &decoded, uint16_t &processed, uint16_t &render) {CSingleLock l(m_sec); decoded = decodedPics, processed=processedPics, render=renderPics;} ++ void SetParams(uint64_t time, int speed) { CSingleLock l(m_sec); latency = time; playSpeed = speed; } ++ void GetParams(uint64_t &lat, int &speed) { CSingleLock l(m_sec); lat = latency; speed = playSpeed; } ++ void SetCmd(int cmd) { CSingleLock l(m_sec); processCmd = cmd; } ++ void GetCmd(int &cmd) { CSingleLock l(m_sec); cmd = processCmd; processCmd = 0; } ++ void SetCanSkipDeint(bool canSkip) { CSingleLock l(m_sec); canSkipDeint = canSkip; } ++ bool CanSkipDeint() { CSingleLock l(m_sec); if (canSkipDeint) return true; else return false;} ++private: ++ CCriticalSection m_sec; ++}; ++ ++/** ++ * CVdpauConfig holds all configuration parameters needed by vdpau ++ * The structure is sent to the internal classes CMixer and COutput ++ * for init. ++ */ ++ ++struct CVdpauConfig ++{ ++ int surfaceWidth; ++ int surfaceHeight; ++ int vidWidth; ++ int vidHeight; ++ int outWidth; ++ int outHeight; ++ VDPAU_procs vdpProcs; ++ VdpDevice vdpDevice; ++ VdpDecoder vdpDecoder; ++ VdpChromaType vdpChromaType; ++ CVdpauBufferStats *stats; ++ CDecoder *vdpau; ++ int featureCount; ++ int upscale; ++ VdpVideoMixerFeature vdpFeatures[14]; ++ std::vector *videoSurfaces; ++ CCriticalSection *videoSurfaceSec; ++ bool usePixmaps; ++ int numRenderBuffers; ++ uint32_t maxReferences; ++ bool useInteropYuv; ++}; ++ ++/** ++ * Holds a decoded frame ++ * Input to COutput for further processing ++ */ ++struct CVdpauDecodedPicture ++{ ++ DVDVideoPicture DVDPic; ++ vdpau_render_state *render; ++}; ++ ++/** ++ * Frame after having been processed by vdpau mixer ++ */ ++struct CVdpauProcessedPicture ++{ ++ DVDVideoPicture DVDPic; ++ vdpau_render_state *render; ++ VdpOutputSurface outputSurface; ++}; ++ ++/** ++ * Ready to render textures ++ * Sent from COutput back to CDecoder ++ * Objects are referenced by DVDVideoPicture and are sent ++ * to renderer ++ */ ++class CVdpauRenderPicture ++{ ++ friend class CDecoder; ++ friend class COutput; ++public: ++ DVDVideoPicture DVDPic; ++ int texWidth, texHeight; ++ CRect crop; ++ GLuint texture[4]; ++ uint32_t sourceIdx; ++ bool valid; ++ CDecoder *vdpau; ++ CVdpauRenderPicture* Acquire(); ++ long Release(); ++private: ++ void ReturnUnused(); ++ int refCount; ++ CCriticalSection *renderPicSection; ++}; ++ ++//----------------------------------------------------------------------------- ++// Mixer ++//----------------------------------------------------------------------------- ++ ++class CMixerControlProtocol : public Protocol ++{ ++public: ++ CMixerControlProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {}; ++ enum OutSignal ++ { ++ INIT = 0, ++ FLUSH, ++ TIMEOUT, ++ }; ++ enum InSignal ++ { ++ ACC, ++ ERROR, ++ }; ++}; ++ ++class CMixerDataProtocol : public Protocol ++{ ++public: ++ CMixerDataProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {}; ++ enum OutSignal ++ { ++ FRAME, ++ BUFFER, ++ }; ++ enum InSignal ++ { ++ PICTURE, ++ }; ++}; ++ ++/** ++ * Embeds the vdpau video mixer ++ * Embedded by COutput class, gets decoded frames from COutput, processes ++ * them in mixer ands sends processed frames back to COutput ++ */ ++class CMixer : private CThread ++{ ++public: ++ CMixer(CEvent *inMsgEvent); ++ virtual ~CMixer(); ++ void Start(); ++ void Dispose(); ++ CMixerControlProtocol m_controlPort; ++ CMixerDataProtocol m_dataPort; ++protected: ++ void OnStartup(); ++ void OnExit(); ++ void Process(); ++ void StateMachine(int signal, Protocol *port, Message *msg); ++ void Init(); ++ void Uninit(); ++ void Flush(); ++ void CreateVdpauMixer(); ++ void ProcessPicture(); ++ void InitCycle(); ++ void FiniCycle(); ++ void CheckFeatures(); ++ void SetPostProcFeatures(bool postProcEnabled); ++ void PostProcOff(); ++ void InitCSCMatrix(int Width); ++ bool GenerateStudioCSCMatrix(VdpColorStandard colorStandard, VdpCSCMatrix &studioCSCMatrix); ++ void SetColor(); ++ void SetNoiseReduction(); ++ void SetSharpness(); ++ void SetDeintSkipChroma(); ++ void SetDeinterlacing(); ++ void SetHWUpscaling(); ++ void DisableHQScaling(); ++ EINTERLACEMETHOD GetDeinterlacingMethod(bool log = false); ++ bool CheckStatus(VdpStatus vdp_st, int line); ++ CEvent m_outMsgEvent; ++ CEvent *m_inMsgEvent; ++ int m_state; ++ bool m_bStateMachineSelfTrigger; ++ ++ // extended state variables for state machine ++ int m_extTimeout; ++ bool m_vdpError; ++ CVdpauConfig m_config; ++ VdpVideoMixer m_videoMixer; ++ VdpProcamp m_Procamp; ++ VdpCSCMatrix m_CSCMatrix; ++ bool m_PostProc; ++ float m_Brightness; ++ float m_Contrast; ++ float m_NoiseReduction; ++ float m_Sharpness; ++ int m_DeintMode; ++ int m_Deint; ++ int m_Upscale; ++ uint32_t *m_BlackBar; + VdpVideoMixerPictureStructure m_mixerfield; +- int m_mixerstep; ++ int m_mixerstep; ++ int m_mixersteps; ++ CVdpauProcessedPicture m_processPicture; ++ std::queue m_outputSurfaces; ++ std::queue m_decodedPics; ++ std::deque m_mixerInput; ++}; ++ ++//----------------------------------------------------------------------------- ++// Output ++//----------------------------------------------------------------------------- ++ ++/** ++ * Buffer pool holds allocated vdpau and gl resources ++ * Embedded in COutput ++ */ ++struct VdpauBufferPool ++{ ++ struct Pixmaps ++ { ++ unsigned short id; ++ bool used; ++ DVDVideoPicture DVDPic; ++ GLuint texture; ++ Pixmap pixmap; ++ GLXPixmap glPixmap; ++ VdpPresentationQueueTarget vdp_flip_target; ++ VdpPresentationQueue vdp_flip_queue; ++ VdpOutputSurface surface; ++ }; ++ struct GLVideoSurface ++ { ++ GLuint texture[4]; ++#ifdef GL_NV_vdpau_interop ++ GLvdpauSurfaceNV glVdpauSurface; ++#endif ++ vdpau_render_state *sourceVuv; ++ VdpOutputSurface sourceRgb; ++ }; ++ unsigned short numOutputSurfaces; ++ std::vector pixmaps; ++ std::vector outputSurfaces; ++ std::deque notVisiblePixmaps; ++ std::vector allRenderPics; ++ std::map glVideoSurfaceMap; ++ std::map glOutputSurfaceMap; ++ std::queue processedPics; ++ std::deque usedRenderPics; ++ std::deque freeRenderPics; ++ CCriticalSection renderPicSec; ++}; ++ ++class COutputControlProtocol : public Protocol ++{ ++public: ++ COutputControlProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {}; ++ enum OutSignal ++ { ++ INIT, ++ FLUSH, ++ PRECLEANUP, ++ TIMEOUT, ++ }; ++ enum InSignal ++ { ++ ACC, ++ ERROR, ++ STATS, ++ }; ++}; ++ ++class COutputDataProtocol : public Protocol ++{ ++public: ++ COutputDataProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {}; ++ enum OutSignal ++ { ++ NEWFRAME = 0, ++ RETURNPIC, ++ }; ++ enum InSignal ++ { ++ PICTURE, ++ }; ++}; ++ ++/** ++ * COutput is embedded in CDecoder and embeds CMixer ++ * The class has its own OpenGl context which is shared with render thread ++ * COuput generated ready to render textures and passes them back to ++ * CDecoder ++ */ ++class COutput : private CThread ++{ ++public: ++ COutput(CEvent *inMsgEvent); ++ virtual ~COutput(); ++ void Start(); ++ void Dispose(); ++ COutputControlProtocol m_controlPort; ++ COutputDataProtocol m_dataPort; ++protected: ++ void OnStartup(); ++ void OnExit(); ++ void Process(); ++ void StateMachine(int signal, Protocol *port, Message *msg); ++ bool HasWork(); ++ CVdpauRenderPicture *ProcessMixerPicture(); ++ void ProcessReturnPicture(CVdpauRenderPicture *pic); ++ int FindFreePixmap(); ++ bool Init(); ++ bool Uninit(); ++ void Flush(); ++ bool CreateGlxContext(); ++ bool DestroyGlxContext(); ++ bool EnsureBufferPool(); ++ void ReleaseBufferPool(); ++ void InitMixer(); ++ bool GLInit(); ++ void GLMapSurfaces(); ++ void GLUnmapSurfaces(); ++ void GLBindPixmaps(); ++ void GLUnbindPixmaps(); ++ bool MakePixmap(VdpauBufferPool::Pixmaps &pixmap); ++ bool MakePixmapGL(VdpauBufferPool::Pixmaps &pixmap); ++ bool CheckStatus(VdpStatus vdp_st, int line); ++ CEvent m_outMsgEvent; ++ CEvent *m_inMsgEvent; ++ int m_state; ++ bool m_bStateMachineSelfTrigger; ++ ++ // extended state variables for state machine ++ int m_extTimeout; ++ bool m_vdpError; ++ CVdpauConfig m_config; ++ VdpauBufferPool m_bufferPool; ++ CMixer m_mixer; ++ Display *m_Display; ++ Window m_Window; ++ GLXContext m_glContext; ++ GLXWindow m_glWindow; ++ Pixmap m_pixmap; ++ GLXPixmap m_glPixmap; ++ ++ // gl functions ++ PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT; ++ PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT; ++#ifdef GL_NV_vdpau_interop ++ PFNGLVDPAUINITNVPROC glVDPAUInitNV; ++ PFNGLVDPAUFININVPROC glVDPAUFiniNV; ++ PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC glVDPAURegisterOutputSurfaceNV; ++ PFNGLVDPAUREGISTERVIDEOSURFACENVPROC glVDPAURegisterVideoSurfaceNV; ++ PFNGLVDPAUISSURFACENVPROC glVDPAUIsSurfaceNV; ++ PFNGLVDPAUUNREGISTERSURFACENVPROC glVDPAUUnregisterSurfaceNV; ++ PFNGLVDPAUSURFACEACCESSNVPROC glVDPAUSurfaceAccessNV; ++ PFNGLVDPAUMAPSURFACESNVPROC glVDPAUMapSurfacesNV; ++ PFNGLVDPAUUNMAPSURFACESNVPROC glVDPAUUnmapSurfacesNV; ++ PFNGLVDPAUGETSURFACEIVNVPROC glVDPAUGetSurfaceivNV; ++#endif ++}; ++ ++//----------------------------------------------------------------------------- ++// VDPAU decoder ++//----------------------------------------------------------------------------- ++ ++/** ++ * VDPAU main class ++ */ ++class CDecoder ++ : public CDVDVideoCodecFFmpeg::IHardwareDecoder ++ , public IDispResource ++{ ++ friend class CVdpauRenderPicture; ++ ++public: ++ ++ struct Desc ++ { ++ const char *name; ++ uint32_t id; ++ uint32_t aux; /* optional extra parameter... */ ++ }; ++ ++ CDecoder(); ++ virtual ~CDecoder(); ++ ++ virtual bool Open (AVCodecContext* avctx, const enum PixelFormat, unsigned int surfaces = 0); ++ virtual int Decode (AVCodecContext* avctx, AVFrame* frame) {return Decode(avctx, frame, false, false);}; ++ virtual int Decode (AVCodecContext* avctx, AVFrame* frame, bool bSoftDrain = false, bool bHardDrain = false); ++ virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture); ++ virtual void Reset(); ++ virtual void Close(); ++ virtual long Release(); ++ virtual bool CanSkipDeint(); ++ ++ virtual int Check(AVCodecContext* avctx); ++ virtual const std::string Name() { return "vdpau"; } + + bool Supports(VdpVideoMixerFeature feature); + bool Supports(EINTERLACEMETHOD method); + EINTERLACEMETHOD AutoInterlaceMethod(); ++ static bool IsVDPAUFormat(PixelFormat fmt); + +- VdpVideoMixerFeature m_features[14]; +- int m_feature_count; ++ static void FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic); ++ static void FFDrawSlice(struct AVCodecContext *s, ++ const AVFrame *src, int offset[4], ++ int y, int type, int height); ++ static int FFGetBuffer(AVCodecContext *avctx, AVFrame *pic); ++ ++ virtual void OnLostDevice(); ++ virtual void OnResetDevice(); ++ ++protected: ++ void SetWidthHeight(int width, int height); ++ bool ConfigVDPAU(AVCodecContext *avctx, int ref_frames); ++ void SpewHardwareAvailable(); ++ bool CheckStatus(VdpStatus vdp_st, int line); ++ bool IsSurfaceValid(vdpau_render_state *render); ++ void InitVDPAUProcs(); ++ void FiniVDPAUProcs(); ++ void FiniVDPAUOutput(); ++ void ReturnRenderPicture(CVdpauRenderPicture *renderPic); ++ long ReleasePicReference(); + +- static bool IsVDPAUFormat(PixelFormat fmt); + static void ReadFormatOf( PixelFormat fmt + , VdpDecoderProfile &decoder_profile + , VdpChromaType &chroma_type); + +- std::vector m_videoSurfaces; +- DllAvUtil m_dllAvUtil; +- +- enum VDPAUOutputMethod +- { +- OUTPUT_NONE, +- OUTPUT_PIXMAP, +- OUTPUT_GL_INTEROP_RGB, +- OUTPUT_GL_INTEROP_YUV +- }; +- VDPAUOutputMethod m_vdpauOutputMethod; ++ VdpStatus (*dl_vdp_device_create_x11)(Display* display, int screen, VdpDevice* device, VdpGetProcAddress **get_proc_address); ++ VdpStatus (*dl_vdp_get_proc_address)(VdpDevice device, VdpFuncId function_id, void** function_pointer); ++ VdpStatus (*dl_vdp_preemption_callback_register)(VdpDevice device, VdpPreemptionCallback callback, void* context); + + // OnLostDevice triggers transition from all states to LOST + // internal errors trigger transition from OPEN to RESET +@@ -247,9 +576,24 @@ + , VDPAU_LOST + , VDPAU_ERROR + } m_DisplayState; +- CSharedSection m_DecoderSection; +- CSharedSection m_DisplaySection; ++ CCriticalSection m_DecoderSection; + CEvent m_DisplayEvent; +- virtual void OnLostDevice(); +- virtual void OnResetDevice(); ++ ++ static void* dl_handle; ++ DllAvUtil m_dllAvUtil; ++ Display* m_Display; ++ ThreadIdentifier m_decoderThread; ++ bool m_vdpauConfigured; ++ CVdpauConfig m_vdpauConfig; ++ std::vector m_videoSurfaces; ++ CCriticalSection m_videoSurfaceSec; ++ ++ COutput m_vdpauOutput; ++ CVdpauBufferStats m_bufferStats; ++ CEvent m_inMsgEvent; ++ CVdpauRenderPicture *m_presentPicture; ++ ++ int m_codecControl; + }; ++ ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp 2012-10-03 03:02:11.514944043 +0200 +@@ -0,0 +1,2396 @@ ++/* ++ * Copyright (C) 2005-2011 Team XBMC ++ * http://www.xbmc.org ++ * ++ * This Program 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, or (at your option) ++ * any later version. ++ * ++ * This Program 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 XBMC; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include "system.h" ++#ifdef HAVE_LIBXVBA ++ ++#include "system_gl.h" ++#include ++#include ++#include "XVBA.h" ++#include "windowing/WindowingFactory.h" ++#include "guilib/GraphicContext.h" ++#include "settings/GUISettings.h" ++#include "settings/Settings.h" ++#include "utils/TimeUtils.h" ++#include "cores/dvdplayer/DVDClock.h" ++ ++using namespace XVBA; ++ ++// XVBA interface ++ ++#define XVBA_LIBRARY "libXvBAW.so.1" ++ ++typedef Bool (*XVBAQueryExtensionProc) (Display *dpy, int *vers); ++typedef Status (*XVBACreateContextProc) (void *input, void *output); ++typedef Status (*XVBADestroyContextProc) (void *context); ++typedef Bool (*XVBAGetSessionInfoProc) (void *input, void *output); ++typedef Status (*XVBACreateSurfaceProc) (void *input, void *output); ++typedef Status (*XVBACreateGLSharedSurfaceProc)(void *input, void *output); ++typedef Status (*XVBADestroySurfaceProc) (void *surface); ++typedef Status (*XVBACreateDecodeBuffersProc) (void *input, void *output); ++typedef Status (*XVBADestroyDecodeBuffersProc) (void *input); ++typedef Status (*XVBAGetCapDecodeProc) (void *input, void *output); ++typedef Status (*XVBACreateDecodeProc) (void *input, void *output); ++typedef Status (*XVBADestroyDecodeProc) (void *session); ++typedef Status (*XVBAStartDecodePictureProc) (void *input); ++typedef Status (*XVBADecodePictureProc) (void *input); ++typedef Status (*XVBAEndDecodePictureProc) (void *input); ++typedef Status (*XVBASyncSurfaceProc) (void *input, void *output); ++typedef Status (*XVBAGetSurfaceProc) (void *input); ++typedef Status (*XVBATransferSurfaceProc) (void *input); ++ ++static struct ++{ ++ XVBAQueryExtensionProc QueryExtension; ++ XVBACreateContextProc CreateContext; ++ XVBADestroyContextProc DestroyContext; ++ XVBAGetSessionInfoProc GetSessionInfo; ++ XVBACreateSurfaceProc CreateSurface; ++ XVBACreateGLSharedSurfaceProc CreateGLSharedSurface; ++ XVBADestroySurfaceProc DestroySurface; ++ XVBACreateDecodeBuffersProc CreateDecodeBuffers; ++ XVBADestroyDecodeBuffersProc DestroyDecodeBuffers; ++ XVBAGetCapDecodeProc GetCapDecode; ++ XVBACreateDecodeProc CreateDecode; ++ XVBADestroyDecodeProc DestroyDecode; ++ XVBAStartDecodePictureProc StartDecodePicture; ++ XVBADecodePictureProc DecodePicture; ++ XVBAEndDecodePictureProc EndDecodePicture; ++ XVBASyncSurfaceProc SyncSurface; ++ XVBAGetSurfaceProc GetSurface; ++ XVBATransferSurfaceProc TransferSurface; ++}g_XVBA_vtable; ++ ++//----------------------------------------------------------------------------- ++//----------------------------------------------------------------------------- ++ ++CXVBAContext *CXVBAContext::m_context = 0; ++CCriticalSection CXVBAContext::m_section; ++Display *CXVBAContext::m_display = 0; ++void *CXVBAContext::m_dlHandle = 0; ++ ++CXVBAContext::CXVBAContext() ++{ ++ m_context = 0; ++// m_dlHandle = 0; ++ m_xvbaContext = 0; ++ m_refCount = 0; ++} ++ ++void CXVBAContext::Release() ++{ ++ CSingleLock lock(m_section); ++ ++ m_refCount--; ++ if (m_refCount <= 0) ++ { ++ Close(); ++ delete this; ++ m_context = 0; ++ } ++} ++ ++void CXVBAContext::Close() ++{ ++ CLog::Log(LOGNOTICE, "XVBA::Close - closing decoder context"); ++ ++ DestroyContext(); ++// if (m_dlHandle) ++// { ++// dlclose(m_dlHandle); ++// m_dlHandle = 0; ++// } ++} ++ ++bool CXVBAContext::EnsureContext(CXVBAContext **ctx) ++{ ++ CSingleLock lock(m_section); ++ ++ if (m_context) ++ { ++ m_context->m_refCount++; ++ *ctx = m_context; ++ return true; ++ } ++ ++ m_context = new CXVBAContext(); ++ *ctx = m_context; ++ { ++ CSingleLock gLock(g_graphicsContext); ++ if (!m_context->LoadSymbols() || !m_context->CreateContext()) ++ { ++ delete m_context; ++ m_context = 0; ++ return false; ++ } ++ } ++ ++ m_context->m_refCount++; ++ ++ *ctx = m_context; ++ return true; ++} ++ ++bool CXVBAContext::LoadSymbols() ++{ ++ if (!m_dlHandle) ++ { ++ m_dlHandle = dlopen(XVBA_LIBRARY, RTLD_LAZY); ++ if (!m_dlHandle) ++ { ++ const char* error = dlerror(); ++ if (!error) ++ error = "dlerror() returned NULL"; ++ ++ CLog::Log(LOGERROR,"XVBA::LoadSymbols: Unable to get handle to lib: %s", error); ++ return false; ++ } ++ } ++ else ++ return true; ++ ++#define INIT_PROC(PREFIX, PROC) do { \ ++ g_##PREFIX##_vtable.PROC = (PREFIX##PROC##Proc) \ ++ dlsym(m_dlHandle, #PREFIX #PROC); \ ++ } while (0) ++ ++#define INIT_PROC_CHECK(PREFIX, PROC) do { \ ++ dlerror(); \ ++ INIT_PROC(PREFIX, PROC); \ ++ if (dlerror()) { \ ++ dlclose(m_dlHandle); \ ++ m_dlHandle = NULL; \ ++ return false; \ ++ } \ ++ } while (0) ++ ++#define XVBA_INIT_PROC(PROC) INIT_PROC_CHECK(XVBA, PROC) ++ ++ XVBA_INIT_PROC(QueryExtension); ++ XVBA_INIT_PROC(CreateContext); ++ XVBA_INIT_PROC(DestroyContext); ++ XVBA_INIT_PROC(GetSessionInfo); ++ XVBA_INIT_PROC(CreateSurface); ++ XVBA_INIT_PROC(CreateGLSharedSurface); ++ XVBA_INIT_PROC(DestroySurface); ++ XVBA_INIT_PROC(CreateDecodeBuffers); ++ XVBA_INIT_PROC(DestroyDecodeBuffers); ++ XVBA_INIT_PROC(GetCapDecode); ++ XVBA_INIT_PROC(CreateDecode); ++ XVBA_INIT_PROC(DestroyDecode); ++ XVBA_INIT_PROC(StartDecodePicture); ++ XVBA_INIT_PROC(DecodePicture); ++ XVBA_INIT_PROC(EndDecodePicture); ++ XVBA_INIT_PROC(SyncSurface); ++ XVBA_INIT_PROC(GetSurface); ++ XVBA_INIT_PROC(TransferSurface); ++ ++#undef XVBA_INIT_PROC ++#undef INIT_PROC ++ ++ return true; ++} ++ ++bool CXVBAContext::CreateContext() ++{ ++ if (m_xvbaContext) ++ return true; ++ ++ CLog::Log(LOGNOTICE,"XVBA::CreateContext - creating decoder context"); ++ ++ Drawable window; ++ { CSingleLock lock(g_graphicsContext); ++ if (!m_display) ++ m_display = XOpenDisplay(NULL); ++ window = 0; ++ } ++ ++ int version; ++ if (!g_XVBA_vtable.QueryExtension(m_display, &version)) ++ return false; ++ CLog::Log(LOGNOTICE,"XVBA::CreateContext - opening xvba version: %i", version); ++ ++ // create XVBA Context ++ XVBA_Create_Context_Input contextInput; ++ XVBA_Create_Context_Output contextOutput; ++ contextInput.size = sizeof(contextInput); ++ contextInput.display = m_display; ++ contextInput.draw = window; ++ contextOutput.size = sizeof(contextOutput); ++ if(Success != g_XVBA_vtable.CreateContext(&contextInput, &contextOutput)) ++ { ++ CLog::Log(LOGERROR,"XVBA::CreateContext - failed to create context"); ++ return false; ++ } ++ m_xvbaContext = contextOutput.context; ++ ++ return true; ++} ++ ++void CXVBAContext::DestroyContext() ++{ ++ if (!m_xvbaContext) ++ return; ++ ++ g_XVBA_vtable.DestroyContext(m_xvbaContext); ++ m_xvbaContext = 0; ++// XCloseDisplay(m_display); ++} ++ ++void *CXVBAContext::GetContext() ++{ ++ return m_xvbaContext; ++} ++ ++//----------------------------------------------------------------------------- ++//----------------------------------------------------------------------------- ++ ++static unsigned int decoderId = 0; ++ ++CDecoder::CDecoder() : m_xvbaOutput(&m_inMsgEvent) ++{ ++ m_xvbaConfig.context = 0; ++ m_xvbaConfig.xvbaSession = 0; ++ m_xvbaConfig.videoSurfaces = &m_videoSurfaces; ++ m_xvbaConfig.videoSurfaceSec = &m_videoSurfaceSec; ++ m_xvbaConfig.apiSec = &m_apiSec; ++ ++ m_displayState = XVBA_OPEN; ++} ++ ++CDecoder::~CDecoder() ++{ ++ Close(); ++} ++ ++typedef struct { ++ unsigned int size; ++ unsigned int num_of_decodecaps; ++ XVBADecodeCap decode_caps_list[]; ++} XVBA_GetCapDecode_Output_Base; ++ ++void CDecoder::OnLostDevice() ++{ ++ CLog::Log(LOGNOTICE,"XVBA::OnLostDevice event"); ++ ++ CSingleLock lock(m_decoderSection); ++ DestroySession(); ++ if (m_xvbaConfig.context) ++ m_xvbaConfig.context->Release(); ++ m_xvbaConfig.context = 0; ++ ++ m_displayState = XVBA_LOST; ++ m_displayEvent.Reset(); ++} ++ ++void CDecoder::OnResetDevice() ++{ ++ CLog::Log(LOGNOTICE,"XVBA::OnResetDevice event"); ++ ++ CSingleLock lock(m_decoderSection); ++ if (m_displayState == XVBA_LOST) ++ { ++ m_displayState = XVBA_RESET; ++ lock.Leave(); ++ m_displayEvent.Set(); ++ } ++} ++ ++bool CDecoder::Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces) ++{ ++ std::string Vendor = g_Windowing.GetRenderVendor(); ++ std::transform(Vendor.begin(), Vendor.end(), Vendor.begin(), ::tolower); ++ if (Vendor.compare(0, 3, "ati") != 0) ++ { ++ return false; ++ } ++ ++ m_decoderId = decoderId++; ++ ++ CLog::Log(LOGNOTICE,"(XVBA::Open) opening xvba decoder, id: %d", m_decoderId); ++ ++ if(avctx->coded_width == 0 ++ || avctx->coded_height == 0) ++ { ++ CLog::Log(LOGWARNING,"(XVBA) no width/height available, can't init"); ++ return false; ++ } ++ ++ if (!m_dllAvUtil.Load()) ++ return false; ++ ++ if (!CXVBAContext::EnsureContext(&m_xvbaConfig.context)) ++ return false; ++ ++ // xvba get session info ++ XVBA_GetSessionInfo_Input sessionInput; ++ XVBA_GetSessionInfo_Output sessionOutput; ++ sessionInput.size = sizeof(sessionInput); ++ sessionInput.context = m_xvbaConfig.context->GetContext(); ++ sessionOutput.size = sizeof(sessionOutput); ++ if (Success != g_XVBA_vtable.GetSessionInfo(&sessionInput, &sessionOutput)) ++ { ++ CLog::Log(LOGERROR,"(XVBA) can't get session info"); ++ return false; ++ } ++ if (sessionOutput.getcapdecode_output_size == 0) ++ { ++ CLog::Log(LOGERROR,"(XVBA) session decode not supported"); ++ return false; ++ } ++ ++ // get decoder capabilities ++ XVBA_GetCapDecode_Input capInput; ++ XVBA_GetCapDecode_Output *capOutput; ++ capInput.size = sizeof(capInput); ++ capInput.context = m_xvbaConfig.context->GetContext(); ++ capOutput = (XVBA_GetCapDecode_Output *)calloc(sessionOutput.getcapdecode_output_size, 1); ++ capOutput->size = sessionOutput.getcapdecode_output_size; ++ if (Success != g_XVBA_vtable.GetCapDecode(&capInput, capOutput)) ++ { ++ CLog::Log(LOGERROR,"(XVBA) can't get decode capabilities"); ++ return false; ++ } ++ ++ int match = -1; ++ if (avctx->codec_id == CODEC_ID_H264) ++ { ++ // search for profile high ++ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i) ++ { ++ if (capOutput->decode_caps_list[i].capability_id == XVBA_H264 && ++ capOutput->decode_caps_list[i].flags == XVBA_H264_HIGH) ++ { ++ match = (int) i; ++ break; ++ } ++ } ++ if (match < 0) ++ { ++ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_H264_HIGH not found"); ++ } ++ } ++ else if (avctx->codec_id == CODEC_ID_VC1) ++ { ++ // search for profile advanced ++ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i) ++ { ++ if (capOutput->decode_caps_list[i].capability_id == XVBA_VC1 && ++ capOutput->decode_caps_list[i].flags == XVBA_VC1_ADVANCED) ++ { ++ match = (int) i; ++ break; ++ } ++ } ++ if (match < 0) ++ { ++ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_VC1_ADVANCED not found"); ++ } ++ } ++ else if (avctx->codec_id == CODEC_ID_MPEG2VIDEO) ++ { ++ // search for profile high ++ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i) ++ { ++ if (capOutput->decode_caps_list[i].capability_id == XVBA_MPEG2_VLD) ++ { ++ // XXX: uncomment when implemented ++ // match = (int) i; ++ // break; ++ } ++ } ++ if (match < 0) ++ { ++ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_MPEG2_VLD not found"); ++ } ++ } ++ else if (avctx->codec_id == CODEC_ID_WMV3) ++ { ++ // search for profile high ++ for (unsigned int i = 0; i < capOutput->num_of_decodecaps; ++i) ++ { ++ if (capOutput->decode_caps_list[i].capability_id == XVBA_VC1 && ++ capOutput->decode_caps_list[i].flags == XVBA_VC1_MAIN) ++ { ++ match = (int) i; ++ break; ++ } ++ } ++ if (match < 0) ++ { ++ CLog::Log(LOGNOTICE, "(XVBA::Open) - profile XVBA_VC1_MAIN not found"); ++ } ++ } ++ ++ if (match < 0) ++ { ++ free(capOutput); ++ return false; ++ } ++ ++ CLog::Log(LOGNOTICE,"(XVBA) using decoder capability id: %i flags: %i", ++ capOutput->decode_caps_list[match].capability_id, ++ capOutput->decode_caps_list[match].flags); ++ CLog::Log(LOGNOTICE,"(XVBA) using surface type: %x", ++ capOutput->decode_caps_list[match].surface_type); ++ ++ m_xvbaConfig.decoderCap = capOutput->decode_caps_list[match]; ++ ++ free(capOutput); ++ ++ // set some varables ++ m_xvbaConfig.xvbaSession = 0; ++ m_xvbaBufferPool.data_buffer = 0; ++ m_xvbaBufferPool.iq_matrix_buffer = 0; ++ m_xvbaBufferPool.picture_descriptor_buffer = 0; ++ m_presentPicture = 0; ++ m_xvbaConfig.numRenderBuffers = surfaces; ++ m_decoderThread = CThread::GetCurrentThreadId(); ++ m_speed = DVD_PLAYSPEED_NORMAL; ++ ++ if (1) //g_guiSettings.GetBool("videoplayer.usexvbasharedsurface")) ++ m_xvbaConfig.useSharedSurfaces = true; ++ else ++ m_xvbaConfig.useSharedSurfaces = false; ++ ++ m_displayState = XVBA_OPEN; ++ ++ // setup ffmpeg ++ avctx->thread_count = 1; ++ avctx->get_buffer = CDecoder::FFGetBuffer; ++ avctx->release_buffer = CDecoder::FFReleaseBuffer; ++ avctx->draw_horiz_band = CDecoder::FFDrawSlice; ++ avctx->slice_flags = SLICE_FLAG_CODED_ORDER|SLICE_FLAG_ALLOW_FIELD; ++ ++ g_Windowing.Register(this); ++ return true; ++} ++ ++void CDecoder::Close() ++{ ++ CLog::Log(LOGNOTICE, "XVBA::Close - closing decoder, id: %d", m_decoderId); ++ ++ if (!m_xvbaConfig.context) ++ return; ++ ++ DestroySession(); ++ if (m_xvbaConfig.context) ++ m_xvbaConfig.context->Release(); ++ m_xvbaConfig.context = 0; ++ ++ while (!m_videoSurfaces.empty()) ++ { ++ xvba_render_state *render = m_videoSurfaces.back(); ++ if(render->buffers_alllocated > 0) ++ m_dllAvUtil.av_free(render->buffers); ++ m_videoSurfaces.pop_back(); ++ free(render); ++ } ++ ++ g_Windowing.Unregister(this); ++ m_dllAvUtil.Unload(); ++} ++ ++long CDecoder::Release() ++{ ++ // check if we should do some pre-cleanup here ++ // a second decoder might need resources ++ if (m_xvbaConfig.xvbaSession) ++ { ++ CSingleLock lock(m_decoderSection); ++ CLog::Log(LOGNOTICE,"XVBA::Release pre-cleanup"); ++ ++ Message *reply; ++ if (m_xvbaOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::PRECLEANUP, ++ &reply, ++ 2000)) ++ { ++ bool success = reply->signal == COutputControlProtocol::ACC ? true : false; ++ reply->Release(); ++ if (!success) ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - pre-cleanup returned error", __FUNCTION__); ++ m_displayState = XVBA_ERROR; ++ } ++ } ++ else ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - pre-cleanup timed out", __FUNCTION__); ++ m_displayState = XVBA_ERROR; ++ } ++ ++ for(unsigned int i = 0; i < m_videoSurfaces.size(); ++i) ++ { ++ xvba_render_state *render = m_videoSurfaces[i]; ++ if (render->surface && !(render->state & FF_XVBA_STATE_USED_FOR_RENDER)) ++ { ++ g_XVBA_vtable.DestroySurface(render->surface); ++ render->surface = 0; ++ render->picture_descriptor = 0; ++ render->iq_matrix = 0; ++ } ++ } ++ } ++ return IHardwareDecoder::Release(); ++} ++ ++long CDecoder::ReleasePicReference() ++{ ++ return IHardwareDecoder::Release(); ++} ++ ++bool CDecoder::Supports(EINTERLACEMETHOD method) ++{ ++ if(method == VS_INTERLACEMETHOD_AUTO) ++ return true; ++ ++ if (1) //g_guiSettings.GetBool("videoplayer.usexvbasharedsurface")) ++ { ++ if (method == VS_INTERLACEMETHOD_XVBA) ++ return true; ++ } ++ ++ return false; ++} ++ ++void CDecoder::ResetState() ++{ ++ m_displayState = XVBA_OPEN; ++} ++ ++int CDecoder::Check(AVCodecContext* avctx) ++{ ++ EDisplayState state; ++ ++ { CSingleLock lock(m_decoderSection); ++ state = m_displayState; ++ } ++ ++ if (state == XVBA_LOST) ++ { ++ CLog::Log(LOGNOTICE,"XVBA::Check waiting for display reset event"); ++ if (!m_displayEvent.WaitMSec(2000)) ++ { ++ CLog::Log(LOGERROR, "XVBA::Check - device didn't reset in reasonable time"); ++ state = XVBA_RESET;; ++ } ++ else ++ { CSingleLock lock(m_decoderSection); ++ state = m_displayState; ++ } ++ } ++ if (state == XVBA_RESET || state == XVBA_ERROR) ++ { ++ CLog::Log(LOGNOTICE,"XVBA::Check - Attempting recovery"); ++ ++ CSingleLock gLock(g_graphicsContext); ++ CSingleLock lock(m_decoderSection); ++ ++ DestroySession(); ++ ResetState(); ++ CXVBAContext::EnsureContext(&m_xvbaConfig.context); ++ ++ if (state == XVBA_RESET) ++ return VC_FLUSHED; ++ else ++ return VC_ERROR; ++ } ++ return 0; ++} ++ ++void CDecoder::SetError(const char* function, const char* msg, int line) ++{ ++ CLog::Log(LOGERROR, "XVBA::%s - %s, line %d", function, msg, line); ++ CSingleLock lock(m_decoderSection); ++ m_displayState = XVBA_ERROR; ++} ++ ++bool CDecoder::CreateSession(AVCodecContext* avctx) ++{ ++ m_xvbaConfig.surfaceWidth = (avctx->coded_width+15) & ~15; ++ m_xvbaConfig.surfaceHeight = (avctx->coded_height+15) & ~15; ++ ++ m_xvbaConfig.vidWidth = avctx->width; ++ m_xvbaConfig.vidHeight = avctx->height; ++ ++ XVBA_Create_Decode_Session_Input sessionInput; ++ XVBA_Create_Decode_Session_Output sessionOutput; ++ ++ sessionInput.size = sizeof(sessionInput); ++ sessionInput.width = m_xvbaConfig.surfaceWidth; ++ sessionInput.height = m_xvbaConfig.surfaceHeight; ++ sessionInput.context = m_xvbaConfig.context->GetContext(); ++ sessionInput.decode_cap = &m_xvbaConfig.decoderCap; ++ sessionOutput.size = sizeof(sessionOutput); ++ ++ if (Success != g_XVBA_vtable.CreateDecode(&sessionInput, &sessionOutput)) ++ { ++ SetError(__FUNCTION__, "failed to create decoder session", __LINE__); ++ CLog::Log(LOGERROR, "Decoder failed with following stats: m_surfaceWidth %u, m_surfaceHeight %u," ++ " m_vidWidth %u, m_vidHeight %u, coded_width %d, coded_height %d", ++ m_xvbaConfig.surfaceWidth, ++ m_xvbaConfig.surfaceHeight, ++ m_xvbaConfig.vidWidth, ++ m_xvbaConfig.vidHeight, ++ avctx->coded_width, ++ avctx->coded_height); ++ return false; ++ } ++ m_xvbaConfig.xvbaSession = sessionOutput.session; ++ ++ // create decode buffers ++ XVBA_Create_DecodeBuff_Input bufferInput; ++ XVBA_Create_DecodeBuff_Output bufferOutput; ++ ++ bufferInput.size = sizeof(bufferInput); ++ bufferInput.session = m_xvbaConfig.xvbaSession; ++ bufferInput.buffer_type = XVBA_PICTURE_DESCRIPTION_BUFFER; ++ bufferInput.num_of_buffers = 1; ++ bufferOutput.size = sizeof(bufferOutput); ++ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput) ++ || bufferOutput.num_of_buffers_in_list != 1) ++ { ++ SetError(__FUNCTION__, "failed to create picture buffer", __LINE__); ++ return false; ++ } ++ m_xvbaBufferPool.picture_descriptor_buffer = bufferOutput.buffer_list; ++ ++ // data buffer ++ bufferInput.buffer_type = XVBA_DATA_BUFFER; ++ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput) ++ || bufferOutput.num_of_buffers_in_list != 1) ++ { ++ SetError(__FUNCTION__, "failed to create data buffer", __LINE__); ++ return false; ++ } ++ m_xvbaBufferPool.data_buffer = bufferOutput.buffer_list; ++ ++ // QO Buffer ++ bufferInput.buffer_type = XVBA_QM_BUFFER; ++ if (Success != g_XVBA_vtable.CreateDecodeBuffers(&bufferInput, &bufferOutput) ++ || bufferOutput.num_of_buffers_in_list != 1) ++ { ++ SetError(__FUNCTION__, "failed to create qm buffer", __LINE__); ++ return false; ++ } ++ m_xvbaBufferPool.iq_matrix_buffer = bufferOutput.buffer_list; ++ ++ ++ // initialize output ++ CSingleLock lock(g_graphicsContext); ++ m_xvbaConfig.stats = &m_bufferStats; ++ m_bufferStats.Reset(); ++ m_xvbaOutput.Start(); ++ Message *reply; ++ if (m_xvbaOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::INIT, ++ &reply, ++ 2000, ++ &m_xvbaConfig, ++ sizeof(m_xvbaConfig))) ++ { ++ bool success = reply->signal == COutputControlProtocol::ACC ? true : false; ++ reply->Release(); ++ if (!success) ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - vdpau output returned error", __FUNCTION__); ++ m_xvbaOutput.Dispose(); ++ return false; ++ } ++ } ++ else ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - failed to init output", __FUNCTION__); ++ m_xvbaOutput.Dispose(); ++ return false; ++ } ++ m_inMsgEvent.Reset(); ++ ++ return true; ++} ++ ++void CDecoder::DestroySession() ++{ ++ // wait for unfinished decoding jobs ++ XbmcThreads::EndTime timer; ++ if (m_xvbaConfig.xvbaSession) ++ { ++ for (unsigned int i = 0; i < m_videoSurfaces.size(); ++i) ++ { ++ xvba_render_state *render = m_videoSurfaces[i]; ++ if (render->surface) ++ { ++ XVBA_Surface_Sync_Input syncInput; ++ XVBA_Surface_Sync_Output syncOutput; ++ syncInput.size = sizeof(syncInput); ++ syncInput.session = m_xvbaConfig.xvbaSession; ++ syncInput.surface = render->surface; ++ syncInput.query_status = XVBA_GET_SURFACE_STATUS; ++ syncOutput.size = sizeof(syncOutput); ++ timer.Set(1000); ++ while(!timer.IsTimePast()) ++ { ++ if (Success != g_XVBA_vtable.SyncSurface(&syncInput, &syncOutput)) ++ { ++ CLog::Log(LOGERROR,"XVBA::DestroySession - failed sync surface"); ++ break; ++ } ++ if (!(syncOutput.status_flags & XVBA_STILL_PENDING)) ++ break; ++ Sleep(10); ++ } ++ if (timer.IsTimePast()) ++ CLog::Log(LOGERROR,"XVBA::DestroySession - unfinished decoding job"); ++ } ++ } ++ } ++ ++ m_xvbaOutput.Dispose(); ++ ++ XVBA_Destroy_Decode_Buffers_Input bufInput; ++ bufInput.size = sizeof(bufInput); ++ bufInput.num_of_buffers_in_list = 1; ++ bufInput.session = m_xvbaConfig.xvbaSession; ++ ++ for (unsigned int i=0; isurface) ++ { ++ g_XVBA_vtable.DestroySurface(render->surface); ++ render->surface = 0; ++ render->picture_descriptor = 0; ++ render->iq_matrix = 0; ++ } ++ } ++ ++ if (m_xvbaConfig.xvbaSession) ++ g_XVBA_vtable.DestroyDecode(m_xvbaConfig.xvbaSession); ++ m_xvbaConfig.xvbaSession = 0; ++} ++ ++bool CDecoder::IsSurfaceValid(xvba_render_state *render) ++{ ++ // find render state in queue ++ bool found(false); ++ unsigned int i; ++ for(i = 0; i < m_videoSurfaces.size(); ++i) ++ { ++ if(m_videoSurfaces[i] == render) ++ { ++ found = true; ++ break; ++ } ++ } ++ if (!found) ++ { ++ CLog::Log(LOGERROR,"%s - video surface not found", __FUNCTION__); ++ return false; ++ } ++ if (m_videoSurfaces[i]->surface == 0) ++ { ++ m_videoSurfaces[i]->state = 0; ++ return false; ++ } ++ ++ return true; ++} ++ ++bool CDecoder::EnsureDataControlBuffers(unsigned int num) ++{ ++ if (m_xvbaBufferPool.data_control_buffers.size() >= num) ++ return true; ++ ++ unsigned int missing = num - m_xvbaBufferPool.data_control_buffers.size(); ++ ++ XVBA_Create_DecodeBuff_Input bufferInput; ++ XVBA_Create_DecodeBuff_Output bufferOutput; ++ bufferInput.size = sizeof(bufferInput); ++ bufferInput.session = m_xvbaConfig.xvbaSession; ++ bufferInput.buffer_type = XVBA_DATA_CTRL_BUFFER; ++ bufferInput.num_of_buffers = 1; ++ bufferOutput.size = sizeof(bufferOutput); ++ ++ for (unsigned int i=0; iopaque; ++ CDecoder* xvba = (CDecoder*)ctx->GetHardware(); ++ unsigned int i; ++ ++ CSingleLock lock(xvba->m_decoderSection); ++ ++ xvba_render_state * render = NULL; ++ render = (xvba_render_state*)pic->data[0]; ++ if(!render) ++ { ++ CLog::Log(LOGERROR, "XVBA::FFReleaseBuffer - invalid context handle provided"); ++ return; ++ } ++ ++ for(i=0; i<4; i++) ++ pic->data[i]= NULL; ++ ++ // find render state in queue ++ if (!xvba->IsSurfaceValid(render)) ++ { ++ CLog::Log(LOGDEBUG, "XVBA::FFReleaseBuffer - ignoring invalid buffer"); ++ return; ++ } ++ ++ render->state &= ~FF_XVBA_STATE_USED_FOR_REFERENCE; ++} ++ ++void CDecoder::FFDrawSlice(struct AVCodecContext *avctx, ++ const AVFrame *src, int offset[4], ++ int y, int type, int height) ++{ ++ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque; ++ CDecoder* xvba = (CDecoder*)ctx->GetHardware(); ++ ++ CSingleLock lock(xvba->m_decoderSection); ++ ++ if(xvba->m_displayState != XVBA_OPEN) ++ return; ++ ++ if(src->linesize[0] || src->linesize[1] || src->linesize[2] ++ || offset[0] || offset[1] || offset[2]) ++ { ++ CLog::Log(LOGERROR, "XVBA::FFDrawSlice - invalid linesizes or offsets provided"); ++ return; ++ } ++ ++ xvba_render_state * render; ++ ++ render = (xvba_render_state*)src->data[0]; ++ if(!render) ++ { ++ CLog::Log(LOGERROR, "XVBA::FFDrawSlice - invalid context handle provided"); ++ return; ++ } ++ ++ // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid ++ if (!xvba->IsSurfaceValid(render)) ++ { ++ CLog::Log(LOGWARNING, "XVBA::FFDrawSlice - ignoring invalid buffer"); ++ return; ++ } ++ ++ // decoding ++ XVBA_Decode_Picture_Start_Input startInput; ++ startInput.size = sizeof(startInput); ++ startInput.session = xvba->m_xvbaConfig.xvbaSession; ++ startInput.target_surface = render->surface; ++ { CSingleLock lock(xvba->m_apiSec); ++ if (Success != g_XVBA_vtable.StartDecodePicture(&startInput)) ++ { ++ xvba->SetError(__FUNCTION__, "failed to start decoding", __LINE__); ++ return; ++ } ++ } ++ ++ XVBA_Decode_Picture_Input picInput; ++ picInput.size = sizeof(picInput); ++ picInput.session = xvba->m_xvbaConfig.xvbaSession; ++ XVBABufferDescriptor *list[2]; ++ picInput.buffer_list = list; ++ list[0] = xvba->m_xvbaBufferPool.picture_descriptor_buffer; ++ picInput.num_of_buffers_in_list = 1; ++ if (avctx->codec_id == CODEC_ID_H264) ++ { ++ list[1] = xvba->m_xvbaBufferPool.iq_matrix_buffer; ++ picInput.num_of_buffers_in_list = 2; ++ } ++ ++ { CSingleLock lock(xvba->m_apiSec); ++ if (Success != g_XVBA_vtable.DecodePicture(&picInput)) ++ { ++ xvba->SetError(__FUNCTION__, "failed to decode picture 1", __LINE__); ++ return; ++ } ++ } ++ ++ if (!xvba->EnsureDataControlBuffers(render->num_slices)) ++ return; ++ ++ XVBADataCtrl *dataControl; ++ int location = 0; ++ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer = 0; ++ for (unsigned int j = 0; j < render->num_slices; ++j) ++ { ++ int startCodeSize = 0; ++ uint8_t startCode[] = {0x00,0x00,0x01}; ++ if (avctx->codec_id == CODEC_ID_H264) ++ { ++ startCodeSize = 3; ++ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location, ++ startCode, 3); ++ } ++ else if (avctx->codec_id == CODEC_ID_VC1 && ++ (memcmp(render->buffers[j].buffer, startCode, 3) != 0)) ++ { ++ startCodeSize = 4; ++ uint8_t sdf = 0x0d; ++ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location, ++ startCode, 3); ++ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location+3, ++ &sdf, 1); ++ } ++ // check for potential buffer overwrite ++ unsigned int bytesToCopy = render->buffers[j].size; ++ unsigned int freeBufferSize = xvba->m_xvbaBufferPool.data_buffer->buffer_size - ++ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer; ++ if (bytesToCopy >= freeBufferSize) ++ { ++ xvba->SetError(__FUNCTION__, "bitstream buffer too large, maybe corrupted packet", __LINE__); ++ return; ++ } ++ memcpy((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+location+startCodeSize, ++ render->buffers[j].buffer, ++ render->buffers[j].size); ++ dataControl = (XVBADataCtrl*)xvba->m_xvbaBufferPool.data_control_buffers[j]->bufferXVBA; ++ dataControl->SliceDataLocation = location; ++ dataControl->SliceBytesInBuffer = render->buffers[j].size+startCodeSize; ++ dataControl->SliceBitsInBuffer = dataControl->SliceBytesInBuffer * 8; ++ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer += dataControl->SliceBytesInBuffer; ++ location += dataControl->SliceBytesInBuffer; ++ } ++ ++ int bufSize = xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer; ++ int padding = bufSize % 128; ++ if (padding) ++ { ++ padding = 128 - padding; ++ xvba->m_xvbaBufferPool.data_buffer->data_size_in_buffer += padding; ++ memset((uint8_t*)xvba->m_xvbaBufferPool.data_buffer->bufferXVBA+bufSize,0,padding); ++ } ++ ++ picInput.num_of_buffers_in_list = 2; ++ for (unsigned int i = 0; i < render->num_slices; ++i) ++ { ++ list[0] = xvba->m_xvbaBufferPool.data_buffer; ++ list[0]->data_offset = 0; ++ list[1] = xvba->m_xvbaBufferPool.data_control_buffers[i]; ++ list[1]->data_size_in_buffer = sizeof(*dataControl); ++ { CSingleLock lock(xvba->m_apiSec); ++ if (Success != g_XVBA_vtable.DecodePicture(&picInput)) ++ { ++ xvba->SetError(__FUNCTION__, "failed to decode picture 2", __LINE__); ++ return; ++ } ++ } ++ } ++ XVBA_Decode_Picture_End_Input endInput; ++ endInput.size = sizeof(endInput); ++ endInput.session = xvba->m_xvbaConfig.xvbaSession; ++ { CSingleLock lock(xvba->m_apiSec); ++ if (Success != g_XVBA_vtable.EndDecodePicture(&endInput)) ++ { ++ xvba->SetError(__FUNCTION__, "failed to decode picture 3", __LINE__); ++ return; ++ } ++ } ++ ++ // decode sync and error ++ XVBA_Surface_Sync_Input syncInput; ++ XVBA_Surface_Sync_Output syncOutput; ++ syncInput.size = sizeof(syncInput); ++ syncInput.session = xvba->m_xvbaConfig.xvbaSession; ++ syncInput.surface = render->surface; ++ syncInput.query_status = XVBA_GET_SURFACE_STATUS; ++ syncOutput.size = sizeof(syncOutput); ++ int64_t start = CurrentHostCounter(); ++ while (1) ++ { ++ { CSingleLock lock(xvba->m_apiSec); ++ if (Success != g_XVBA_vtable.SyncSurface(&syncInput, &syncOutput)) ++ { ++ xvba->SetError(__FUNCTION__, "failed sync surface 1", __LINE__); ++ return; ++ } ++ } ++ if (!(syncOutput.status_flags & XVBA_STILL_PENDING)) ++ break; ++ if (CurrentHostCounter() - start > CurrentHostFrequency()) ++ { ++ xvba->SetError(__FUNCTION__, "timed out waiting for surface", __LINE__); ++ break; ++ } ++ usleep(100); ++ } ++ render->state |= FF_XVBA_STATE_DECODED; ++} ++ ++int CDecoder::FFGetBuffer(AVCodecContext *avctx, AVFrame *pic) ++{ ++ CDVDVideoCodecFFmpeg* ctx = (CDVDVideoCodecFFmpeg*)avctx->opaque; ++ CDecoder* xvba = (CDecoder*)ctx->GetHardware(); ++ ++ pic->data[0] = ++ pic->data[1] = ++ pic->data[2] = ++ pic->data[3] = 0; ++ ++ pic->linesize[0] = ++ pic->linesize[1] = ++ pic->linesize[2] = ++ pic->linesize[3] = 0; ++ ++ CSingleLock lock(xvba->m_decoderSection); ++ ++ if(xvba->m_displayState != XVBA_OPEN) ++ return -1; ++ ++ if (xvba->m_xvbaConfig.xvbaSession == 0) ++ { ++ if (!xvba->CreateSession(avctx)) ++ return -1; ++ } ++ ++ xvba_render_state * render = NULL; ++ // find unused surface ++ { CSingleLock lock(xvba->m_videoSurfaceSec); ++ for(unsigned int i = 0; i < xvba->m_videoSurfaces.size(); ++i) ++ { ++ if(!(xvba->m_videoSurfaces[i]->state & (FF_XVBA_STATE_USED_FOR_REFERENCE | FF_XVBA_STATE_USED_FOR_RENDER))) ++ { ++ render = xvba->m_videoSurfaces[i]; ++ render->state = 0; ++ break; ++ } ++ } ++ } ++ ++ // create a new render state ++ if (render == NULL) ++ { ++ render = (xvba_render_state*)calloc(sizeof(xvba_render_state), 1); ++ if (render == NULL) ++ { ++ CLog::Log(LOGERROR, "XVBA::FFGetBuffer - calloc failed"); ++ return -1; ++ } ++ render->surface = 0; ++ render->buffers_alllocated = 0; ++ CSingleLock lock(xvba->m_videoSurfaceSec); ++ xvba->m_videoSurfaces.push_back(render); ++ } ++ ++ // create a new surface ++ if (render->surface == 0) ++ { ++ XVBA_Create_Surface_Input surfaceInput; ++ XVBA_Create_Surface_Output surfaceOutput; ++ surfaceInput.size = sizeof(surfaceInput); ++ surfaceInput.surface_type = xvba->m_xvbaConfig.decoderCap.surface_type; ++ surfaceInput.width = xvba->m_xvbaConfig.surfaceWidth; ++ surfaceInput.height = xvba->m_xvbaConfig.surfaceHeight; ++ surfaceInput.session = xvba->m_xvbaConfig.xvbaSession; ++ surfaceOutput.size = sizeof(surfaceOutput); ++ { CSingleLock lock(xvba->m_apiSec); ++ if (Success != g_XVBA_vtable.CreateSurface(&surfaceInput, &surfaceOutput)) ++ { ++ xvba->SetError(__FUNCTION__, "failed to create video surface", __LINE__); ++ return -1; ++ } ++ } ++ render->surface = surfaceOutput.surface; ++ render->picture_descriptor = (XVBAPictureDescriptor *)xvba->m_xvbaBufferPool.picture_descriptor_buffer->bufferXVBA; ++ render->iq_matrix = (XVBAQuantMatrixAvc *)xvba->m_xvbaBufferPool.iq_matrix_buffer->bufferXVBA; ++ CLog::Log(LOGDEBUG, "XVBA::FFGetBuffer - created video surface"); ++ } ++ ++ if (render == NULL) ++ return -1; ++ ++ pic->data[0] = (uint8_t*)render; ++ ++ pic->type= FF_BUFFER_TYPE_USER; ++ ++ render->state |= FF_XVBA_STATE_USED_FOR_REFERENCE; ++ render->state &= ~FF_XVBA_STATE_DECODED; ++ pic->reordered_opaque= avctx->reordered_opaque; ++ ++ return 0; ++} ++ ++int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame) ++{ ++ int result = Check(avctx); ++ if (result) ++ return result; ++ ++ CSingleLock lock(m_decoderSection); ++ ++ if(frame) ++ { // we have a new frame from decoder ++ ++ xvba_render_state * render = (xvba_render_state*)frame->data[0]; ++ if(!render) ++ { ++ CLog::Log(LOGERROR, "XVBA::Decode - no render buffer"); ++ return VC_ERROR; ++ } ++ ++ // ffmpeg vc-1 decoder does not flush, make sure the data buffer is still valid ++ if (!IsSurfaceValid(render)) ++ { ++ CLog::Log(LOGWARNING, "XVBA::Decode - ignoring invalid buffer"); ++ return VC_BUFFER; ++ } ++ if (!(render->state & FF_XVBA_STATE_DECODED)) ++ { ++ CLog::Log(LOGDEBUG, "XVBA::Decode - ffmpeg failed"); ++ return VC_BUFFER; ++ } ++ ++ CSingleLock lock(m_videoSurfaceSec); ++ render->state |= FF_XVBA_STATE_USED_FOR_RENDER; ++ lock.Leave(); ++ ++ // send frame to output for processing ++ CXvbaDecodedPicture pic; ++ memset(&pic.DVDPic, 0, sizeof(pic.DVDPic)); ++ ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&pic.DVDPic); ++ pic.render = render; ++ m_bufferStats.IncDecoded(); ++ m_xvbaOutput.m_dataPort.SendOutMessage(COutputDataProtocol::NEWFRAME, &pic, sizeof(pic)); ++ ++ m_codecControl = pic.DVDPic.iFlags & (DVP_FLAG_DRAIN | DVP_FLAG_NO_POSTPROC); ++ } ++ ++ int retval = 0; ++ uint16_t decoded, processed, render; ++ Message *msg; ++ while (m_xvbaOutput.m_controlPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputControlProtocol::ERROR) ++ { ++ m_displayState = XVBA_ERROR; ++ retval |= VC_ERROR; ++ } ++ msg->Release(); ++ } ++ ++ m_bufferStats.Get(decoded, processed, render); ++ ++ uint64_t startTime = CurrentHostCounter(); ++ while (!retval) ++ { ++ if (m_xvbaOutput.m_dataPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputDataProtocol::PICTURE) ++ { ++ if (m_presentPicture) ++ { ++ m_presentPicture->ReturnUnused(); ++ m_presentPicture = 0; ++ } ++ ++ m_presentPicture = *(CXvbaRenderPicture**)msg->data; ++ m_presentPicture->xvba = this; ++ m_bufferStats.DecRender(); ++ m_bufferStats.Get(decoded, processed, render); ++ retval |= VC_PICTURE; ++ } ++ msg->Release(); ++ } ++ else if (m_xvbaOutput.m_controlPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputControlProtocol::STATS) ++ { ++ m_bufferStats.Get(decoded, processed, render); ++ } ++ else ++ { ++ m_displayState = XVBA_ERROR; ++ retval |= VC_ERROR; ++ } ++ msg->Release(); ++ } ++ ++ if ((m_codecControl & DVP_FLAG_DRAIN)) ++ { ++ if (decoded + processed + render < 2) ++ { ++ retval |= VC_BUFFER; ++ } ++ } ++ else ++ { ++ if (decoded + processed + render < 4) ++ { ++ retval |= VC_BUFFER; ++ } ++ } ++ ++ if (!retval && !m_inMsgEvent.WaitMSec(2000)) ++ break; ++ } ++ uint64_t diff = CurrentHostCounter() - startTime; ++ if (retval & VC_PICTURE) ++ { ++ m_bufferStats.SetParams(diff, m_speed); ++ if (diff*1000/CurrentHostFrequency() > 50) ++ CLog::Log(LOGDEBUG,"XVBA::Decode long wait: %d", (int)((diff*1000)/CurrentHostFrequency())); ++ } ++ ++ if (!retval) ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - timed out waiting for output message", __FUNCTION__); ++ m_displayState = XVBA_ERROR; ++ retval |= VC_ERROR; ++ } ++ ++ return retval; ++ ++} ++ ++bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) ++{ ++ CSingleLock lock(m_decoderSection); ++ ++ if (m_displayState != XVBA_OPEN) ++ return false; ++ ++ *picture = m_presentPicture->DVDPic; ++ picture->xvba = m_presentPicture; ++ ++ return true; ++} ++ ++void CDecoder::ReturnRenderPicture(CXvbaRenderPicture *renderPic) ++{ ++ m_xvbaOutput.m_dataPort.SendOutMessage(COutputDataProtocol::RETURNPIC, &renderPic, sizeof(renderPic)); ++} ++ ++ ++//void CDecoder::CopyYV12(int index, uint8_t *dest) ++//{ ++// CSharedLock lock(m_decoderSection); ++// ++// { CSharedLock dLock(m_displaySection); ++// if(m_displayState != XVBA_OPEN) ++// return; ++// } ++// ++// if (!m_flipBuffer[index].outPic) ++// { ++// CLog::Log(LOGWARNING, "XVBA::Present: present picture is NULL"); ++// return; ++// } ++// ++// XVBA_GetSurface_Target target; ++// target.size = sizeof(target); ++// target.surfaceType = XVBA_YV12; ++// target.flag = XVBA_FRAME; ++// ++// XVBA_Get_Surface_Input input; ++// input.size = sizeof(input); ++// input.session = m_xvbaSession; ++// input.src_surface = m_flipBuffer[index].outPic->render->surface; ++// input.target_buffer = dest; ++// input.target_pitch = m_surfaceWidth; ++// input.target_width = m_surfaceWidth; ++// input.target_height = m_surfaceHeight; ++// input.target_parameter = target; ++// { CSingleLock lock(m_apiSec); ++// if (Success != g_XVBA_vtable.GetSurface(&input)) ++// { ++// CLog::Log(LOGERROR,"(XVBA::CopyYV12) failed to get surface"); ++// } ++// } ++//} ++ ++void CDecoder::Reset() ++{ ++ CSingleLock lock(m_decoderSection); ++ ++ if (!m_xvbaConfig.xvbaSession) ++ return; ++ ++ Message *reply; ++ if (m_xvbaOutput.m_controlPort.SendOutMessageSync(COutputControlProtocol::FLUSH, ++ &reply, ++ 2000)) ++ { ++ bool success = reply->signal == COutputControlProtocol::ACC ? true : false; ++ reply->Release(); ++ if (!success) ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - flush returned error", __FUNCTION__); ++ m_displayState = XVBA_ERROR; ++ } ++ else ++ m_bufferStats.Reset(); ++ } ++ else ++ { ++ CLog::Log(LOGERROR, "XVBA::%s - flush timed out", __FUNCTION__); ++ m_displayState = XVBA_ERROR; ++ } ++} ++ ++bool CDecoder::CanSkipDeint() ++{ ++ return m_bufferStats.CanSkipDeint(); ++} ++ ++void CDecoder::SetSpeed(int speed) ++{ ++ m_speed = speed; ++} ++ ++//----------------------------------------------------------------------------- ++// RenderPicture ++//----------------------------------------------------------------------------- ++ ++CXvbaRenderPicture* CXvbaRenderPicture::Acquire() ++{ ++ CSingleLock lock(*renderPicSection); ++ ++ if (refCount == 0) ++ xvba->Acquire(); ++ ++ refCount++; ++ return this; ++} ++ ++long CXvbaRenderPicture::Release() ++{ ++ CSingleLock lock(*renderPicSection); ++ ++ refCount--; ++ if (refCount > 0) ++ return refCount; ++ ++ lock.Leave(); ++ xvba->ReturnRenderPicture(this); ++ xvba->ReleasePicReference(); ++ ++ return refCount; ++} ++ ++void CXvbaRenderPicture::Transfer() ++{ ++ CSingleLock lock(*renderPicSection); ++ ++ if (valid) ++ xvbaOutput->TransferSurface(sourceIdx); ++} ++ ++void CXvbaRenderPicture::ReturnUnused() ++{ ++ { CSingleLock lock(*renderPicSection); ++ if (refCount > 0) ++ return; ++ } ++ if (xvba) ++ xvba->ReturnRenderPicture(this); ++} ++ ++//----------------------------------------------------------------------------- ++// Output ++//----------------------------------------------------------------------------- ++COutput::COutput(CEvent *inMsgEvent) : ++ CThread("XVBA Output Thread"), ++ m_controlPort("OutputControlPort", inMsgEvent, &m_outMsgEvent), ++ m_dataPort("OutputDataPort", inMsgEvent, &m_outMsgEvent) ++{ ++ m_inMsgEvent = inMsgEvent; ++ ++ CXvbaRenderPicture pic; ++ pic.renderPicSection = &m_bufferPool.renderPicSec; ++ pic.refCount = 0; ++ for (unsigned int i = 0; i < NUM_RENDER_PICS; i++) ++ { ++ m_bufferPool.allRenderPics.push_back(pic); ++ } ++ for (unsigned int i = 0; i < m_bufferPool.allRenderPics.size(); ++i) ++ { ++ m_bufferPool.freeRenderPics.push_back(&m_bufferPool.allRenderPics[i]); ++ } ++} ++ ++void COutput::Start() ++{ ++ Create(); ++} ++ ++COutput::~COutput() ++{ ++ Dispose(); ++ ++ m_bufferPool.freeRenderPics.clear(); ++ m_bufferPool.usedRenderPics.clear(); ++ m_bufferPool.allRenderPics.clear(); ++} ++ ++void COutput::Dispose() ++{ ++ CSingleLock lock(g_graphicsContext); ++ m_bStop = true; ++ m_outMsgEvent.Set(); ++ StopThread(); ++ m_controlPort.Purge(); ++ m_dataPort.Purge(); ++} ++ ++void COutput::OnStartup() ++{ ++ CLog::Log(LOGNOTICE, "COutput::OnStartup: Output Thread created"); ++} ++ ++void COutput::OnExit() ++{ ++ CLog::Log(LOGNOTICE, "COutput::OnExit: Output Thread terminated"); ++} ++ ++enum OUTPUT_STATES ++{ ++ O_TOP = 0, // 0 ++ O_TOP_ERROR, // 1 ++ O_TOP_UNCONFIGURED, // 2 ++ O_TOP_CONFIGURED, // 3 ++ O_TOP_CONFIGURED_WAIT_RES1, // 4 ++ O_TOP_CONFIGURED_WAIT_DEC, // 5 ++ O_TOP_CONFIGURED_STEP1, // 6 ++ O_TOP_CONFIGURED_WAIT_RES2, // 7 ++ O_TOP_CONFIGURED_STEP2, // 8 ++}; ++ ++int OUTPUT_parentStates[] = { ++ -1, ++ 0, //TOP_ERROR ++ 0, //TOP_UNCONFIGURED ++ 0, //TOP_CONFIGURED ++ 3, //TOP_CONFIGURED_WAIT_RES1 ++ 3, //TOP_CONFIGURED_WAIT_DEC ++ 3, //TOP_CONFIGURED_STEP1 ++ 3, //TOP_CONFIGURED_WAIT_RES2 ++ 3, //TOP_CONFIGURED_STEP2 ++}; ++ ++void COutput::StateMachine(int signal, Protocol *port, Message *msg) ++{ ++ for (int state = m_state; ; state = OUTPUT_parentStates[state]) ++ { ++ switch (state) ++ { ++ case O_TOP: // TOP ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::FLUSH: ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ case COutputControlProtocol::PRECLEANUP: ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ default: ++ break; ++ } ++ } ++ else if (port == &m_dataPort) ++ { ++ switch (signal) ++ { ++ case COutputDataProtocol::RETURNPIC: ++ CXvbaRenderPicture *pic; ++ pic = *((CXvbaRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ return; ++ default: ++ break; ++ } ++ } ++ { ++ std::string portName = port == NULL ? "timer" : port->portName; ++ CLog::Log(LOGWARNING, "COutput::%s - signal: %d form port: %s not handled for state: %d", __FUNCTION__, signal, portName.c_str(), m_state); ++ } ++ return; ++ ++ case O_TOP_ERROR: ++ m_extTimeout = 1000; ++ break; ++ ++ case O_TOP_UNCONFIGURED: ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::INIT: ++ CXvbaConfig *data; ++ data = (CXvbaConfig*)msg->data; ++ if (data) ++ { ++ m_config = *data; ++ } ++ Init(); ++ EnsureBufferPool(); ++ if (!m_xvbaError) ++ { ++ m_state = O_TOP_CONFIGURED_WAIT_RES1; ++ msg->Reply(COutputControlProtocol::ACC); ++ } ++ else ++ { ++ m_state = O_TOP_ERROR; ++ msg->Reply(COutputControlProtocol::ERROR); ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED: ++ if (port == &m_controlPort) ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::FLUSH: ++ m_state = O_TOP_CONFIGURED_WAIT_RES1; ++ Flush(); ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ case COutputControlProtocol::PRECLEANUP: ++ m_state = O_TOP_CONFIGURED_WAIT_RES1; ++ Flush(); ++ PreReleaseBufferPool(); ++ msg->Reply(COutputControlProtocol::ACC); ++ return; ++ default: ++ break; ++ } ++ } ++ else if (port == &m_dataPort) ++ { ++ switch (signal) ++ { ++ case COutputDataProtocol::NEWFRAME: ++ CXvbaDecodedPicture *frame; ++ frame = (CXvbaDecodedPicture*)msg->data; ++ if (frame) ++ { ++ m_decodedPics.push(*frame); ++ m_extTimeout = 0; ++ } ++ return; ++ case COutputDataProtocol::RETURNPIC: ++ CXvbaRenderPicture *pic; ++ pic = *((CXvbaRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ m_controlPort.SendInMessage(COutputControlProtocol::STATS); ++ m_extTimeout = 0; ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_WAIT_RES1: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++ if (!m_decodedPics.empty() && FindFreeSurface() >= 0 && !m_bufferPool.freeRenderPics.empty()) ++ { ++ m_state = O_TOP_CONFIGURED_WAIT_DEC; ++ m_bStateMachineSelfTrigger = true; ++ } ++ else ++ { ++ if (m_extTimeout != 0) ++ { ++ uint16_t decoded, processed, render; ++ m_config.stats->Get(decoded, processed, render); ++// CLog::Log(LOGDEBUG, "CVDPAU::COutput - timeout idle: decoded: %d, proc: %d, render: %d", decoded, processed, render); ++ } ++ m_extTimeout = 100; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_WAIT_DEC: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++ if (IsDecodingFinished()) ++ { ++ m_state = O_TOP_CONFIGURED_STEP1; ++ m_bStateMachineSelfTrigger = true; ++ } ++ else ++ { ++ m_extTimeout = 1; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_STEP1: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++ m_processPicture = m_decodedPics.front(); ++ m_decodedPics.pop(); ++ InitCycle(); ++ CXvbaRenderPicture *pic; ++ pic = ProcessPicture(); ++ if (pic) ++ { ++ m_config.stats->IncRender(); ++ m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic)); ++ } ++ if (m_xvbaError) ++ { ++ m_state = O_TOP_ERROR; ++ return; ++ } ++ if (m_deinterlacing && !m_deintSkip) ++ { ++ m_state = O_TOP_CONFIGURED_WAIT_RES2; ++ m_extTimeout = 0; ++ } ++ else ++ { ++ FiniCycle(); ++ m_state = O_TOP_CONFIGURED_WAIT_RES1; ++ m_extTimeout = 0; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_WAIT_RES2: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++ if (FindFreeSurface() >= 0 && !m_bufferPool.freeRenderPics.empty()) ++ { ++ m_state = O_TOP_CONFIGURED_STEP2; ++ m_bStateMachineSelfTrigger = true; ++ } ++ else ++ { ++ if (m_extTimeout != 0) ++ { ++ uint16_t decoded, processed, render; ++ m_config.stats->Get(decoded, processed, render); ++ CLog::Log(LOGDEBUG, "CVDPAU::COutput - timeout idle: decoded: %d, proc: %d, render: %d", decoded, processed, render); ++ } ++ m_extTimeout = 100; ++ } ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ case O_TOP_CONFIGURED_STEP2: ++ if (port == NULL) // timeout ++ { ++ switch (signal) ++ { ++ case COutputControlProtocol::TIMEOUT: ++ CXvbaRenderPicture *pic; ++ m_deintStep = 1; ++ pic = ProcessPicture(); ++ if (pic) ++ { ++ m_config.stats->IncRender(); ++ m_dataPort.SendInMessage(COutputDataProtocol::PICTURE, &pic, sizeof(pic)); ++ } ++ if (m_xvbaError) ++ { ++ m_state = O_TOP_ERROR; ++ return; ++ } ++ FiniCycle(); ++ m_state = O_TOP_CONFIGURED_WAIT_RES1; ++ m_extTimeout = 0; ++ return; ++ default: ++ break; ++ } ++ } ++ break; ++ ++ default: // we are in no state, should not happen ++ CLog::Log(LOGERROR, "COutput::%s - no valid state: %d", __FUNCTION__, m_state); ++ return; ++ } ++ } // for ++} ++ ++void COutput::Process() ++{ ++ Message *msg; ++ Protocol *port = NULL; ++ bool gotMsg; ++ ++ m_state = O_TOP_UNCONFIGURED; ++ m_extTimeout = 1000; ++ m_bStateMachineSelfTrigger = false; ++ ++ while (!m_bStop) ++ { ++ gotMsg = false; ++ ++ if (m_bStateMachineSelfTrigger) ++ { ++ m_bStateMachineSelfTrigger = false; ++ // self trigger state machine ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ continue; ++ } ++ // check control port ++ else if (m_controlPort.ReceiveOutMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_controlPort; ++ } ++ // check data port ++ else if (m_dataPort.ReceiveOutMessage(&msg)) ++ { ++ gotMsg = true; ++ port = &m_dataPort; ++ } ++ if (gotMsg) ++ { ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ continue; ++ } ++ ++ // wait for message ++ else if (m_outMsgEvent.WaitMSec(m_extTimeout)) ++ { ++ continue; ++ } ++ // time out ++ else ++ { ++ msg = m_controlPort.GetMessage(); ++ msg->signal = COutputControlProtocol::TIMEOUT; ++ port = 0; ++ // signal timeout to state machine ++ StateMachine(msg->signal, port, msg); ++ if (!m_bStateMachineSelfTrigger) ++ { ++ msg->Release(); ++ msg = NULL; ++ } ++ } ++ } ++ Flush(); ++ Uninit(); ++} ++ ++bool COutput::Init() ++{ ++ if (!CreateGlxContext()) ++ return false; ++ ++ m_xvbaError = false; ++ m_processPicture.render = 0; ++ ++ return true; ++} ++ ++bool COutput::Uninit() ++{ ++ ReleaseBufferPool(); ++ DestroyGlxContext(); ++ return true; ++} ++ ++void COutput::Flush() ++{ ++ while (!m_decodedPics.empty()) ++ { ++ CXvbaDecodedPicture pic = m_decodedPics.front(); ++ m_decodedPics.pop(); ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~(FF_XVBA_STATE_USED_FOR_RENDER | FF_XVBA_STATE_DECODED); ++ } ++ ++ if (m_processPicture.render) ++ { ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ m_processPicture.render->state &= ~(FF_XVBA_STATE_USED_FOR_RENDER | FF_XVBA_STATE_DECODED); ++ m_processPicture.render = 0; ++ } ++ ++ Message *msg; ++ while (m_dataPort.ReceiveOutMessage(&msg)) ++ { ++ if (msg->signal == COutputDataProtocol::NEWFRAME) ++ { ++ CXvbaDecodedPicture pic = *(CXvbaDecodedPicture*)msg->data; ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ if (pic.render) ++ pic.render->state &= ~(FF_XVBA_STATE_USED_FOR_RENDER | FF_XVBA_STATE_DECODED); ++ } ++ else if (msg->signal == COutputDataProtocol::RETURNPIC) ++ { ++ CXvbaRenderPicture *pic; ++ pic = *((CXvbaRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ } ++ msg->Release(); ++ } ++ ++ while (m_dataPort.ReceiveInMessage(&msg)) ++ { ++ if (msg->signal == COutputDataProtocol::PICTURE) ++ { ++ CXvbaRenderPicture *pic; ++ pic = *((CXvbaRenderPicture**)msg->data); ++ ProcessReturnPicture(pic); ++ } ++ } ++} ++ ++bool COutput::IsDecodingFinished() ++{ ++ return true; ++ ++ // check for decoding to be finished ++ CXvbaDecodedPicture decodedPic = m_decodedPics.front(); ++ ++ XVBA_Surface_Sync_Input syncInput; ++ XVBA_Surface_Sync_Output syncOutput; ++ syncInput.size = sizeof(syncInput); ++ syncInput.session = m_config.xvbaSession; ++ syncInput.surface = decodedPic.render->surface; ++ syncInput.query_status = XVBA_GET_SURFACE_STATUS; ++ syncOutput.size = sizeof(syncOutput); ++ { CSingleLock lock(*(m_config.apiSec)); ++ if (Success != g_XVBA_vtable.SyncSurface(&syncInput, &syncOutput)) ++ { ++ CLog::Log(LOGERROR,"XVBA - failed sync surface"); ++ m_xvbaError = true; ++ return false; ++ } ++ } ++ if (!(syncOutput.status_flags & XVBA_STILL_PENDING)) ++ return true; ++ ++ return false; ++} ++ ++void COutput::TransferSurface(uint32_t source) ++{ ++ XvbaBufferPool::GLVideoSurface *glSurface = &m_bufferPool.glSurfaces[source]; ++ ++ if (glSurface->transferred) ++ return; ++ ++ glSurface->transferred = true; ++ ++ // transfer surface ++ XVBA_Transfer_Surface_Input transInput; ++ transInput.size = sizeof(transInput); ++ transInput.session = m_config.xvbaSession; ++ transInput.src_surface = glSurface->render->surface; ++ transInput.target_surface = glSurface->glSurface; ++ transInput.flag = glSurface->field; ++ { CSingleLock lock(*(m_config.apiSec)); ++ if (Success != g_XVBA_vtable.TransferSurface(&transInput)) ++ { ++ CLog::Log(LOGERROR,"(XVBA) failed to transfer surface"); ++ m_xvbaError = true; ++ return; ++ } ++ } ++} ++ ++CXvbaRenderPicture* COutput::ProcessPicture() ++{ ++ CXvbaRenderPicture *retPic = 0; ++ ++ if (m_deintStep == 1) ++ { ++ if(m_field == XVBA_TOP_FIELD) ++ m_field = XVBA_BOTTOM_FIELD; ++ else ++ m_field = XVBA_TOP_FIELD; ++ } ++ ++ // find unused shared surface ++ unsigned int idx = FindFreeSurface(); ++ XvbaBufferPool::GLVideoSurface *glSurface = &m_bufferPool.glSurfaces[idx]; ++ glSurface->used = true; ++ glSurface->field = m_field; ++ glSurface->render = m_processPicture.render; ++ glSurface->transferred = false; ++ ++ int cmd = 0; ++ m_config.stats->GetCmd(cmd); ++ ++// if (!(cmd & DVP_FLAG_SKIP_PROC)) ++// { ++ // transfer surface ++ XVBA_Transfer_Surface_Input transInput; ++ transInput.size = sizeof(transInput); ++ transInput.session = m_config.xvbaSession; ++ transInput.src_surface = m_processPicture.render->surface; ++ transInput.target_surface = glSurface->glSurface; ++ transInput.flag = m_field; ++ { CSingleLock lock(*(m_config.apiSec)); ++ if (Success != g_XVBA_vtable.TransferSurface(&transInput)) ++ { ++ CLog::Log(LOGERROR,"(XVBA) failed to transfer surface"); ++ m_xvbaError = true; ++ return retPic; ++ } ++ } ++ ++ // make sure that transfer is completed ++// uint64_t maxTimeout = 1000000000LL; ++// GLsync ReadyFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); ++// glClientWaitSync(ReadyFence, GL_SYNC_FLUSH_COMMANDS_BIT, maxTimeout); ++// glDeleteSync(ReadyFence); ++// glFinish();GL_SYNC_FLUSH_COMMANDS_BIT ++// } ++// else ++// { ++// CLog::Log(LOGDEBUG,"XVBA::ProcessPicture - skipped transfer surface"); ++// m_processPicture.DVDPic.iFlags |= DVP_FLAG_DROPPED; ++// } ++ ++ // prepare render pic ++ retPic = m_bufferPool.freeRenderPics.front(); ++ m_bufferPool.freeRenderPics.pop_front(); ++ m_bufferPool.usedRenderPics.push_back(retPic); ++ retPic->sourceIdx = glSurface->id; ++ retPic->DVDPic = m_processPicture.DVDPic; ++ retPic->valid = true; ++ retPic->texture = glSurface->texture; ++ retPic->crop = CRect(0,0,0,0); ++ retPic->texWidth = m_config.surfaceWidth; ++ retPic->texHeight = m_config.surfaceHeight; ++ retPic->xvbaOutput = this; ++ ++ // set repeat pic for de-interlacing ++ if (m_deinterlacing) ++ { ++ if (m_deintStep == 1) ++ { ++ retPic->DVDPic.pts = DVD_NOPTS_VALUE; ++ retPic->DVDPic.dts = DVD_NOPTS_VALUE; ++ } ++ retPic->DVDPic.iRepeatPicture = 0.0; ++ } ++ ++ return retPic; ++} ++ ++void COutput::ProcessReturnPicture(CXvbaRenderPicture *pic) ++{ ++ std::deque::iterator it; ++ it = std::find(m_bufferPool.usedRenderPics.begin(), m_bufferPool.usedRenderPics.end(), pic); ++ if (it == m_bufferPool.usedRenderPics.end()) ++ { ++ CLog::Log(LOGWARNING, "COutput::ProcessReturnPicture - pic not found"); ++ return; ++ } ++ m_bufferPool.usedRenderPics.erase(it); ++ m_bufferPool.freeRenderPics.push_back(pic); ++ if (!pic->valid) ++ { ++ CLog::Log(LOGDEBUG, "COutput::%s - return of invalid render pic", __FUNCTION__); ++ return; ++ } ++ ++ if (m_config.useSharedSurfaces) ++ { ++ xvba_render_state *render = m_bufferPool.glSurfaces[pic->sourceIdx].render; ++ if (render) ++ { ++ // check if video surface if referenced by other glSurfaces ++ bool referenced(false); ++ for (unsigned int i=0; isourceIdx) ++ continue; ++ if (m_bufferPool.glSurfaces[i].render == render) ++ { ++ referenced = true; ++ break; ++ } ++ } ++ if (m_processPicture.render == render) ++ referenced = true; ++ ++ // release video surface ++ if (!referenced) ++ { ++ CSingleLock lock(*m_config.videoSurfaceSec); ++ render->state &= ~(FF_XVBA_STATE_USED_FOR_RENDER | FF_XVBA_STATE_DECODED); ++ } ++ ++ // unreference video surface ++ m_bufferPool.glSurfaces[pic->sourceIdx].render = 0; ++ } ++ m_bufferPool.glSurfaces[pic->sourceIdx].used = false; ++ return; ++ } ++} ++ ++int COutput::FindFreeSurface() ++{ ++ // find free shared surface ++ unsigned int i; ++ for (i = 0; i < m_bufferPool.glSurfaces.size(); ++i) ++ { ++ if (!m_bufferPool.glSurfaces[i].used) ++ break; ++ } ++ if (i == m_bufferPool.glSurfaces.size()) ++ return -1; ++ else ++ return i; ++} ++ ++void COutput::InitCycle() ++{ ++ uint64_t latency; ++ int speed; ++ m_config.stats->GetParams(latency, speed); ++ latency = (latency*1000)/CurrentHostFrequency(); ++ ++ m_config.stats->SetCanSkipDeint(false); ++ ++ EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; ++ EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; ++ bool interlaced = m_processPicture.DVDPic.iFlags & DVP_FLAG_INTERLACED; ++ ++ if (mode == VS_DEINTERLACEMODE_FORCE || ++ (mode == VS_DEINTERLACEMODE_AUTO && interlaced)) ++ { ++ if((method == VS_INTERLACEMETHOD_AUTO && interlaced) ++ || method == VS_INTERLACEMETHOD_XVBA) ++ { ++ m_deinterlacing = true; ++ m_deintSkip = false; ++ m_config.stats->SetCanSkipDeint(true); ++ ++ if (m_processPicture.DVDPic.iFlags & DVP_FLAG_DROPDEINT) ++ { ++ m_deintSkip = true; ++ } ++ ++ // do only half deinterlacing ++ if (speed != DVD_PLAYSPEED_NORMAL || !g_graphicsContext.IsFullScreenVideo()) ++ { ++ m_config.stats->SetCanSkipDeint(false); ++ m_deintSkip = true; ++ } ++ ++ if(m_processPicture.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST) ++ m_field = XVBA_TOP_FIELD; ++ else ++ m_field = XVBA_BOTTOM_FIELD; ++ } ++ } ++ else ++ { ++ m_deinterlacing = false; ++ m_field = XVBA_FRAME; ++ } ++ ++ m_processPicture.DVDPic.format = RENDER_FMT_XVBA; ++ m_processPicture.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST | ++ DVP_FLAG_REPEAT_TOP_FIELD | ++ DVP_FLAG_INTERLACED); ++ m_processPicture.DVDPic.iWidth = m_config.vidWidth; ++ m_processPicture.DVDPic.iHeight = m_config.vidHeight; ++ ++ m_deintStep = 0; ++} ++ ++void COutput::FiniCycle() ++{ ++// { CSingleLock lock(*m_config.videoSurfaceSec); ++// m_processPicture.render->state &= ~FF_XVBA_STATE_USED_FOR_RENDER; ++// } ++ m_processPicture.render = 0; ++ m_config.stats->DecDecoded(); ++} ++ ++bool COutput::EnsureBufferPool() ++{ ++ if (m_config.useSharedSurfaces && m_bufferPool.glSurfaces.empty()) ++ { ++ GLenum textureTarget; ++ if (!glewIsSupported("GL_ARB_texture_non_power_of_two") && glewIsSupported("GL_ARB_texture_rectangle")) ++ { ++ textureTarget = GL_TEXTURE_RECTANGLE_ARB; ++ } ++ else ++ textureTarget = GL_TEXTURE_2D; ++ ++ // create shared surfaces ++ XvbaBufferPool::GLVideoSurface surface; ++ for (unsigned int i = 0; i < NUM_RENDER_PICS; ++i) ++ { ++ glEnable(textureTarget); ++ glGenTextures(1, &surface.texture); ++ glBindTexture(textureTarget, surface.texture); ++ glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ++ glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ++ glPixelStorei(GL_UNPACK_ALIGNMENT, 4); ++ glTexImage2D(textureTarget, 0, GL_RGBA, m_config.surfaceWidth, m_config.surfaceHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); ++ ++ XVBA_Create_GLShared_Surface_Input surfInput; ++ XVBA_Create_GLShared_Surface_Output surfOutput; ++ surfInput.size = sizeof(surfInput); ++ surfInput.session = m_config.xvbaSession; ++ surfInput.gltexture = surface.texture; ++ surfInput.glcontext = m_glContext; ++ surfOutput.size = sizeof(surfOutput); ++ surfOutput.surface = 0; ++ if (Success != g_XVBA_vtable.CreateGLSharedSurface(&surfInput, &surfOutput)) ++ { ++ CLog::Log(LOGERROR,"(XVBA) failed to create shared surface"); ++ m_xvbaError = true; ++ break; ++ } ++ CLog::Log(LOGDEBUG, "XVBA::GetTexture - created shared surface"); ++ ++ surface.glSurface = surfOutput.surface; ++ surface.id = i; ++ surface.used = false; ++ surface.render = 0; ++ m_bufferPool.glSurfaces.push_back(surface); ++ } ++ glDisable(textureTarget); ++ } ++ ++ return true; ++} ++ ++void COutput::ReleaseBufferPool() ++{ ++ CSingleLock lock(m_bufferPool.renderPicSec); ++ ++ if (m_config.useSharedSurfaces) ++ { ++ for (unsigned int i = 0; i < m_bufferPool.glSurfaces.size(); ++i) ++ { ++ if (!m_bufferPool.glSurfaces[i].glSurface) ++ continue; ++ g_XVBA_vtable.DestroySurface(m_bufferPool.glSurfaces[i].glSurface); ++ glDeleteTextures(1, &m_bufferPool.glSurfaces[i].texture); ++ } ++ m_bufferPool.glSurfaces.clear(); ++ } ++ // invalidate all used render pictures ++ for (unsigned int i = 0; i < m_bufferPool.usedRenderPics.size(); ++i) ++ { ++ m_bufferPool.usedRenderPics[i]->valid = false; ++ unsigned int idx = m_bufferPool.usedRenderPics[i]->sourceIdx; ++ if (m_bufferPool.glSurfaces[idx].render) ++ { ++ { CSingleLock lock(*m_config.videoSurfaceSec); ++ m_bufferPool.glSurfaces[idx].render->state &= ~(FF_XVBA_STATE_USED_FOR_RENDER | FF_XVBA_STATE_DECODED); ++ m_bufferPool.glSurfaces[idx].render = 0; ++ } ++ } ++ } ++} ++ ++void COutput::PreReleaseBufferPool() ++{ ++ CSingleLock lock(m_bufferPool.renderPicSec); ++ ++ if (m_config.useSharedSurfaces) ++ { ++ for (unsigned int i = 0; i < m_bufferPool.glSurfaces.size(); ++i) ++ { ++ if (!m_bufferPool.glSurfaces[i].used) ++ { ++ g_XVBA_vtable.DestroySurface(m_bufferPool.glSurfaces[i].glSurface); ++ glDeleteTextures(1, &m_bufferPool.glSurfaces[i].texture); ++ m_bufferPool.glSurfaces[i].glSurface = 0; ++ m_bufferPool.glSurfaces[i].used = true; ++ } ++ } ++ } ++} ++ ++bool COutput::CreateGlxContext() ++{ ++ GLXContext glContext; ++ ++ m_Display = g_Windowing.GetDisplay(); ++ glContext = g_Windowing.GetGlxContext(); ++ m_Window = g_Windowing.GetWindow(); ++ ++ // Get our window attribs. ++ XWindowAttributes wndattribs; ++ XGetWindowAttributes(m_Display, m_Window, &wndattribs); ++ ++ // Get visual Info ++ XVisualInfo visInfo; ++ visInfo.visualid = wndattribs.visual->visualid; ++ int nvisuals = 0; ++ XVisualInfo* visuals = XGetVisualInfo(m_Display, VisualIDMask, &visInfo, &nvisuals); ++ if (nvisuals != 1) ++ { ++ CLog::Log(LOGERROR, "XVBA::COutput::CreateGlxContext - could not find visual"); ++ return false; ++ } ++ visInfo = visuals[0]; ++ XFree(visuals); ++ ++ m_pixmap = XCreatePixmap(m_Display, ++ m_Window, ++ 192, ++ 108, ++ visInfo.depth); ++ if (!m_pixmap) ++ { ++ CLog::Log(LOGERROR, "XVBA::COutput::CreateGlxContext - Unable to create XPixmap"); ++ return false; ++ } ++ ++ // create gl pixmap ++ m_glPixmap = glXCreateGLXPixmap(m_Display, &visInfo, m_pixmap); ++ ++ if (!m_glPixmap) ++ { ++ CLog::Log(LOGINFO, "XVBA::COutput::CreateGlxContext - Could not create glPixmap"); ++ return false; ++ } ++ ++ m_glContext = glXCreateContext(m_Display, &visInfo, glContext, True); ++ ++ if (!glXMakeCurrent(m_Display, m_glPixmap, m_glContext)) ++ { ++ CLog::Log(LOGINFO, "XVBA::COutput::CreateGlxContext - Could not make Pixmap current"); ++ return false; ++ } ++ ++ CLog::Log(LOGNOTICE, "XVBA::COutput::CreateGlxContext - created context"); ++ return true; ++} ++ ++bool COutput::DestroyGlxContext() ++{ ++ if (m_glContext) ++ { ++ glXMakeCurrent(m_Display, None, NULL); ++ glXDestroyContext(m_Display, m_glContext); ++ } ++ m_glContext = 0; ++ ++ if (m_glPixmap) ++ glXDestroyPixmap(m_Display, m_glPixmap); ++ m_glPixmap = 0; ++ ++ if (m_pixmap) ++ XFreePixmap(m_Display, m_pixmap); ++ m_pixmap = 0; ++ ++ return true; ++} ++ ++#endif +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.h 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.h 2012-10-03 03:02:11.418942090 +0200 +@@ -0,0 +1,383 @@ ++/* ++ * Copyright (C) 2005-2011 Team XBMC ++ * http://www.xbmc.org ++ * ++ * This Program 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, or (at your option) ++ * any later version. ++ * ++ * This Program 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 XBMC; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++#pragma once ++ ++#include "X11/Xlib.h" ++#include "amd/amdxvba.h" ++#include "DllAvCodec.h" ++#include "DVDCodecs/Video/DVDVideoCodecFFmpeg.h" ++#include "threads/Thread.h" ++#include "threads/CriticalSection.h" ++#include "threads/SharedSection.h" ++#include "threads/Event.h" ++#include "guilib/DispResource.h" ++#include "guilib/Geometry.h" ++#include "libavcodec/xvba.h" ++#include "utils/ActorProtocol.h" ++#include "settings/VideoSettings.h" ++#include ++#include ++#include ++ ++using namespace Actor; ++ ++ ++namespace XVBA ++{ ++ ++//----------------------------------------------------------------------------- ++// XVBA data structs ++//----------------------------------------------------------------------------- ++ ++class CDecoder; ++class CXVBAContext; ++class COutput; ++ ++#define NUM_RENDER_PICS 9 ++ ++/** ++ * Buffer statistics used to control number of frames in queue ++ */ ++ ++class CXvbaBufferStats ++{ ++public: ++ uint16_t decodedPics; ++ uint16_t processedPics; ++ uint16_t renderPics; ++ uint64_t latency; // time decoder has waited for a frame, ideally there is no latency ++ int playSpeed; ++ bool canSkipDeint; ++ int processCmd; ++ ++ void IncDecoded() { CSingleLock l(m_sec); decodedPics++;} ++ void DecDecoded() { CSingleLock l(m_sec); decodedPics--;} ++ void IncProcessed() { CSingleLock l(m_sec); processedPics++;} ++ void DecProcessed() { CSingleLock l(m_sec); processedPics--;} ++ void IncRender() { CSingleLock l(m_sec); renderPics++;} ++ void DecRender() { CSingleLock l(m_sec); renderPics--;} ++ void Reset() { CSingleLock l(m_sec); decodedPics=0; processedPics=0;renderPics=0;latency=0;} ++ void Get(uint16_t &decoded, uint16_t &processed, uint16_t &render) {CSingleLock l(m_sec); decoded = decodedPics, processed=processedPics, render=renderPics;} ++ void SetParams(uint64_t time, int speed) { CSingleLock l(m_sec); latency = time; playSpeed = speed; } ++ void GetParams(uint64_t &lat, int &speed) { CSingleLock l(m_sec); lat = latency; speed = playSpeed; } ++ void SetCmd(int cmd) { CSingleLock l(m_sec); processCmd = cmd; } ++ void GetCmd(int &cmd) { CSingleLock l(m_sec); cmd = processCmd; processCmd = 0; } ++ void SetCanSkipDeint(bool canSkip) { CSingleLock l(m_sec); canSkipDeint = canSkip; } ++ bool CanSkipDeint() { CSingleLock l(m_sec); if (canSkipDeint) return true; else return false;} ++private: ++ CCriticalSection m_sec; ++}; ++ ++/** ++ * CXvbaConfig holds all configuration parameters needed by vdpau ++ * The structure is sent to the internal classes CMixer and COutput ++ * for init. ++ */ ++ ++struct CXvbaConfig ++{ ++ int surfaceWidth; ++ int surfaceHeight; ++ int vidWidth; ++ int vidHeight; ++ int outWidth; ++ int outHeight; ++ bool useSharedSurfaces; ++ ++ CXVBAContext *context; ++ XVBADecodeCap decoderCap; ++ void *xvbaSession; ++ std::vector *videoSurfaces; ++ CCriticalSection *videoSurfaceSec; ++ CCriticalSection *apiSec; ++ ++ CXvbaBufferStats *stats; ++ int numRenderBuffers; ++ uint32_t maxReferences; ++}; ++ ++/** ++ * Holds a decoded frame ++ * Input to COutput for further processing ++ */ ++struct CXvbaDecodedPicture ++{ ++ DVDVideoPicture DVDPic; ++ xvba_render_state *render; ++}; ++ ++/** ++ * Ready to render textures ++ * Sent from COutput back to CDecoder ++ * Objects are referenced by DVDVideoPicture and are sent ++ * to renderer ++ */ ++class CXvbaRenderPicture ++{ ++ friend class CDecoder; ++ friend class COutput; ++public: ++ DVDVideoPicture DVDPic; ++ int texWidth, texHeight; ++ CRect crop; ++ GLuint texture; ++ uint32_t sourceIdx; ++ bool valid; ++ CDecoder *xvba; ++ CXvbaRenderPicture* Acquire(); ++ long Release(); ++ void Transfer(); ++private: ++ void ReturnUnused(); ++ int refCount; ++ CCriticalSection *renderPicSection; ++ COutput *xvbaOutput; ++}; ++ ++//----------------------------------------------------------------------------- ++// Output ++//----------------------------------------------------------------------------- ++ ++/** ++ * Buffer pool holds allocated xvba and gl resources ++ * Embedded in COutput ++ */ ++struct XvbaBufferPool ++{ ++ struct GLVideoSurface ++ { ++ unsigned int id; ++ bool used; ++ bool transferred; ++ GLuint texture; ++ void *glSurface; ++ xvba_render_state *render; ++ XVBA_SURFACE_FLAG field; ++ }; ++ std::vector glSurfaces; ++ std::vector allRenderPics; ++ std::deque usedRenderPics; ++ std::deque freeRenderPics; ++ CCriticalSection renderPicSec; ++}; ++ ++class COutputControlProtocol : public Protocol ++{ ++public: ++ COutputControlProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {}; ++ enum OutSignal ++ { ++ INIT, ++ FLUSH, ++ PRECLEANUP, ++ TIMEOUT, ++ }; ++ enum InSignal ++ { ++ ACC, ++ ERROR, ++ STATS, ++ }; ++}; ++ ++class COutputDataProtocol : public Protocol ++{ ++public: ++ COutputDataProtocol(std::string name, CEvent* inEvent, CEvent *outEvent) : Protocol(name, inEvent, outEvent) {}; ++ enum OutSignal ++ { ++ NEWFRAME = 0, ++ RETURNPIC, ++ }; ++ enum InSignal ++ { ++ PICTURE, ++ }; ++}; ++ ++/** ++ * COutput is embedded in CDecoder and embeds CMixer ++ * The class has its own OpenGl context which is shared with render thread ++ * COuput generated ready to render textures and passes them back to ++ * CDecoder ++ */ ++class COutput : private CThread ++{ ++public: ++ COutput(CEvent *inMsgEvent); ++ virtual ~COutput(); ++ void Start(); ++ void Dispose(); ++ void TransferSurface(uint32_t source); ++ COutputControlProtocol m_controlPort; ++ COutputDataProtocol m_dataPort; ++protected: ++ void OnStartup(); ++ void OnExit(); ++ void Process(); ++ void StateMachine(int signal, Protocol *port, Message *msg); ++ bool HasWork(); ++ bool IsDecodingFinished(); ++ CXvbaRenderPicture* ProcessPicture(); ++ void ProcessReturnPicture(CXvbaRenderPicture *pic); ++ int FindFreeSurface(); ++ void InitCycle(); ++ void FiniCycle(); ++ bool Init(); ++ bool Uninit(); ++ void Flush(); ++ bool CreateGlxContext(); ++ bool DestroyGlxContext(); ++ bool EnsureBufferPool(); ++ void ReleaseBufferPool(); ++ void PreReleaseBufferPool(); ++ CEvent m_outMsgEvent; ++ CEvent *m_inMsgEvent; ++ int m_state; ++ bool m_bStateMachineSelfTrigger; ++ ++ // extended state variables for state machine ++ int m_extTimeout; ++ bool m_xvbaError; ++ CXvbaConfig m_config; ++ XvbaBufferPool m_bufferPool; ++ Display *m_Display; ++ Window m_Window; ++ GLXContext m_glContext; ++ GLXWindow m_glWindow; ++ Pixmap m_pixmap; ++ GLXPixmap m_glPixmap; ++ std::queue m_decodedPics; ++ CXvbaDecodedPicture m_processPicture; ++ XVBA_SURFACE_FLAG m_field; ++ bool m_deinterlacing; ++ int m_deintStep; ++ bool m_deintSkip; ++}; ++ ++//----------------------------------------------------------------------------- ++// XVBA decoder ++//----------------------------------------------------------------------------- ++ ++class CXVBAContext ++{ ++public: ++ static bool EnsureContext(CXVBAContext **ctx); ++ void *GetContext(); ++ void Release(); ++private: ++ CXVBAContext(); ++ void Close(); ++ bool LoadSymbols(); ++ bool CreateContext(); ++ void DestroyContext(); ++ static CXVBAContext *m_context; ++ static CCriticalSection m_section; ++ static Display *m_display; ++ int m_refCount; ++ static void *m_dlHandle; ++ void *m_xvbaContext; ++}; ++ ++class CDecoder : public CDVDVideoCodecFFmpeg::IHardwareDecoder, ++ public IDispResource ++{ ++ friend class CXvbaRenderPicture; ++ ++public: ++ ++ struct pictureAge ++ { ++ int b_age; ++ int ip_age[2]; ++ }; ++ ++ enum EDisplayState ++ { XVBA_OPEN ++ , XVBA_RESET ++ , XVBA_LOST ++ , XVBA_ERROR ++ }; ++ ++ CDecoder(); ++ virtual ~CDecoder(); ++ virtual void OnLostDevice(); ++ virtual void OnResetDevice(); ++ ++ virtual bool Open(AVCodecContext* avctx, const enum PixelFormat fmt, unsigned int surfaces = 0); ++ virtual int Decode (AVCodecContext* avctx, AVFrame* frame); ++ virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture); ++ virtual void Reset(); ++ virtual void Close(); ++ virtual int Check(AVCodecContext* avctx); ++ virtual long Release(); ++ virtual const std::string Name() { return "xvba"; } ++ virtual bool CanSkipDeint(); ++ virtual void SetSpeed(int speed); ++ ++ bool Supports(EINTERLACEMETHOD method); ++ long ReleasePicReference(); ++ ++protected: ++ bool CreateSession(AVCodecContext* avctx); ++ void DestroySession(); ++ bool EnsureDataControlBuffers(unsigned int num); ++ void ResetState(); ++ void SetError(const char* function, const char* msg, int line); ++ bool IsSurfaceValid(xvba_render_state *render); ++ void ReturnRenderPicture(CXvbaRenderPicture *renderPic); ++ ++ // callbacks for ffmpeg ++ static void FFReleaseBuffer(AVCodecContext *avctx, AVFrame *pic); ++ static void FFDrawSlice(struct AVCodecContext *avctx, ++ const AVFrame *src, int offset[4], ++ int y, int type, int height); ++ static int FFGetBuffer(AVCodecContext *avctx, AVFrame *pic); ++ ++ DllAvUtil m_dllAvUtil; ++ CCriticalSection m_decoderSection; ++ CEvent m_displayEvent; ++ EDisplayState m_displayState; ++ CXvbaConfig m_xvbaConfig; ++ std::vector m_videoSurfaces; ++ CCriticalSection m_apiSec, m_videoSurfaceSec; ++ ThreadIdentifier m_decoderThread; ++ ++ unsigned int m_decoderId; ++ struct XVBABufferPool ++ { ++ XVBABufferDescriptor *picture_descriptor_buffer; ++ XVBABufferDescriptor *iq_matrix_buffer; ++ XVBABufferDescriptor *data_buffer; ++ std::vector data_control_buffers; ++ }; ++ XVBABufferPool m_xvbaBufferPool; ++ ++ COutput m_xvbaOutput; ++ CXvbaBufferStats m_bufferStats; ++ CEvent m_inMsgEvent; ++ CXvbaRenderPicture *m_presentPicture; ++ ++ int m_speed; ++ int m_codecControl; ++}; ++ ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp 2012-10-03 03:02:11.495943656 +0200 +@@ -18,13 +18,13 @@ + * + */ + +-#include "system.h" + #ifndef __STDC_CONSTANT_MACROS + #define __STDC_CONSTANT_MACROS + #endif + #ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS + #endif ++#include "system.h" + #ifdef _LINUX + #include "stdint.h" + #endif +@@ -495,6 +495,9 @@ + AddStream(i); + } + ++ m_bPtsWrapChecked = false; ++ m_bPtsWrap = false; ++ + return true; + } + +@@ -605,6 +608,12 @@ + if (pts == (int64_t)AV_NOPTS_VALUE) + return DVD_NOPTS_VALUE; + ++ if (m_bPtsWrap) ++ { ++ if (pts < m_iStartTime && pts < m_iEndTime) ++ pts += m_iMaxTime; ++ } ++ + // do calculations in floats as they can easily overflow otherwise + // we don't care for having a completly exact timestamp anyway + double timestamp = (double)pts * num / den; +@@ -729,6 +738,24 @@ + pkt.pts = AV_NOPTS_VALUE; + } + ++ if (!m_bPtsWrapChecked && m_pFormatContext->iformat->flags & AVFMT_TS_DISCONT) ++ { ++ int defaultStream = m_dllAvFormat.av_find_default_stream_index(m_pFormatContext); ++ int64_t duration = m_pFormatContext->streams[defaultStream]->duration * 1.5; ++ m_iMaxTime = 1LL<streams[defaultStream]->pts_wrap_bits; ++ m_iStartTime = m_pFormatContext->streams[defaultStream]->start_time; ++ if (m_iStartTime != DVD_NOPTS_VALUE) ++ { ++ m_iEndTime = (m_iStartTime + duration) & ~m_iMaxTime; ++ if (m_iEndTime < m_iStartTime) ++ { ++ CLog::Log(LOGNOTICE,"CDVDDemuxFFmpeg::Read - file contains pts overflow"); ++ m_bPtsWrap = true; ++ } ++ } ++ m_bPtsWrapChecked = true; ++ } ++ + // copy contents into our own packet + pPacket->iSize = pkt.size; + +@@ -845,10 +872,20 @@ + int ret; + { + CSingleLock lock(m_critSection); ++ + ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); + + if(ret >= 0) ++ { + UpdateCurrentPTS(); ++ ++ // seek may fail silently on streams which allow discontinuity ++ // if current timestamp is way off asume a pts overflow and try bisect seek ++ if (m_bPtsWrap && fabs(time - m_iCurrentPts/1000) > 10000) ++ { ++ ret = SeekTimeDiscont(seek_pts, backwords) ? 1 : -1; ++ } ++ } + } + + if(m_iCurrentPts == DVD_NOPTS_VALUE) +@@ -867,6 +904,165 @@ + return (ret >= 0); + } + ++bool CDVDDemuxFFmpeg::SeekTimeDiscont(int64_t pts, bool backwards) ++{ ++ // this code is taken from ffmpeg function ff_gen_search ++ // it is modified to assume a pts overflow if timestamp < start_time ++ if (!m_pFormatContext->iformat->read_timestamp) ++ return false; ++ ++ int defaultStream = m_dllAvFormat.av_find_default_stream_index(m_pFormatContext); ++ ++ if (defaultStream < 0) ++ { ++ return false; ++ } ++ ++ // timestamp for default must be expressed in AV_TIME_BASE units ++ pts = m_dllAvUtil.av_rescale_rnd(pts, m_pFormatContext->streams[defaultStream]->time_base.den, ++ AV_TIME_BASE * (int64_t)m_pFormatContext->streams[defaultStream]->time_base.num, ++ AV_ROUND_NEAR_INF); ++ ++ int64_t pos, pos_min, pos_max, pos_limit, ts, ts_min, ts_max; ++ int64_t start_pos, filesize; ++ int no_change; ++ ++ pos_min = m_pFormatContext->data_offset; ++ ts_min = m_pFormatContext->iformat->read_timestamp(m_pFormatContext, defaultStream, ++ &pos_min, INT64_MAX); ++ if (ts_min == AV_NOPTS_VALUE) ++ return false; ++ ++ if(ts_min >= pts) ++ { ++ pos = pos_min; ++ return true; ++ } ++ ++ int step= 1024; ++ filesize = m_pInput->GetLength(); ++ pos_max = filesize - 1; ++ do ++ { ++ pos_max -= step; ++ ts_max = m_pFormatContext->iformat->read_timestamp(m_pFormatContext, defaultStream, ++ &pos_max, pos_max + step); ++ step += step; ++ }while (ts_max == AV_NOPTS_VALUE && pos_max >= step); ++ ++ if (ts_max == AV_NOPTS_VALUE) ++ return false; ++ ++ if (ts_max < m_iStartTime && ts_max < m_iEndTime) ++ ts_max += m_iMaxTime; ++ ++ for(;;) ++ { ++ int64_t tmp_pos = pos_max + 1; ++ int64_t tmp_ts = m_pFormatContext->iformat->read_timestamp(m_pFormatContext, defaultStream, ++ &tmp_pos, INT64_MAX); ++ if(tmp_ts == AV_NOPTS_VALUE) ++ break; ++ ++ if (tmp_ts < m_iStartTime && tmp_ts < m_iEndTime) ++ tmp_ts += m_iMaxTime; ++ ++ ts_max = tmp_ts; ++ pos_max = tmp_pos; ++ if (tmp_pos >= filesize) ++ break; ++ } ++ pos_limit = pos_max; ++ ++ if(ts_max <= pts) ++ { ++ bool ret = SeekByte(pos_max); ++ if (ret) ++ { ++ m_iCurrentPts = ConvertTimestamp(ts_max, m_pFormatContext->streams[defaultStream]->time_base.den, ++ m_pFormatContext->streams[defaultStream]->time_base.num); ++ } ++ return ret; ++ } ++ ++ if(ts_min > ts_max) ++ { ++ return false; ++ } ++ else if (ts_min == ts_max) ++ { ++ pos_limit = pos_min; ++ } ++ ++ no_change=0; ++ while (pos_min < pos_limit) ++ { ++ if (no_change == 0) ++ { ++ int64_t approximate_keyframe_distance= pos_max - pos_limit; ++ // interpolate position (better than dichotomy) ++ pos = m_dllAvUtil.av_rescale_rnd(pts - ts_min, pos_max - pos_min, ++ ts_max - ts_min, AV_ROUND_NEAR_INF) ++ + pos_min - approximate_keyframe_distance; ++ } ++ else if (no_change == 1) ++ { ++ // bisection, if interpolation failed to change min or max pos last time ++ pos = (pos_min + pos_limit) >> 1; ++ } ++ else ++ { ++ /* linear search if bisection failed, can only happen if there ++ are very few or no keyframes between min/max */ ++ pos = pos_min; ++ } ++ if (pos <= pos_min) ++ pos= pos_min + 1; ++ else if (pos > pos_limit) ++ pos= pos_limit; ++ start_pos = pos; ++ ++ ts = m_pFormatContext->iformat->read_timestamp(m_pFormatContext, defaultStream, ++ &pos, INT64_MAX); ++ if (pos == pos_max) ++ no_change++; ++ else ++ no_change=0; ++ ++ if (ts == AV_NOPTS_VALUE) ++ { ++ return false; ++ } ++ ++ if (ts < m_iStartTime && ts < m_iEndTime) ++ ts += m_iMaxTime; ++ ++ if (pts <= ts) ++ { ++ pos_limit = start_pos - 1; ++ pos_max = pos; ++ ts_max = ts; ++ } ++ if (pts >= ts) ++ { ++ pos_min = pos; ++ ts_min = ts; ++ } ++ } ++ ++ pos = (backwards) ? pos_min : pos_max; ++ ts = (backwards) ? ts_min : ts_max; ++ ++ bool ret = SeekByte(pos); ++ if (ret) ++ { ++ m_iCurrentPts = ConvertTimestamp(ts, m_pFormatContext->streams[defaultStream]->time_base.den, ++ m_pFormatContext->streams[defaultStream]->time_base.num); ++ } ++ ++ return ret; ++} ++ + bool CDVDDemuxFFmpeg::SeekByte(int64_t pos) + { + g_demuxer.set(this); +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h 2012-10-03 03:02:11.496943676 +0200 +@@ -97,6 +97,7 @@ + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); ++ bool SeekTimeDiscont(int64_t pts, bool backwards); + bool SeekByte(int64_t pos); + int GetStreamLength(); + CDemuxStream* GetStream(int iStreamId); +@@ -141,5 +142,8 @@ + XbmcThreads::EndTime m_timeout; + + CDVDInputStream* m_pInput; ++ ++ bool m_bPtsWrap, m_bPtsWrapChecked; ++ int64_t m_iStartTime, m_iMaxTime, m_iEndTime; + }; + +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp 2012-10-03 03:02:11.518944123 +0200 +@@ -45,61 +45,6 @@ + + using namespace std; + +-CPTSOutputQueue::CPTSOutputQueue() +-{ +- Flush(); +-} +- +-void CPTSOutputQueue::Add(double pts, double delay, double duration) +-{ +- CSingleLock lock(m_sync); +- +- TPTSItem item; +- item.pts = pts; +- item.timestamp = CDVDClock::GetAbsoluteClock() + delay; +- item.duration = duration; +- +- // first one is applied directly +- if(m_queue.empty() && m_current.pts == DVD_NOPTS_VALUE) +- m_current = item; +- else +- m_queue.push(item); +- +- // call function to make sure the queue +- // doesn't grow should nobody call it +- Current(); +-} +-void CPTSOutputQueue::Flush() +-{ +- CSingleLock lock(m_sync); +- +- while( !m_queue.empty() ) m_queue.pop(); +- m_current.pts = DVD_NOPTS_VALUE; +- m_current.timestamp = 0.0; +- m_current.duration = 0.0; +-} +- +-double CPTSOutputQueue::Current() +-{ +- CSingleLock lock(m_sync); +- +- if(!m_queue.empty() && m_current.pts == DVD_NOPTS_VALUE) +- { +- m_current = m_queue.front(); +- m_queue.pop(); +- } +- +- while( !m_queue.empty() && CDVDClock::GetAbsoluteClock() >= m_queue.front().timestamp ) +- { +- m_current = m_queue.front(); +- m_queue.pop(); +- } +- +- if( m_current.timestamp == 0 ) return m_current.pts; +- +- return m_current.pts + min(m_current.duration, (CDVDClock::GetAbsoluteClock() - m_current.timestamp)); +-} +- + void CPTSInputQueue::Add(int64_t bytes, double pts) + { + CSingleLock lock(m_sync); +@@ -152,7 +97,6 @@ + CDVDStreamInfo m_hints; + }; + +- + CDVDPlayerAudio::CDVDPlayerAudio(CDVDClock* pClock, CDVDMessageQueue& parent) + : CThread("CDVDPlayerAudio") + , m_messageQueue("audio") +@@ -172,21 +116,17 @@ + m_synctype = SYNC_DISCON; + m_setsynctype = SYNC_DISCON; + m_prevsynctype = -1; +- m_error = 0; +- m_errorbuff = 0; +- m_errorcount = 0; + m_syncclock = true; + m_integral = 0; + m_skipdupcount = 0; + m_prevskipped = false; + m_maxspeedadjust = 0.0; + +- m_errortime = 0; +- m_freq = CurrentHostFrequency(); +- + m_messageQueue.SetMaxDataSize(6 * 1024 * 1024); + m_messageQueue.SetMaxTimeSize(8.0); + g_dvdPerformanceCounter.EnableAudioQueue(&m_messageQueue); ++ ++ m_syncError.Reset(); + } + + CDVDPlayerAudio::~CDVDPlayerAudio() +@@ -250,17 +190,15 @@ + m_setsynctype = g_guiSettings.GetInt("videoplayer.synctype"); + m_prevsynctype = -1; + +- m_error = 0; +- m_errorbuff = 0; +- m_errorcount = 0; + m_integral = 0; + m_skipdupcount = 0; + m_prevskipped = false; + m_syncclock = true; +- m_errortime = CurrentHostCounter(); + m_silence = false; + + m_maxspeedadjust = g_guiSettings.GetFloat("videoplayer.maxspeedadjust"); ++ ++ m_syncError.Reset(); + } + + void CDVDPlayerAudio::CloseStream(bool bWaitForBuffers) +@@ -298,9 +236,6 @@ + delete m_pAudioCodec; + m_pAudioCodec = NULL; + } +- +- // flush any remaining pts values +- m_ptsOutput.Flush(); + } + + // decode one audio frame and returns its uncompressed size +@@ -449,12 +384,11 @@ + m_audioClock = pMsgGeneralResync->m_timestamp; + + m_ptsInput.Flush(); +- m_ptsOutput.Flush(); +- m_ptsOutput.Add(m_audioClock, m_dvdAudio.GetDelay(), 0); ++ m_dvdAudio.SetPlayingPts(m_audioClock); + if (pMsgGeneralResync->m_clock) + { + CLog::Log(LOGDEBUG, "CDVDPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 1)", m_audioClock); +- m_pClock->Discontinuity(m_ptsOutput.Current()); ++ m_pClock->Discontinuity(m_dvdAudio.GetPlayingPts()); + } + else + CLog::Log(LOGDEBUG, "CDVDPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f, 0)", m_audioClock); +@@ -469,7 +403,6 @@ + else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) + { + m_dvdAudio.Flush(); +- m_ptsOutput.Flush(); + m_ptsInput.Flush(); + m_syncclock = true; + m_stalled = true; +@@ -515,7 +448,6 @@ + } + else + { +- m_ptsOutput.Flush(); + m_syncclock = true; + if (m_speed != DVD_PLAYSPEED_PAUSE) + m_dvdAudio.Flush(); +@@ -654,15 +586,6 @@ + m_stalled = false; + } + +- // store the delay for this pts value so we can calculate the current playing +- if(packetadded) +- { +- if(m_speed == DVD_PLAYSPEED_PAUSE) +- m_ptsOutput.Add(audioframe.pts, m_dvdAudio.GetDelay() - audioframe.duration, 0); +- else +- m_ptsOutput.Add(audioframe.pts, m_dvdAudio.GetDelay() - audioframe.duration, audioframe.duration); +- } +- + // signal to our parent that we have initialized + if(m_started == false) + { +@@ -670,7 +593,7 @@ + m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_AUDIO)); + } + +- if( m_ptsOutput.Current() == DVD_NOPTS_VALUE ) ++ if( m_dvdAudio.GetPlayingPts() == DVD_NOPTS_VALUE ) + continue; + + if( m_speed != DVD_PLAYSPEED_NORMAL ) +@@ -712,7 +635,7 @@ + void CDVDPlayerAudio::HandleSyncError(double duration) + { + double clock = m_pClock->GetClock(); +- double error = m_ptsOutput.Current() - clock; ++ double error = m_dvdAudio.GetPlayingPts() - clock; + int64_t now; + + if( fabs(error) > DVD_MSEC_TO_TIME(100) || m_syncclock ) +@@ -721,41 +644,29 @@ + if(m_speed == DVD_PLAYSPEED_NORMAL) + CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f", clock, clock+error, error); + +- m_errorbuff = 0; +- m_errorcount = 0; + m_skipdupcount = 0; +- m_error = 0; + m_syncclock = false; +- m_errortime = CurrentHostCounter(); +- ++ m_syncError.Reset(); + return; + } + + if (m_speed != DVD_PLAYSPEED_NORMAL) + { +- m_errorbuff = 0; +- m_errorcount = 0; + m_integral = 0; + m_skipdupcount = 0; +- m_error = 0; +- m_errortime = CurrentHostCounter(); ++ m_syncError.Reset(); + return; + } + +- m_errorbuff += error; +- m_errorcount++; ++ m_syncError.AddCurrent(error); + + //check if measured error for 1 second + now = CurrentHostCounter(); +- if ((now - m_errortime) >= m_freq) ++ if ((now - m_syncError.m_errortime) >= m_syncError.m_freq * 2) + { +- m_errortime = now; +- m_error = m_errorbuff / m_errorcount; ++ m_syncError.CalcAverage(now); + +- m_errorbuff = 0; +- m_errorcount = 0; +- +- if (m_synctype == SYNC_DISCON) ++ if (m_synctype == SYNC_DISCON || ((m_synctype == SYNC_SKIPDUP) && m_syncError.m_histErrorCount < 0)) + { + double limit, error; + if (g_VideoReferenceClock.GetRefreshRate(&limit) > 0) +@@ -765,15 +676,15 @@ + + //make error a multiple of limit, rounded towards zero, + //so it won't interfere with the sync methods in CXBMCRenderManager::WaitPresentTime +- if (m_error > 0.0) +- error = limit * floor(m_error / limit); ++ if (m_syncError.m_error > 0.0) ++ error = limit * floor(m_syncError.m_error / limit); + else +- error = limit * ceil(m_error / limit); ++ error = limit * ceil(m_syncError.m_error / limit); + } + else + { + limit = DVD_MSEC_TO_TIME(10); +- error = m_error; ++ error = m_syncError.m_error; + } + + if (fabs(error) > limit - 0.001) +@@ -782,14 +693,18 @@ + if(m_speed == DVD_PLAYSPEED_NORMAL) + CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f", clock, clock+error, error); + } ++ ++ m_syncError.m_histErrorCount = 0; + } +- else if (m_synctype == SYNC_SKIPDUP && m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) ++ else if (m_synctype == SYNC_SKIPDUP && m_skipdupcount == 0 && ++ fabs(m_syncError.m_error) > DVD_MSEC_TO_TIME(10) && ++ m_syncError.m_histErrorCount > 10) + { + //check how many packets to skip/duplicate +- m_skipdupcount = (int)(m_error / duration); ++ m_skipdupcount = (int)(m_syncError.m_error / duration); + //if less than one frame off, see if it's more than two thirds of a frame, so we can get better in sync +- if (m_skipdupcount == 0 && fabs(m_error) > duration / 3 * 2) +- m_skipdupcount = (int)(m_error / (duration / 3 * 2)); ++ if (m_skipdupcount == 0 && fabs(m_syncError.m_error) > duration / 3 * 2) ++ m_skipdupcount = (int)(m_syncError.m_error / (duration / 3 * 2)); + + if (m_skipdupcount > 0) + CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Duplicating %i packet(s) of %.2f ms duration", +@@ -797,14 +712,16 @@ + else if (m_skipdupcount < 0) + CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Skipping %i packet(s) of %.2f ms duration ", + m_skipdupcount * -1, duration / DVD_TIME_BASE * 1000.0); ++ ++ m_syncError.m_histErrorCount = 0; + } + else if (m_synctype == SYNC_RESAMPLE) + { + //reset the integral on big errors, failsafe +- if (fabs(m_error) > DVD_TIME_BASE) ++ if (fabs(m_syncError.m_error) > DVD_TIME_BASE) + m_integral = 0; +- else if (fabs(m_error) > DVD_MSEC_TO_TIME(5)) +- m_integral += m_error / DVD_TIME_BASE / INTEGRAL; ++ else if (fabs(m_syncError.m_error) > DVD_MSEC_TO_TIME(5)) ++ m_integral += m_syncError.m_error / DVD_TIME_BASE / INTEGRAL; + } + } + } +@@ -842,13 +759,13 @@ + double proportional = 0.0, proportionaldiv; + + //on big errors use more proportional +- if (fabs(m_error / DVD_TIME_BASE) > 0.0) ++ if (fabs(m_syncError.m_error / DVD_TIME_BASE) > 0.0) + { +- proportionaldiv = PROPORTIONAL * (PROPREF / fabs(m_error / DVD_TIME_BASE)); ++ proportionaldiv = PROPORTIONAL * (PROPREF / fabs(m_syncError.m_error / DVD_TIME_BASE)); + if (proportionaldiv < PROPDIVMIN) proportionaldiv = PROPDIVMIN; + else if (proportionaldiv > PROPDIVMAX) proportionaldiv = PROPDIVMAX; + +- proportional = m_error / DVD_TIME_BASE / proportionaldiv; ++ proportional = m_syncError.m_error / DVD_TIME_BASE / proportionaldiv; + } + + m_resampleratio = 1.0 / g_VideoReferenceClock.GetSpeed() + proportional + m_integral; +@@ -940,3 +857,44 @@ + { + return m_pAudioCodec && m_pAudioCodec->NeedPassthrough(); + } ++ ++void CDVDPlayerAudio::SyncError::Reset() ++{ ++ m_error = 0; ++ m_errorbuff = 0; ++ m_errorcount = 0; ++ m_errortime = 0; ++ m_freq = CurrentHostFrequency(); ++ m_histError = 0; ++ m_histErrorCount = 0; ++} ++ ++void CDVDPlayerAudio::SyncError::AddCurrent(double error) ++{ ++ m_errorbuff += error; ++ m_errorcount++; ++} ++ ++void CDVDPlayerAudio::SyncError::CalcAverage(int64_t time) ++{ ++ m_errortime = time; ++ m_error = m_errorbuff / m_errorcount; ++ m_errorbuff = 0; ++ m_errorcount = 0; ++ ++ if (m_histErrorCount <= 0) ++ { ++ m_histError = m_error; ++ m_histErrorCount = 1; ++ } ++ else ++ { ++ if (fabs(m_error-m_histError) < DVD_MSEC_TO_TIME(20)) ++ m_histErrorCount++; ++ else ++ { ++ CLog::Log(LOGDEBUG, "CDVDPlayerAudio::SyncError - error variance: %d ms", DVD_TIME_TO_MSEC(m_error-m_histError)); ++ m_histErrorCount = -1; ++ } ++ } ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerAudio.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerAudio.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerAudio.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerAudio.h 2012-10-03 03:02:11.503943819 +0200 +@@ -63,21 +63,6 @@ + bool passthrough; + } DVDAudioFrame; + +-class CPTSOutputQueue +-{ +-private: +- typedef struct {double pts; double timestamp; double duration;} TPTSItem; +- TPTSItem m_current; +- std::queue m_queue; +- CCriticalSection m_sync; +- +-public: +- CPTSOutputQueue(); +- void Add(double pts, double delay, double duration); +- void Flush(); +- double Current(); +-}; +- + class CPTSInputQueue + { + private: +@@ -131,7 +116,7 @@ + CPTSOutputQueue m_ptsOutput; + CPTSInputQueue m_ptsInput; + +- double GetCurrentPts() { return m_ptsOutput.Current(); } ++ double GetCurrentPts() { return m_dvdAudio.GetPlayingPts(); } + + bool IsStalled() { return m_stalled; } + bool IsPassthrough() const; +@@ -188,6 +173,21 @@ + } + } m_decode; + ++ struct SyncError ++ { ++ void Reset(); ++ void AddCurrent(double error); ++ void CalcAverage(int64_t time); ++ double m_error; //last average error ++ int64_t m_errortime; //timestamp of last time we measured ++ int64_t m_freq; ++ double m_errorbuff; //place to store average errors ++ int m_errorcount;//number of errors stored ++ ++ double m_histError; ++ int m_histErrorCount; ++ } m_syncError; ++ + CDVDAudio m_dvdAudio; // audio output device + CDVDClock* m_pClock; // dvd master clock + CDVDAudioCodec* m_pAudioCodec; // audio codec +@@ -207,15 +207,9 @@ + int m_setsynctype; + int m_prevsynctype; //so we can print to the log + +- double m_error; //last average error +- +- int64_t m_errortime; //timestamp of last time we measured +- int64_t m_freq; +- + void SetSyncType(bool passthrough); + void HandleSyncError(double duration); +- double m_errorbuff; //place to store average errors +- int m_errorcount;//number of errors stored ++ + bool m_syncclock; + + double m_integral; //integral correction for resampler +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayer.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayer.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayer.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayer.cpp 2012-10-03 03:02:11.379941298 +0200 +@@ -462,7 +462,7 @@ + m_ready.Reset(); + + #if defined(HAS_VIDEO_PLAYBACK) +- g_renderManager.PreInit(); ++ g_renderManager.PreInit(&m_clock); + #endif + + Create(); +@@ -1557,7 +1557,7 @@ + + } + else if (m_CurrentVideo.id >= 0 +- && m_CurrentVideo.inited == true ++ && (m_CurrentVideo.inited == true || GetPlaySpeed() < 0) // allow rewind at end of file + && m_SpeedState.lastpts != m_dvdPlayerVideo.GetCurrentPts() + && m_SpeedState.lasttime != GetTime()) + { +@@ -2184,6 +2184,12 @@ + if (speed != DVD_PLAYSPEED_PAUSE && m_playSpeed != DVD_PLAYSPEED_PAUSE && speed != m_playSpeed) + m_callback.OnPlayBackSpeedChanged(speed / DVD_PLAYSPEED_NORMAL); + ++ // do a seek after rewind, clock is not in sync with current pts ++ if (m_playSpeed < 0 && speed >= 0) ++ { ++ m_messenger.Put(new CDVDMsgPlayerSeek(GetTime(), true, true, true)); ++ } ++ + // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE + // audioplayer, stops outputing audio to audiorendere, but still tries to + // sleep an correct amount for each packet +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp 2012-10-03 03:02:11.524944245 +0200 +@@ -261,6 +261,7 @@ + m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0; + m_started = false; + m_codecname = m_pVideoCodec->GetName(); ++ g_renderManager.EnableBuffering(false); + } + + void CDVDPlayerVideo::CloseStream(bool bWaitForBuffers) +@@ -325,8 +326,10 @@ + + int iDropped = 0; //frames dropped in a row + bool bRequestDrop = false; ++ int iDropDirective; + + m_videoStats.Start(); ++ m_droppingStats.Reset(); + + while (!m_bStop) + { +@@ -436,6 +439,8 @@ + picture.iFlags &= ~DVP_FLAG_ALLOCATED; + m_packets.clear(); + m_started = false; ++ m_droppingStats.Reset(); ++ g_renderManager.EnableBuffering(false); + } + else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush()) + { +@@ -448,6 +453,8 @@ + //we need to recalculate the framerate + //TODO: this needs to be set on a streamchange instead + ResetFrameRateCalc(); ++ m_droppingStats.Reset(); ++ g_renderManager.EnableBuffering(false); + + m_stalled = true; + m_started = false; +@@ -465,6 +472,7 @@ + m_speed = static_cast(pMsg)->m_value; + if(m_speed == DVD_PLAYSPEED_PAUSE) + m_iNrOfPicturesNotToSkip = 0; ++ m_droppingStats.Reset(); + } + else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) + { +@@ -499,6 +507,28 @@ + m_iNrOfPicturesNotToSkip = 1; + } + ++ bRequestDrop = false; ++ iDropDirective = CalcDropRequirement(pts); ++ if (iDropDirective & EOS_VERYLATE) ++ { ++ if (m_bAllowDrop) ++ { ++ m_pullupCorrection.Flush(); ++ bRequestDrop = true; ++ } ++ } ++ int codecControl = 0; ++ if (iDropDirective & EOS_BUFFER_LEVEL) ++ codecControl |= DVP_FLAG_DRAIN; ++ if (m_speed > DVD_PLAYSPEED_NORMAL) ++ codecControl |= DVP_FLAG_NO_POSTPROC; ++ m_pVideoCodec->SetCodecControl(codecControl); ++ if (iDropDirective & EOS_DROPPED) ++ { ++ m_iDroppedFrames++; ++ iDropped++; ++ } ++ + #ifdef PROFILE + bRequestDrop = false; + #else +@@ -508,6 +538,7 @@ + bRequestDrop = false; + m_iDroppedRequest = 0; + m_iLateFrames = 0; ++ m_droppingStats.m_requestOutputDrop = false; + } + #endif + +@@ -555,15 +586,8 @@ + } + + m_videoStats.AddSampleBytes(pPacket->iSize); +- // assume decoder dropped a picture if it didn't give us any +- // picture from a demux packet, this should be reasonable +- // for libavformat as a demuxer as it normally packetizes +- // pictures when they come from demuxer +- if(bRequestDrop && !bPacketDrop && (iDecoderState & VC_BUFFER) && !(iDecoderState & VC_PICTURE)) +- { +- m_iDroppedFrames++; +- iDropped++; +- } ++ ++ bRequestDrop = false; + + // loop while no error + while (!m_bStop) +@@ -586,6 +610,8 @@ + + m_pVideoCodec->Reset(); + m_packets.clear(); ++ picture.iFlags &= ~DVP_FLAG_ALLOCATED; ++ g_renderManager.DiscardBuffer(); + break; + } + +@@ -695,11 +721,15 @@ + CDVDCodecUtils::FreePicture(pTempYUVPackedPicture); + #endif + ++ if (!m_bFpsInvalid) ++ frametime = (double)DVD_TIME_BASE/m_fFrameRate; ++ + if(m_started == false) + { + m_codecname = m_pVideoCodec->GetName(); + m_started = true; + m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); ++ g_renderManager.EnableBuffering(true); + } + + // guess next frame pts. iDuration is always valid +@@ -1004,7 +1034,7 @@ + || m_output.height != pPicture->iHeight + || m_output.dwidth != pPicture->iDisplayWidth + || m_output.dheight != pPicture->iDisplayHeight +- || m_output.framerate != config_framerate ++ || (!m_bFpsInvalid && fmod(m_output.framerate, config_framerate) != 0.0 ) + || m_output.color_format != (unsigned int)pPicture->format + || m_output.extended_format != pPicture->extended_format + || ( m_output.color_matrix != pPicture->color_matrix && pPicture->color_matrix != 0 ) // don't reconfigure on unspecified +@@ -1112,6 +1142,9 @@ + case RENDER_FMT_VDPAU: + formatstr = "VDPAU"; + break; ++ case RENDER_FMT_VDPAU_420: ++ formatstr = "VDPAU_420"; ++ break; + case RENDER_FMT_DXVA: + formatstr = "DXVA"; + break; +@@ -1130,6 +1163,9 @@ + case RENDER_FMT_NONE: + formatstr = "NONE"; + break; ++ case RENDER_FMT_XVBA: ++ formatstr = "XVBA"; ++ break; + } + + if(m_bAllowFullscreen) +@@ -1149,7 +1185,7 @@ + m_output.height = pPicture->iHeight; + m_output.dwidth = pPicture->iDisplayWidth; + m_output.dheight = pPicture->iDisplayHeight; +- m_output.framerate = config_framerate; ++ m_output.framerate = config_framerate == 0.0 ? g_graphicsContext.GetFPS() : config_framerate; + m_output.color_format = pPicture->format; + m_output.extended_format = pPicture->extended_format; + m_output.color_matrix = pPicture->color_matrix; +@@ -1221,67 +1257,47 @@ + iSleepTime = iFrameSleep; + else + iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; ++// ++//#ifdef PROFILE /* during profiling, try to play as fast as possible */ ++// iSleepTime = 0; ++//#endif ++// ++// // present the current pts of this frame to user, and include the actual ++// // presentation delay, to allow him to adjust for it ++// if( m_stalled ) ++// m_iCurrentPts = DVD_NOPTS_VALUE; ++// else ++// m_iCurrentPts = pts - max(0.0, iSleepTime); ++// ++// // timestamp when we think next picture should be displayed based on current duration ++// m_FlipTimeStamp = iCurrentClock; ++// m_FlipTimeStamp += max(0.0, iSleepTime); ++// m_FlipTimeStamp += iFrameDuration; ++ ++ if ((m_droppingStats.m_requestOutputDrop && !(pPicture->iFlags & DVP_FLAG_NOSKIP)) ++ || (pPicture->iFlags & DVP_FLAG_DROPPED)) ++ { ++ m_droppingStats.AddOutputDropGain(pts, 1/m_fFrameRate); ++ m_droppingStats.m_requestOutputDrop = false; ++ CLog::Log(LOGDEBUG,"%s - dropped in output", __FUNCTION__); ++ return result | EOS_DROPPED; ++ } + +-#ifdef PROFILE /* during profiling, try to play as fast as possible */ +- iSleepTime = 0; +-#endif +- +- // present the current pts of this frame to user, and include the actual +- // presentation delay, to allow him to adjust for it +- if( m_stalled ) +- m_iCurrentPts = DVD_NOPTS_VALUE; +- else +- m_iCurrentPts = pts - max(0.0, iSleepTime); +- +- // timestamp when we think next picture should be displayed based on current duration +- m_FlipTimeStamp = iCurrentClock; +- m_FlipTimeStamp += max(0.0, iSleepTime); +- m_FlipTimeStamp += iFrameDuration; +- +- if (iSleepTime <= 0 && m_speed) +- m_iLateFrames++; +- else +- m_iLateFrames = 0; +- +- // ask decoder to drop frames next round, as we are very late +- if(m_iLateFrames > 10) ++ if( m_speed < 0 ) + { +- if (!(pPicture->iFlags & DVP_FLAG_NOSKIP)) ++ double decoderPts = m_droppingStats.m_lastDecoderPts; ++ double renderPts = m_droppingStats.m_lastRenderPts; ++ if (pts > renderPts) + { +- //if we're calculating the framerate, +- //don't drop frames until we've calculated a stable framerate +- if (m_bAllowDrop || m_speed != DVD_PLAYSPEED_NORMAL) +- { +- result |= EOS_VERYLATE; +- m_pullupCorrection.Flush(); //dropped frames mess up the pattern, so just flush it +- } +- +- //if we requested 5 drops in a row and we're still late, drop on output +- //this keeps a/v sync if the decoder can't drop, or we're still calculating the framerate +- if (m_iDroppedRequest > 5) ++ if (decoderPts >= renderPts) + { +- m_iDroppedRequest--; //decrease so we only drop half the frames +- return result | EOS_DROPPED; ++ Sleep(200); + } +- m_iDroppedRequest++; +- } +- } +- else +- { +- m_iDroppedRequest = 0; +- } +- +- if( m_speed < 0 ) +- { +- if( iClockSleep < -DVD_MSEC_TO_TIME(200) +- && !(pPicture->iFlags & DVP_FLAG_NOSKIP) ) + return result | EOS_DROPPED; ++ } + } + +- if( (pPicture->iFlags & DVP_FLAG_DROPPED) ) +- return result | EOS_DROPPED; +- +- if( m_speed != DVD_PLAYSPEED_NORMAL && limited ) ++ if( m_speed != DVD_PLAYSPEED_NORMAL && m_speed >= 0 && limited ) + { + // calculate frame dropping pattern to render at this speed + // we do that by deciding if this or next frame is closest +@@ -1317,6 +1333,16 @@ + mDisplayField = FS_BOT; + } + ++ int buffer = g_renderManager.WaitForBuffer(m_bStop); ++ while (buffer < 0 && !CThread::m_bStop && ++ CDVDClock::GetAbsoluteClock(false) < iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500) ) ++ { ++ Sleep(1); ++ buffer = g_renderManager.WaitForBuffer(m_bStop); ++ } ++ if (buffer < 0) ++ return EOS_DROPPED; ++ + ProcessOverlays(pPicture, pts); + AutoCrop(pPicture); + +@@ -1333,7 +1359,7 @@ + if (index < 0) + return EOS_DROPPED; + +- g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, mDisplayField); ++ g_renderManager.FlipPage(CThread::m_bStop, pts, -1, mDisplayField, m_speed); + + return result; + #else +@@ -1552,6 +1578,22 @@ + g_advancedSettings.m_videoFpsDetect == 0; + } + ++double CDVDPlayerVideo::GetCurrentPts() ++{ ++ double iSleepTime, iRenderPts; ++ int iBufferLevel; ++ ++ // get render stats ++ g_renderManager.GetStats(iSleepTime, iRenderPts, iBufferLevel); ++ ++ if( m_stalled ) ++ iRenderPts = DVD_NOPTS_VALUE; ++ else ++ iRenderPts = iRenderPts - max(0.0, iSleepTime); ++ ++ return iRenderPts; ++} ++ + #define MAXFRAMERATEDIFF 0.01 + #define MAXFRAMESERR 1000 + +@@ -1576,7 +1618,7 @@ + double frameduration = m_pullupCorrection.GetFrameDuration(); + + if (frameduration == DVD_NOPTS_VALUE || +- (g_advancedSettings.m_videoFpsDetect == 1 && m_pullupCorrection.GetPatternLength() > 1)) ++ (g_advancedSettings.m_videoFpsDetect == 1 && (m_pullupCorrection.GetPatternLength() > 1 && !m_bFpsInvalid))) + { + //reset the stored framerates if no good framerate was detected + m_fStableFrameRate = 0.0; +@@ -1632,3 +1674,152 @@ + m_iFrameRateCount = 0; + } + } ++ ++int CDVDPlayerVideo::CalcDropRequirement(double pts) ++{ ++ int result = 0; ++ double iSleepTime; ++ double iDecoderPts, iRenderPts; ++ double iInterval; ++ int interlaced; ++ double iGain; ++ double iLateness; ++ bool bNewFrame; ++ int iSkippedDeint = 0; ++ int iBufferLevel; ++ ++ // get decoder stats ++ if (!m_pVideoCodec->GetPts(iDecoderPts, iSkippedDeint, interlaced)) ++ iDecoderPts = pts; ++ if (iDecoderPts == DVD_NOPTS_VALUE) ++ iDecoderPts = pts; ++ ++ // get render stats ++ g_renderManager.GetStats(iSleepTime, iRenderPts, iBufferLevel); ++ ++ if (iBufferLevel < 0) ++ result |= EOS_BUFFER_LEVEL; ++ else if (iBufferLevel < 2) ++ { ++ result |= EOS_BUFFER_LEVEL; ++ CLog::Log(LOGDEBUG,"CDVDPlayerVideo::CalcDropRequirement - hurry: %d", iBufferLevel); ++ } ++ ++ bNewFrame = iDecoderPts != m_droppingStats.m_lastDecoderPts; ++ ++ if (interlaced) ++ iInterval = 2/m_fFrameRate*(double)DVD_TIME_BASE; ++ else ++ iInterval = 1/m_fFrameRate*(double)DVD_TIME_BASE; ++ ++ ++ m_FlipTimeStamp = m_pClock->GetAbsoluteClock() + max(0.0, iSleepTime) + iInterval; ++ ++ if( m_stalled ) ++ m_iCurrentPts = DVD_NOPTS_VALUE; ++ else ++ m_iCurrentPts = iRenderPts - max(0.0, iSleepTime); ++ ++ ++ if (m_droppingStats.m_lastDecoderPts > 0 ++ && bNewFrame ++ && m_bAllowDrop ++ && m_droppingStats.m_dropRequests > 0) ++ { ++ iGain = (iDecoderPts - m_droppingStats.m_lastDecoderPts - iInterval)/(double)DVD_TIME_BASE; ++ if (iSkippedDeint) ++ { ++ CDroppingStats::CGain gain; ++ gain.gain = 1/m_fFrameRate; ++ gain.pts = iDecoderPts; ++ m_droppingStats.m_gain.push_back(gain); ++ m_droppingStats.m_totalGain += gain.gain; ++ result |= EOS_DROPPED; ++ m_droppingStats.m_dropRequests = 0; ++ CLog::Log(LOGDEBUG,"CDVDPlayerVideo::CalcDropRequirement - dropped de-interlacing cycle, Sleeptime: %f, Bufferlevel: %d", iSleepTime, iBufferLevel); ++ } ++ else if (iGain > 1/m_fFrameRate) ++ { ++ CDroppingStats::CGain gain; ++ gain.gain = iGain; ++ gain.pts = iDecoderPts; ++ m_droppingStats.m_gain.push_back(gain); ++ m_droppingStats.m_totalGain += iGain; ++ result |= EOS_DROPPED; ++ m_droppingStats.m_dropRequests = 0; ++ CLog::Log(LOGDEBUG,"CDVDPlayerVideo::CalcDropRequirement - dropped in decoder, Sleeptime: %f, Bufferlevel: %d, Gain: %f", iSleepTime, iBufferLevel, iGain); ++ } ++ ++ } ++ m_droppingStats.m_lastDecoderPts = iDecoderPts; ++ ++ // subtract gains ++ while (!m_droppingStats.m_gain.empty() && ++ iRenderPts >= m_droppingStats.m_gain.front().pts) ++ { ++ m_droppingStats.m_totalGain -= m_droppingStats.m_gain.front().gain; ++ m_droppingStats.m_gain.pop_front(); ++ } ++ ++// if (iSleepTime < 0) ++// { ++// CLog::Log(LOGNOTICE,"----- sleep: %f, gain :%f", ++// iSleepTime, m_droppingStats.m_totalGain); ++// } ++ ++ // calculate lateness ++ iLateness = iSleepTime + m_droppingStats.m_totalGain; ++ if (iLateness < 0 && m_speed) ++ { ++ if (bNewFrame) ++ m_droppingStats.m_lateFrames++; ++ ++ // if lateness is smaller than frametime, we observe this state ++ // for 10 cycles ++ if (m_droppingStats.m_lateFrames > 10 || iLateness < -2/m_fFrameRate) ++ { ++ // is frame allowed to skip ++ if (m_iNrOfPicturesNotToSkip <= 0) ++ { ++ result |= EOS_VERYLATE; ++ ++ // drop in output ++ if (m_droppingStats.m_dropRequests > 7 && g_graphicsContext.IsFullScreenVideo()) ++ { ++ m_droppingStats.m_dropRequests--; //decrease so we only drop half the frames ++ m_droppingStats.m_requestOutputDrop = true; ++ } ++ else if (bNewFrame) ++ m_droppingStats.m_dropRequests++; ++ } ++ } ++ } ++ else ++ { ++ m_droppingStats.m_dropRequests = 0; ++ m_droppingStats.m_lateFrames = 0; ++ m_droppingStats.m_requestOutputDrop = false; ++ } ++ m_droppingStats.m_lastRenderPts = iRenderPts; ++ return result; ++} ++ ++void CDroppingStats::Reset() ++{ ++ m_gain.clear(); ++ m_totalGain = 0; ++ m_lastDecoderPts = 0; ++ m_lastRenderPts = 0; ++ m_lateFrames = 0; ++ m_dropRequests = 0; ++ m_requestOutputDrop = false; ++} ++ ++void CDroppingStats::AddOutputDropGain(double pts, double frametime) ++{ ++ CDroppingStats::CGain gain; ++ gain.gain = frametime; ++ gain.pts = pts; ++ m_gain.push_back(gain); ++ m_totalGain += frametime; ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerVideo.h xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerVideo.h +--- xbmc-6cf5b9d/xbmc/cores/dvdplayer/DVDPlayerVideo.h 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/dvdplayer/DVDPlayerVideo.h 2012-10-03 03:02:11.498943716 +0200 +@@ -37,6 +37,26 @@ + + #define VIDEO_PICTURE_QUEUE_SIZE 1 + ++class CDroppingStats ++{ ++public: ++ void Reset(); ++ void AddOutputDropGain(double pts, double frametime); ++ struct CGain ++ { ++ double gain; ++ double pts; ++ }; ++ std::deque m_gain; ++ double m_totalGain; ++ double m_lastDecoderPts; ++ double m_lastRenderPts; ++ unsigned int m_lateFrames; ++ unsigned int m_dropRequests; ++ bool m_requestOutputDrop; ++}; ++ ++ + class CDVDPlayerVideo : public CThread + { + public: +@@ -88,7 +108,7 @@ + + bool InitializedOutputDevice(); + +- double GetCurrentPts() { return m_iCurrentPts; } ++ double GetCurrentPts(); + int GetPullupCorrection() { return m_pullupCorrection.GetPatternLength(); } + + double GetOutputDelay(); /* returns the expected delay, from that a packet is put in queue */ +@@ -110,6 +130,7 @@ + #define EOS_ABORT 1 + #define EOS_DROPPED 2 + #define EOS_VERYLATE 4 ++#define EOS_BUFFER_LEVEL 8 + + void AutoCrop(DVDVideoPicture* pPicture); + void AutoCrop(DVDVideoPicture *pPicture, RECT &crop); +@@ -135,6 +156,7 @@ + + void ResetFrameRateCalc(); + void CalcFrameRate(); ++ int CalcDropRequirement(double pts); + + double m_fFrameRate; //framerate of the video currently playing + bool m_bCalcFrameRate; //if we should calculate the framerate from the timestamps +@@ -195,5 +217,7 @@ + CPullupCorrection m_pullupCorrection; + + std::list m_packets; ++ ++ CDroppingStats m_droppingStats; + }; + +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/BaseRenderer.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/BaseRenderer.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/BaseRenderer.h 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/BaseRenderer.h 2012-10-03 03:02:11.350940709 +0200 +@@ -74,10 +74,13 @@ + void GetVideoRect(CRect &source, CRect &dest); + float GetAspectRatio() const; + +- virtual bool AddVideoPicture(DVDVideoPicture* picture) { return false; } ++ virtual bool AddVideoPicture(DVDVideoPicture* picture, int index) { return false; } + virtual void Flush() {}; + + virtual unsigned int GetProcessorSize() { return 0; } ++ virtual unsigned int GetMaxProcessorSize() { return 0; } ++ virtual void SetProcessorSize(int numBuffers) { } ++ virtual void ReleaseBuffer(int idx) { } + + virtual bool Supports(ERENDERFEATURE feature) { return false; } + +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp 2012-10-03 03:02:11.522944204 +0200 +@@ -63,6 +63,9 @@ + VA_MICRO_VERSION == 0 && VA_SDS_VERSION < 5))) + + #endif ++#ifdef HAVE_LIBXVBA ++#include "cores/dvdplayer/DVDCodecs/Video/XVBA.h" ++#endif + + #ifdef TARGET_DARWIN + #include "osx/CocoaInterface.h" +@@ -129,6 +132,9 @@ + #ifdef HAVE_LIBVDPAU + vdpau = NULL; + #endif ++#ifdef HAVE_LIBXVBA ++ xvba = NULL; ++#endif + } + + CLinuxRendererGL::YUVBUFFER::~YUVBUFFER() +@@ -238,7 +244,7 @@ + + void CLinuxRendererGL::ManageTextures() + { +- m_NumYV12Buffers = 2; ++ // m_NumYV12Buffers = 2; + //m_iYV12RenderBuffer = 0; + return; + } +@@ -256,6 +262,12 @@ + else + CLog::Log(LOGNOTICE,"Using GL_TEXTURE_2D"); + ++ // function pointer for texture might change in ++ // call to LoadShaders ++ glFinish(); ++ for (int i = 0 ; i < NUM_BUFFERS ; i++) ++ (this->*m_textureDelete)(i); ++ + // create the yuv textures + LoadShaders(); + +@@ -593,6 +605,25 @@ + + glFinish(); + m_bValidated = false; ++ m_iYV12RenderBuffer = 0; ++} ++ ++void CLinuxRendererGL::ReleaseBuffer(int idx) ++{ ++ YUVBUFFER &buf = m_buffers[idx]; ++#ifdef HAVE_LIBVDPAU ++ SAFE_RELEASE(buf.vdpau); ++#endif ++#ifdef HAVE_LIBXVBA ++ SAFE_RELEASE(buf.xvba); ++#endif ++#ifdef HAVE_LIBVA ++ buf.vaapi.surface.reset(); ++#endif ++#ifdef TARGET_DARWIN ++ if (buf.cvBufferRef) ++ CVBufferRelease(buf.cvBufferRef); ++#endif + } + + void CLinuxRendererGL::Update(bool bPauseDrawing) +@@ -673,6 +704,18 @@ + glDisable(GL_POLYGON_STIPPLE); + + } ++ else if(m_format == RENDER_FMT_VDPAU_420 ++ && !(flags & (RENDER_FLAG_TOP | RENDER_FLAG_BOT))) ++ { ++ glDisable(GL_BLEND); ++ glColor4f(1.0f, 1.0f, 1.0f, 1.0f); ++ Render(flags | RENDER_FLAG_TOP, index); ++ ++ glEnable(GL_BLEND); ++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ++ glColor4f(1.0f, 1.0f, 1.0f, 128 / 255.0f); ++ Render(flags | RENDER_FLAG_BOT , index); ++ } + else + Render(flags, index); + +@@ -753,11 +796,6 @@ + + m_buffers[m_iYV12RenderBuffer].flipindex = ++m_flipindex; + +-#ifdef HAVE_LIBVDPAU +- if((m_renderMethod & RENDER_VDPAU) && m_buffers[m_iYV12RenderBuffer].vdpau) +- m_buffers[m_iYV12RenderBuffer].vdpau->Present(); +-#endif +- + return; + } + +@@ -772,7 +810,6 @@ + m_resolution = RES_DESKTOP; + + m_iYV12RenderBuffer = 0; +- m_NumYV12Buffers = 2; + + m_formats.push_back(RENDER_FMT_YUV420P); + GLint size; +@@ -869,7 +906,7 @@ + case VS_SCALINGMETHOD_LINEAR: + SetTextureFilter(m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR); + m_renderQuality = RQ_SINGLEPASS; +- if (((m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI)) && m_nonLinStretch) ++ if (((m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI) || (m_renderMethod & RENDER_XVBA)) && m_nonLinStretch) + { + m_pVideoFilterShader = new StretchFilterShader(); + if (!m_pVideoFilterShader->CompileAndLink()) +@@ -955,6 +992,11 @@ + CLog::Log(LOGNOTICE, "GL: Using CVBREF render method"); + m_renderMethod = RENDER_CVREF; + } ++ else if (m_format == RENDER_FMT_XVBA) ++ { ++ CLog::Log(LOGNOTICE, "GL: Using XVBA render method"); ++ m_renderMethod = RENDER_XVBA; ++ } + else + { + int requestedMethod = g_guiSettings.GetInt("videoplayer.rendermethod"); +@@ -1085,6 +1127,12 @@ + m_textureCreate = &CLinuxRendererGL::CreateVDPAUTexture; + m_textureDelete = &CLinuxRendererGL::DeleteVDPAUTexture; + } ++ else if (m_format == RENDER_FMT_VDPAU_420) ++ { ++ m_textureUpload = &CLinuxRendererGL::UploadVDPAUTexture420; ++ m_textureCreate = &CLinuxRendererGL::CreateVDPAUTexture420; ++ m_textureDelete = &CLinuxRendererGL::DeleteVDPAUTexture420; ++ } + else if (m_format == RENDER_FMT_VAAPI) + { + m_textureUpload = &CLinuxRendererGL::UploadVAAPITexture; +@@ -1097,6 +1145,12 @@ + m_textureCreate = &CLinuxRendererGL::CreateCVRefTexture; + m_textureDelete = &CLinuxRendererGL::DeleteCVRefTexture; + } ++ else if (m_format == RENDER_FMT_XVBA) ++ { ++ m_textureUpload = &CLinuxRendererGL::UploadXVBATexture; ++ m_textureCreate = &CLinuxRendererGL::CreateXVBATexture; ++ m_textureDelete = &CLinuxRendererGL::DeleteXVBATexture; ++ } + else + { + // setup default YV12 texture handlers +@@ -1160,7 +1214,10 @@ + m_currentField = FIELD_FULL; + + // call texture load function ++ m_skipRender = false; + (this->*m_textureUpload)(renderBuffer); ++ if (m_skipRender) ++ return; + + if (m_renderMethod & RENDER_GLSL) + { +@@ -1197,6 +1254,13 @@ + RenderVAAPI(renderBuffer, m_currentField); + } + #endif ++#ifdef HAVE_LIBXVBA ++ else if (m_renderMethod & RENDER_XVBA) ++ { ++ UpdateVideoFilter(); ++ RenderXVBA(renderBuffer, m_currentField); ++ } ++#endif + else + { + // RENDER_CVREF uses the same render as the default case +@@ -1500,17 +1564,12 @@ + void CLinuxRendererGL::RenderVDPAU(int index, int field) + { + #ifdef HAVE_LIBVDPAU +- YUVPLANE &plane = m_buffers[index].fields[field][0]; +- CVDPAU *vdpau = m_buffers[m_iYV12RenderBuffer].vdpau; +- +- if (!vdpau) +- return; ++ YUVPLANE &plane = m_buffers[index].fields[0][1]; + + glEnable(m_textureTarget); + glActiveTextureARB(GL_TEXTURE0); +- glBindTexture(m_textureTarget, plane.id); + +- vdpau->BindPixmap(); ++ glBindTexture(m_textureTarget, plane.id); + + // Try some clamping or wrapping + glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +@@ -1568,8 +1627,6 @@ + if (m_pVideoFilterShader) + m_pVideoFilterShader->Disable(); + +- vdpau->ReleasePixmap(); +- + glBindTexture (m_textureTarget, 0); + glDisable(m_textureTarget); + #endif +@@ -1664,6 +1721,77 @@ + #endif + } + ++void CLinuxRendererGL::RenderXVBA(int index, int field) ++{ ++#ifdef HAVE_LIBXVBA ++ YUVPLANE &plane = m_buffers[index].fields[0][1]; ++ ++ glEnable(m_textureTarget); ++ glActiveTextureARB(GL_TEXTURE0); ++ ++ glBindTexture(m_textureTarget, plane.id); ++ ++ // Try some clamping or wrapping ++ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ++ ++ if (m_pVideoFilterShader) ++ { ++ GLint filter; ++ if (!m_pVideoFilterShader->GetTextureFilter(filter)) ++ filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR; ++ ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter); ++ m_pVideoFilterShader->SetSourceTexture(0); ++ m_pVideoFilterShader->SetWidth(m_sourceWidth); ++ m_pVideoFilterShader->SetHeight(m_sourceHeight); ++ ++ //disable non-linear stretch when a dvd menu is shown, parts of the menu are rendered through the overlay renderer ++ //having non-linear stretch on breaks the alignment ++ if (g_application.m_pPlayer && g_application.m_pPlayer->IsInMenu()) ++ m_pVideoFilterShader->SetNonLinStretch(1.0); ++ else ++ m_pVideoFilterShader->SetNonLinStretch(pow(g_settings.m_fPixelRatio, g_advancedSettings.m_videoNonLinStretchRatio)); ++ ++ m_pVideoFilterShader->Enable(); ++ } ++ else ++ { ++ GLint filter = m_scalingMethod == VS_SCALINGMETHOD_NEAREST ? GL_NEAREST : GL_LINEAR; ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, filter); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, filter); ++ } ++ ++ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); ++ VerifyGLState(); ++ ++ glBegin(GL_QUADS); ++ if (m_textureTarget==GL_TEXTURE_2D) ++ { ++ glTexCoord2f(plane.rect.x1, plane.rect.y1); glVertex2f(m_destRect.x1, m_destRect.y1); ++ glTexCoord2f(plane.rect.x2, plane.rect.y1); glVertex2f(m_destRect.x2, m_destRect.y1); ++ glTexCoord2f(plane.rect.x2, plane.rect.y2); glVertex2f(m_destRect.x2, m_destRect.y2); ++ glTexCoord2f(plane.rect.x1, plane.rect.y2); glVertex2f(m_destRect.x1, m_destRect.y2); ++ } ++ else ++ { ++ glTexCoord2f(m_destRect.x1, m_destRect.y1); glVertex4f(m_destRect.x1, m_destRect.y1, 0.0f, 0.0f); ++ glTexCoord2f(m_destRect.x2, m_destRect.y1); glVertex4f(m_destRect.x2, m_destRect.y1, 1.0f, 0.0f); ++ glTexCoord2f(m_destRect.x2, m_destRect.y2); glVertex4f(m_destRect.x2, m_destRect.y2, 1.0f, 1.0f); ++ glTexCoord2f(m_destRect.x1, m_destRect.y2); glVertex4f(m_destRect.x1, m_destRect.y2, 0.0f, 1.0f); ++ } ++ glEnd(); ++ VerifyGLState(); ++ ++ if (m_pVideoFilterShader) ++ m_pVideoFilterShader->Disable(); ++ ++ glBindTexture (m_textureTarget, 0); ++ glDisable(m_textureTarget); ++#endif ++} ++ + void CLinuxRendererGL::RenderSoftware(int index, int field) + { + // used for textues uploaded from rgba or CVPixelBuffers. +@@ -2253,12 +2381,14 @@ + { + #ifdef HAVE_LIBVDPAU + YUVPLANE &plane = m_buffers[index].fields[0][0]; ++ YUVFIELDS &fields = m_buffers[index].fields; + + SAFE_RELEASE(m_buffers[index].vdpau); + + if(plane.id && glIsTexture(plane.id)) + glDeleteTextures(1, &plane.id); + plane.id = 0; ++ fields[0][1].id = 0; + #endif + } + +@@ -2292,11 +2422,152 @@ + void CLinuxRendererGL::UploadVDPAUTexture(int index) + { + #ifdef HAVE_LIBVDPAU ++ VDPAU::CVdpauRenderPicture *vdpau = m_buffers[index].vdpau; ++ ++ unsigned int flipindex = m_buffers[index].flipindex; ++ YUVFIELDS &fields = m_buffers[index].fields; ++ YUVPLANE &plane = fields[0][0]; ++ ++ if (!vdpau || !vdpau->valid) ++ { ++ m_eventTexturesDone[index]->Set(); ++ m_skipRender = true; ++ return; ++ } ++ ++ fields[0][1].id = vdpau->texture[0]; ++ ++ m_eventTexturesDone[index]->Set(); ++#endif ++} ++ ++void CLinuxRendererGL::DeleteVDPAUTexture420(int index) ++{ ++#ifdef HAVE_LIBVDPAU ++ YUVPLANE &plane = m_buffers[index].fields[0][0]; ++ YUVFIELDS &fields = m_buffers[index].fields; ++ ++ SAFE_RELEASE(m_buffers[index].vdpau); ++ ++ if(plane.id && glIsTexture(plane.id)) ++ glDeleteTextures(1, &plane.id); ++ plane.id = 0; ++ fields[1][0].id = 0; ++ fields[1][1].id = 0; ++ fields[2][0].id = 0; ++ fields[2][1].id = 0; ++ ++#endif ++} ++ ++bool CLinuxRendererGL::CreateVDPAUTexture420(int index) ++{ ++#ifdef HAVE_LIBVDPAU ++ YV12Image &im = m_buffers[index].image; ++ YUVFIELDS &fields = m_buffers[index].fields; ++ YUVPLANE &plane = fields[0][0]; ++ GLuint *pbo = m_buffers[index].pbo; ++ ++ DeleteVDPAUTexture420(index); ++ ++ memset(&im , 0, sizeof(im)); ++ memset(&fields, 0, sizeof(fields)); ++ ++ im.cshift_x = 1; ++ im.cshift_y = 1; ++ ++ im.plane[0] = NULL; ++ im.plane[1] = NULL; ++ im.plane[2] = NULL; ++ ++ for(int p = 0;p<3;p++) ++ { ++ pbo[p] = None; ++ } ++ ++ glEnable(m_textureTarget); ++ glGenTextures(1, &plane.id); ++ glDisable(m_textureTarget); ++ + m_eventTexturesDone[index]->Set(); +- glPixelStorei(GL_UNPACK_ALIGNMENT,1); //what's this for? + #endif ++ return true; + } + ++void CLinuxRendererGL::UploadVDPAUTexture420(int index) ++{ ++#ifdef HAVE_LIBVDPAU ++ VDPAU::CVdpauRenderPicture *vdpau = m_buffers[index].vdpau; ++ YV12Image &im = m_buffers[index].image; ++ ++ unsigned int flipindex = m_buffers[index].flipindex; ++ YUVFIELDS &fields = m_buffers[index].fields; ++ YUVPLANE &plane = fields[0][0]; ++ ++ if (!vdpau || !vdpau->valid) ++ { ++ m_eventTexturesDone[index]->Set(); ++ m_skipRender = true; ++ return; ++ } ++ ++ im.height = vdpau->texHeight; ++ im.width = vdpau->texWidth; ++ ++ // YUV ++ for (int f = FIELD_FULL; f<=FIELD_BOT ; f++) ++ { ++ int fieldshift = (f==FIELD_FULL) ? 0 : 1; ++ YUVPLANES &planes = fields[f]; ++ ++ planes[0].texwidth = im.width; ++ planes[0].texheight = im.height >> fieldshift; ++ ++ planes[1].texwidth = planes[0].texwidth >> im.cshift_x; ++ planes[1].texheight = planes[0].texheight >> im.cshift_y; ++ planes[2].texwidth = planes[1].texwidth; ++ planes[2].texheight = planes[1].texheight; ++ ++ for (int p = 0; p < 3; p++) ++ { ++ planes[p].pixpertex_x = 1; ++ planes[p].pixpertex_y = 1; ++ } ++ } ++ // crop ++// m_sourceRect.x1 += vdpau->crop.x1; ++// m_sourceRect.x2 -= vdpau->crop.x2; ++// m_sourceRect.y1 += vdpau->crop.y1; ++// m_sourceRect.y2 -= vdpau->crop.y2; ++ ++ // set textures ++ fields[1][0].id = vdpau->texture[0]; ++ fields[1][1].id = vdpau->texture[2]; ++ fields[2][0].id = vdpau->texture[1]; ++ fields[2][1].id = vdpau->texture[3]; ++ ++ glEnable(m_textureTarget); ++ for (int f = 1; f < 3; f++) ++ { ++ for (int p=0;p<2;p++) ++ { ++ glBindTexture(m_textureTarget,fields[f][p].id); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ++ glTexParameteri(m_textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ++ ++ glBindTexture(m_textureTarget,0); ++ VerifyGLState(); ++ } ++ fields[f][2].id = fields[f][1].id; ++ } ++ CalculateTextureSourceRects(index, 3); ++ glDisable(m_textureTarget); ++ ++ m_eventTexturesDone[index]->Set(); ++#endif ++} + + void CLinuxRendererGL::DeleteVAAPITexture(int index) + { +@@ -2428,7 +2699,7 @@ + || status == VA_STATUS_ERROR_INVALID_DISPLAY) + { + va.display->lost(true); +- for(int i = 0; i < NUM_BUFFERS; i++) ++ for(int i = 0; i < m_NumYV12Buffers; i++) + { + m_buffers[i].vaapi.display.reset(); + m_buffers[i].vaapi.surface.reset(); +@@ -2576,6 +2847,93 @@ + return true; + } + ++void CLinuxRendererGL::DeleteXVBATexture(int index) ++{ ++#ifdef HAVE_LIBXVBA ++ YUVPLANE &plane = m_buffers[index].fields[0][0]; ++ YUVFIELDS &fields = m_buffers[index].fields; ++ ++ SAFE_RELEASE(m_buffers[index].xvba); ++ ++ if(plane.id && glIsTexture(plane.id)) ++ glDeleteTextures(1, &plane.id); ++ plane.id = 0; ++ fields[0][1].id = 0; ++#endif ++} ++ ++bool CLinuxRendererGL::CreateXVBATexture(int index) ++{ ++#ifdef HAVE_LIBXVBA ++ YV12Image &im = m_buffers[index].image; ++ YUVFIELDS &fields = m_buffers[index].fields; ++ YUVPLANE &plane = fields[0][0]; ++ ++ DeleteXVBATexture(index); ++ ++ memset(&im , 0, sizeof(im)); ++ memset(&fields, 0, sizeof(fields)); ++ ++ glGenTextures(1, &plane.id); ++ ++ m_eventTexturesDone[index]->Set(); ++#endif ++ return true; ++} ++ ++void CLinuxRendererGL::UploadXVBATexture(int index) ++{ ++#ifdef HAVE_LIBXVBA ++ XVBA::CXvbaRenderPicture *xvba = m_buffers[index].xvba; ++ YV12Image &im = m_buffers[index].image; ++ ++ YUVFIELDS &fields = m_buffers[index].fields; ++ YUVPLANE &plane = fields[0][1]; ++ ++ if (!xvba) ++ { ++ fields[0][1].id = fields[0][0].id; ++ m_eventTexturesDone[index]->Set(); ++ CLog::Log(LOGWARNING,"CLinuxRendererGL::UploadXVBATexture no xvba texture, index: %d", index); ++ return; ++ } ++// xvba->Transfer(); ++ ++ fields[0][1].id = xvba->texture; ++ ++ im.height = xvba->texHeight; ++ im.width = xvba->texWidth; ++ ++ plane.texwidth = xvba->texWidth; ++ plane.texheight = xvba->texHeight; ++ plane.pixpertex_x = 1; ++ plane.pixpertex_y = 1; ++ ++ plane.rect = m_sourceRect; ++ plane.width = im.width; ++ plane.height = im.height; ++ ++ plane.height /= plane.pixpertex_y; ++ plane.rect.y1 /= plane.pixpertex_y; ++ plane.rect.y2 /= plane.pixpertex_y; ++ plane.width /= plane.pixpertex_x; ++ plane.rect.x1 /= plane.pixpertex_x; ++ plane.rect.x2 /= plane.pixpertex_x; ++ ++ if (m_textureTarget == GL_TEXTURE_2D) ++ { ++ plane.height /= plane.texheight; ++ plane.rect.y1 /= plane.texheight; ++ plane.rect.y2 /= plane.texheight; ++ plane.width /= plane.texwidth; ++ plane.rect.x1 /= plane.texwidth; ++ plane.rect.x2 /= plane.texwidth; ++ } ++ ++ m_eventTexturesDone[index]->Set(); ++#endif ++} ++ + void CLinuxRendererGL::UploadYUV422PackedTexture(int source) + { + YUVBUFFER& buf = m_buffers[source]; +@@ -3161,6 +3519,9 @@ + if (m_renderMethod & RENDER_VAAPI) + return false; + ++ if (m_renderMethod & RENDER_XVBA) ++ return false; ++ + return (m_renderMethod & RENDER_GLSL) + || (m_renderMethod & RENDER_ARB) + || ((m_renderMethod & RENDER_SW) && glewIsSupported("GL_ARB_imaging") == GL_TRUE); +@@ -3174,6 +3535,9 @@ + if (m_renderMethod & RENDER_VAAPI) + return false; + ++ if (m_renderMethod & RENDER_XVBA) ++ return false; ++ + return (m_renderMethod & RENDER_GLSL) + || (m_renderMethod & RENDER_ARB) + || ((m_renderMethod & RENDER_SW) && glewIsSupported("GL_ARB_imaging") == GL_TRUE); +@@ -3197,7 +3561,8 @@ + if (feature == RENDERFEATURE_NONLINSTRETCH) + { + if (((m_renderMethod & RENDER_GLSL) && !(m_renderMethod & RENDER_POT)) || +- (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI)) ++ (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI) || ++ (m_renderMethod & RENDER_XVBA)) + return true; + } + +@@ -3233,12 +3598,13 @@ + if(method == VS_INTERLACEMETHOD_AUTO) + return true; + +- if(m_renderMethod & RENDER_VDPAU) ++ if(m_renderMethod & RENDER_VDPAU || ++ m_format == RENDER_FMT_VDPAU_420) + { + #ifdef HAVE_LIBVDPAU +- CVDPAU *vdpau = m_buffers[m_iYV12RenderBuffer].vdpau; +- if(vdpau) +- return vdpau->Supports(method); ++ VDPAU::CVdpauRenderPicture *vdpauPic = m_buffers[m_iYV12RenderBuffer].vdpau; ++ if(vdpauPic && vdpauPic->vdpau) ++ return vdpauPic->vdpau->Supports(method); + #endif + return false; + } +@@ -3262,6 +3628,16 @@ + return false; + } + ++ if(m_renderMethod & RENDER_XVBA) ++ { ++#ifdef HAVE_LIBXVBA ++ XVBA::CXvbaRenderPicture *xvba = m_buffers[m_iYV12RenderBuffer].xvba; ++ if(xvba) ++ return xvba->xvba->Supports(method); ++#endif ++ return false; ++ } ++ + #ifdef TARGET_DARWIN + // YADIF too slow for HD but we have no methods to fall back + // to something that works so just turn it off. +@@ -3304,7 +3680,7 @@ + || method == VS_SCALINGMETHOD_LANCZOS3) + { + if ((glewIsSupported("GL_EXT_framebuffer_object") && (m_renderMethod & RENDER_GLSL)) || +- (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI)) ++ (m_renderMethod & RENDER_VDPAU) || (m_renderMethod & RENDER_VAAPI) || (m_renderMethod & RENDER_XVBA)) + { + // spline36 and lanczos3 are only allowed through advancedsettings.xml + if(method != VS_SCALINGMETHOD_SPLINE36 +@@ -3324,14 +3700,7 @@ + return VS_INTERLACEMETHOD_NONE; + + if(m_renderMethod & RENDER_VDPAU) +- { +-#ifdef HAVE_LIBVDPAU +- CVDPAU *vdpau = m_buffers[m_iYV12RenderBuffer].vdpau; +- if(vdpau) +- return vdpau->AutoInterlaceMethod(); +-#endif + return VS_INTERLACEMETHOD_NONE; +- } + + if(Supports(VS_INTERLACEMETHOD_RENDER_BOB)) + return VS_INTERLACEMETHOD_RENDER_BOB; +@@ -3374,26 +3743,27 @@ + } + + #ifdef HAVE_LIBVDPAU +-void CLinuxRendererGL::AddProcessor(CVDPAU* vdpau) ++void CLinuxRendererGL::AddProcessor(VDPAU::CVdpauRenderPicture *vdpau, int index) + { +- YUVBUFFER &buf = m_buffers[NextYV12Texture()]; ++ YUVBUFFER &buf = m_buffers[index]; ++ VDPAU::CVdpauRenderPicture *pic = vdpau->Acquire(); + SAFE_RELEASE(buf.vdpau); +- buf.vdpau = (CVDPAU*)vdpau->Acquire(); ++ buf.vdpau = pic; + } + #endif + + #ifdef HAVE_LIBVA +-void CLinuxRendererGL::AddProcessor(VAAPI::CHolder& holder) ++void CLinuxRendererGL::AddProcessor(VAAPI::CHolder& holder, int index) + { +- YUVBUFFER &buf = m_buffers[NextYV12Texture()]; ++ YUVBUFFER &buf = m_buffers[index]; + buf.vaapi.surface = holder.surface; + } + #endif + + #ifdef TARGET_DARWIN +-void CLinuxRendererGL::AddProcessor(struct __CVBuffer *cvBufferRef) ++void CLinuxRendererGL::AddProcessor(struct __CVBuffer *cvBufferRef, int index) + { +- YUVBUFFER &buf = m_buffers[NextYV12Texture()]; ++ YUVBUFFER &buf = m_buffers[index]; + if (buf.cvBufferRef) + CVBufferRelease(buf.cvBufferRef); + buf.cvBufferRef = cvBufferRef; +@@ -3402,4 +3772,14 @@ + } + #endif + ++#ifdef HAVE_LIBXVBA ++void CLinuxRendererGL::AddProcessor(XVBA::CXvbaRenderPicture* xvba, int index) ++{ ++ YUVBUFFER &buf = m_buffers[index]; ++ XVBA::CXvbaRenderPicture *pic = xvba->Acquire(); ++ SAFE_RELEASE(buf.xvba); ++ buf.xvba = pic; ++} ++#endif ++ + #endif +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp 2012-10-03 03:02:11.353940770 +0200 +@@ -1913,16 +1913,16 @@ + } + + #ifdef HAVE_LIBOPENMAX +-void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture) ++void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index) + { +- YUVBUFFER &buf = m_buffers[NextYV12Texture()]; ++ YUVBUFFER &buf = m_buffers[index]; + buf.openMaxBuffer = picture->openMaxBuffer; + } + #endif + #ifdef HAVE_VIDEOTOOLBOXDECODER +-void CLinuxRendererGLES::AddProcessor(struct __CVBuffer *cvBufferRef) ++void CLinuxRendererGLES::AddProcessor(struct __CVBuffer *cvBufferRef, int index) + { +- YUVBUFFER &buf = m_buffers[NextYV12Texture()]; ++ YUVBUFFER &buf = m_buffers[index]; + if (buf.cvBufferRef) + CVBufferRelease(buf.cvBufferRef); + buf.cvBufferRef = cvBufferRef; +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGLES.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGLES.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGLES.h 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGLES.h 2012-10-03 03:02:11.354940790 +0200 +@@ -152,10 +152,10 @@ + virtual std::vector SupportedFormats() { return m_formats; } + + #ifdef HAVE_LIBOPENMAX +- virtual void AddProcessor(COpenMax* openMax, DVDVideoPicture *picture); ++ virtual void AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index); + #endif + #ifdef HAVE_VIDEOTOOLBOXDECODER +- virtual void AddProcessor(struct __CVBuffer *cvBufferRef); ++ virtual void AddProcessor(struct __CVBuffer *cvBufferRef, int index); + #endif + + protected: +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGL.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGL.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/LinuxRendererGL.h 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/LinuxRendererGL.h 2012-10-03 03:02:11.411941948 +0200 +@@ -38,13 +38,14 @@ + + class CRenderCapture; + +-class CVDPAU; + class CBaseTexture; + namespace Shaders { class BaseYUV2RGBShader; } + namespace Shaders { class BaseVideoFilterShader; } + namespace VAAPI { struct CHolder; } ++namespace VDPAU { class CVdpauRenderPicture; } ++namespace XVBA { class CXvbaRenderPicture; } + +-#define NUM_BUFFERS 3 ++#define NUM_BUFFERS 10 + + + #undef ALIGN +@@ -91,6 +92,7 @@ + RENDER_POT=0x10, + RENDER_VAAPI=0x20, + RENDER_CVREF = 0x40, ++ RENDER_XVBA=0x80, + }; + + enum RenderQuality +@@ -138,17 +140,23 @@ + virtual void UnInit(); + virtual void Reset(); /* resets renderer after seek for example */ + virtual void Flush(); ++ virtual void ReleaseBuffer(int idx); ++ virtual void SetProcessorSize(int numBuffers) { m_NumYV12Buffers = numBuffers; } ++ virtual unsigned int GetMaxProcessorSize() { return NUM_BUFFERS; } ++ virtual unsigned int GetProcessorSize() { return m_NumYV12Buffers; } + + #ifdef HAVE_LIBVDPAU +- virtual void AddProcessor(CVDPAU* vdpau); ++ virtual void AddProcessor(VDPAU::CVdpauRenderPicture* vdpau, int index); + #endif + #ifdef HAVE_LIBVA +- virtual void AddProcessor(VAAPI::CHolder& holder); ++ virtual void AddProcessor(VAAPI::CHolder& holder, int index); + #endif + #ifdef TARGET_DARWIN +- virtual void AddProcessor(struct __CVBuffer *cvBufferRef); ++ virtual void AddProcessor(struct __CVBuffer *cvBufferRef, int index); ++#endif ++#ifdef HAVE_LIBXVBA ++ virtual void AddProcessor(XVBA::CXvbaRenderPicture* xvba, int index); + #endif +- + virtual void RenderUpdate(bool clear, DWORD flags = 0, DWORD alpha = 255); + + // Feature support +@@ -192,6 +200,10 @@ + void DeleteVDPAUTexture(int index); + bool CreateVDPAUTexture(int index); + ++ void UploadVDPAUTexture420(int index); ++ void DeleteVDPAUTexture420(int index); ++ bool CreateVDPAUTexture420(int index); ++ + void UploadVAAPITexture(int index); + void DeleteVAAPITexture(int index); + bool CreateVAAPITexture(int index); +@@ -204,6 +216,10 @@ + void DeleteYUV422PackedTexture(int index); + bool CreateYUV422PackedTexture(int index); + ++ void UploadXVBATexture(int index); ++ void DeleteXVBATexture(int index); ++ bool CreateXVBATexture(int index); ++ + void UploadRGBTexture(int index); + void ToRGBFrame(YV12Image* im, unsigned flipIndexPlane, unsigned flipIndexBuf); + void ToRGBFields(YV12Image* im, unsigned flipIndexPlaneTop, unsigned flipIndexPlaneBot, unsigned flipIndexBuf); +@@ -216,7 +232,9 @@ + void RenderSinglePass(int renderBuffer, int field); // single pass glsl renderer + void RenderSoftware(int renderBuffer, int field); // single pass s/w yuv2rgb renderer + void RenderVDPAU(int renderBuffer, int field); // render using vdpau hardware ++ void RenderVDPAUYV12(int renderBuffer, int field); // render using vdpau hardware + void RenderVAAPI(int renderBuffer, int field); // render using vdpau hardware ++ void RenderXVBA(int renderBuffer, int field); // render using xvba hardware + + CFrameBufferObject m_fbo; + +@@ -272,7 +290,7 @@ + GLuint pbo[MAX_PLANES]; + + #ifdef HAVE_LIBVDPAU +- CVDPAU* vdpau; ++ VDPAU::CVdpauRenderPicture *vdpau; + #endif + #ifdef HAVE_LIBVA + VAAPI::CHolder& vaapi; +@@ -280,6 +298,9 @@ + #ifdef TARGET_DARWIN_OSX + struct __CVBuffer *cvBufferRef; + #endif ++#ifdef HAVE_LIBXVBA ++ XVBA::CXvbaRenderPicture *xvba; ++#endif + }; + + typedef YUVBUFFER YUVBUFFERS[NUM_BUFFERS]; +@@ -318,6 +339,7 @@ + bool m_nonLinStretch; + bool m_nonLinStretchGui; + float m_pixelRatio; ++ bool m_skipRender; + }; + + +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/OverlayRenderer.cpp xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/OverlayRenderer.cpp +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/OverlayRenderer.cpp 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/OverlayRenderer.cpp 2012-10-03 03:02:11.354940790 +0200 +@@ -89,7 +89,7 @@ + CRenderer::CRenderer() + { + m_render = 0; +- m_decode = (m_render + 1) % 2; ++// m_decode = (m_render + 1) % 2; + } + + CRenderer::~CRenderer() +@@ -151,9 +151,10 @@ + { + CSingleLock lock(m_section); + +- for(int i = 0; i < 2; i++) ++ for(int i = 0; i < m_iNumBuffers; i++) + Release(m_buffers[i]); + ++ m_render = 0; + Release(m_cleanup); + } + +@@ -161,10 +162,23 @@ + { + CSingleLock lock(m_section); + +- m_render = m_decode; +- m_decode =(m_decode + 1) % 2; ++// m_render = m_decode; ++ m_render = (m_render + 1) % m_iNumBuffers; ++ ++// Release(m_buffers[m_decode]); ++} ++ ++void CRenderer::SetBuffer(int idx) ++{ ++ CSingleLock lock(m_section); ++ Release(m_buffers[idx]); ++ m_decode = idx; ++} + +- Release(m_buffers[m_decode]); ++void CRenderer::ReleaseBuffer(int idx) ++{ ++ CSingleLock lock(m_section); ++ Release(m_buffers[idx]); + } + + void CRenderer::Render() +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/OverlayRenderer.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/OverlayRenderer.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/OverlayRenderer.h 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/OverlayRenderer.h 2012-10-03 03:02:11.355940810 +0200 +@@ -98,6 +98,9 @@ + void Flip(); + void Render(); + void Flush(); ++ void SetNumBuffers(int numBuffers) { m_iNumBuffers = numBuffers; } ++ void SetBuffer(int idx); ++ void ReleaseBuffer(int idx); + + protected: + +@@ -124,7 +127,8 @@ + void Release(SElementV& list); + + CCriticalSection m_section; +- SElementV m_buffers[2]; ++ SElementV m_buffers[10]; ++ int m_iNumBuffers; + int m_decode; + int m_render; + +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/RenderFormats.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/RenderFormats.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/RenderFormats.h 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/RenderFormats.h 2012-10-03 03:02:11.412941968 +0200 +@@ -26,6 +26,7 @@ + RENDER_FMT_YUV420P10, + RENDER_FMT_YUV420P16, + RENDER_FMT_VDPAU, ++ RENDER_FMT_VDPAU_420, + RENDER_FMT_NV12, + RENDER_FMT_UYVY422, + RENDER_FMT_YUYV422, +@@ -34,6 +35,7 @@ + RENDER_FMT_OMXEGL, + RENDER_FMT_CVBREF, + RENDER_FMT_BYPASS, ++ RENDER_FMT_XVBA, + }; + + #endif +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/RenderManager.cpp xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/RenderManager.cpp +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/RenderManager.cpp 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/RenderManager.cpp 2012-10-03 03:02:11.413941989 +0200 +@@ -28,6 +28,7 @@ + #include "utils/MathUtils.h" + #include "threads/SingleLock.h" + #include "utils/log.h" ++#include "utils/TimeUtils.h" + + #include "Application.h" + #include "ApplicationMessenger.h" +@@ -247,6 +248,13 @@ + return false; + } + ++ // check if decoder supports buffering ++ m_bCodecSupportsBuffering = false; ++ if (format == RENDER_FMT_VDPAU ++ || format == RENDER_FMT_VDPAU_420 ++ || format == RENDER_FMT_XVBA) ++ m_bCodecSupportsBuffering = true; ++ + bool result = m_pRenderer->Configure(width, height, d_width, d_height, fps, flags, format, extended_format, orientation); + if(result) + { +@@ -261,6 +269,7 @@ + m_bReconfigured = true; + m_presentstep = PRESENT_IDLE; + m_presentevent.Set(); ++ ResetRenderBuffer(); + } + + return result; +@@ -292,8 +301,12 @@ + if (!m_pRenderer) + return; + ++ if (m_presentstep == PRESENT_IDLE) ++ PrepareNextRender(); ++ + if(m_presentstep == PRESENT_FLIP) + { ++ FlipRenderBuffer(); + m_overlays.Flip(); + m_pRenderer->FlipPage(m_presentsource); + m_presentstep = PRESENT_FRAME; +@@ -312,7 +325,7 @@ + m_presentevent.Set(); + } + +-unsigned int CXBMCRenderManager::PreInit() ++unsigned int CXBMCRenderManager::PreInit(CDVDClock *pClock) + { + CRetakeLock lock(m_sharedSection); + +@@ -320,6 +333,7 @@ + m_presenterr = 0.0; + m_errorindex = 0; + memset(m_errorbuff, 0, sizeof(m_errorbuff)); ++ m_pClock = pClock; + + m_bIsStarted = false; + m_bPauseDrawing = false; +@@ -338,6 +352,10 @@ + + UpdateDisplayLatency(); + ++ m_bUseBuffering = false; ++ m_bCodecSupportsBuffering = true; ++ ResetRenderBuffer(); ++ + return m_pRenderer->PreInit(); + } + +@@ -366,7 +384,9 @@ + + CRetakeLock lock(m_sharedSection); + m_pRenderer->Flush(); ++ m_overlays.Flush(); + m_flushEvent.Set(); ++ ResetRenderBuffer(); + } + else + { +@@ -534,25 +554,21 @@ + m_pRenderer->SetViewMode(iViewMode); + } + +-void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/) ++void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/, int speed /*= 0*/) + { +- if(timestamp - GetPresentTime() > MAXPRESENTDELAY) +- timestamp = GetPresentTime() + MAXPRESENTDELAY; +- +- /* can't flip, untill timestamp */ +- if(!g_graphicsContext.IsFullScreenVideo()) +- WaitPresentTime(timestamp); +- +- /* make sure any queued frame was fully presented */ +- double timeout = m_presenttime + 1.0; +- while(m_presentstep != PRESENT_IDLE && !bStop) ++ if (!m_bUseBuffering) + { +- if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop) ++ /* make sure any queued frame was fully presented */ ++ double timeout = m_presenttime + 1.0; ++ while(m_presentstep != PRESENT_IDLE && !bStop) + { +- CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for previous frame"); +- return; ++ if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop) ++ { ++ CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for previous frame"); ++ return; ++ } + } +- }; ++ } + + if(bStop) + return; +@@ -560,58 +576,67 @@ + { CRetakeLock lock(m_sharedSection); + if(!m_pRenderer) return; + +- m_presenttime = timestamp; +- m_presentfield = sync; +- m_presentstep = PRESENT_FLIP; +- m_presentsource = source; ++ double presenttime = timestamp; ++ EFIELDSYNC presentfield = sync; ++ EPRESENTMETHOD presentmethod; ++ + EDEINTERLACEMODE deinterlacemode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; + EINTERLACEMETHOD interlacemethod = AutoInterlaceMethodInternal(g_settings.m_currentVideoSettings.m_InterlaceMethod); + + bool invert = false; + + if (deinterlacemode == VS_DEINTERLACEMODE_OFF) +- m_presentmethod = PRESENT_METHOD_SINGLE; ++ presentmethod = PRESENT_METHOD_SINGLE; + else + { +- if (deinterlacemode == VS_DEINTERLACEMODE_AUTO && m_presentfield == FS_NONE) +- m_presentmethod = PRESENT_METHOD_SINGLE; ++ if (deinterlacemode == VS_DEINTERLACEMODE_AUTO && presentfield == FS_NONE) ++ presentmethod = PRESENT_METHOD_SINGLE; + else + { +- if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BLEND) m_presentmethod = PRESENT_METHOD_BLEND; +- else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE) m_presentmethod = PRESENT_METHOD_WEAVE; +- else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED) { m_presentmethod = PRESENT_METHOD_WEAVE ; invert = true; } +- else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB) m_presentmethod = PRESENT_METHOD_BOB; +- else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED) { m_presentmethod = PRESENT_METHOD_BOB; invert = true; } +- else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BOB) m_presentmethod = PRESENT_METHOD_BOB; +- else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BEST) m_presentmethod = PRESENT_METHOD_BOB; +- else m_presentmethod = PRESENT_METHOD_SINGLE; ++ if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BLEND) presentmethod = PRESENT_METHOD_BLEND; ++ else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE) presentmethod = PRESENT_METHOD_WEAVE; ++ else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED) { presentmethod = PRESENT_METHOD_WEAVE ; invert = true; } ++ else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB) presentmethod = PRESENT_METHOD_BOB; ++ else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED) { presentmethod = PRESENT_METHOD_BOB; invert = true; } ++ else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BOB) presentmethod = PRESENT_METHOD_BOB; ++ else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BEST) presentmethod = PRESENT_METHOD_BOB; ++ else presentmethod = PRESENT_METHOD_SINGLE; + + /* default to odd field if we want to deinterlace and don't know better */ +- if (deinterlacemode == VS_DEINTERLACEMODE_FORCE && m_presentfield == FS_NONE) +- m_presentfield = FS_TOP; ++ if (deinterlacemode == VS_DEINTERLACEMODE_FORCE && presentfield == FS_NONE) ++ presentfield = FS_TOP; + + /* invert present field */ + if(invert) + { +- if( m_presentfield == FS_BOT ) +- m_presentfield = FS_TOP; ++ if( presentfield == FS_BOT ) ++ presentfield = FS_TOP; + else +- m_presentfield = FS_BOT; ++ presentfield = FS_BOT; + } + } + } + ++ FlipFreeBuffer(); ++ m_renderBuffers[m_iOutputRenderBuffer].pts = timestamp; ++ m_renderBuffers[m_iOutputRenderBuffer].presentfield = presentfield; ++ m_renderBuffers[m_iOutputRenderBuffer].presentmethod = presentmethod; ++ m_speed = speed; + } + + g_application.NewFrame(); +- /* wait untill render thread have flipped buffers */ +- timeout = m_presenttime + 1.0; +- while(m_presentstep == PRESENT_FLIP && !bStop) ++ ++ if (!m_bUseBuffering) + { +- if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop) ++ /* wait untill render thread have flipped buffers */ ++ double timeout = m_presenttime + 1.0; ++ while(m_presentstep == PRESENT_FLIP && !bStop) + { +- CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for flip to complete"); +- return; ++ if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop) ++ { ++ CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for flip to complete"); ++ return; ++ } + } + } + } +@@ -675,8 +700,12 @@ + if (!m_pRenderer) + return; + ++ if (m_presentstep == PRESENT_IDLE) ++ PrepareNextRender(); ++ + if(m_presentstep == PRESENT_FLIP) + { ++ FlipRenderBuffer(); + m_overlays.Flip(); + m_pRenderer->FlipPage(m_presentsource); + m_presentstep = PRESENT_FRAME; +@@ -800,11 +829,11 @@ + if (!m_pRenderer) + return -1; + +- if(m_pRenderer->AddVideoPicture(&pic)) ++ if(m_pRenderer->AddVideoPicture(&pic, (m_iOutputRenderBuffer + 1) % m_iNumRenderBuffers)) + return 1; + + YV12Image image; +- int index = m_pRenderer->GetImage(&image); ++ int index = m_pRenderer->GetImage(&image, (m_iOutputRenderBuffer + 1) % m_iNumRenderBuffers); + + if(index < 0) + return index; +@@ -829,20 +858,25 @@ + CDVDCodecUtils::CopyDXVA2Picture(&image, &pic); + } + #ifdef HAVE_LIBVDPAU +- else if(pic.format == RENDER_FMT_VDPAU) +- m_pRenderer->AddProcessor(pic.vdpau); ++ else if(pic.format == RENDER_FMT_VDPAU ++ || pic.format == RENDER_FMT_VDPAU_420) ++ m_pRenderer->AddProcessor(pic.vdpau, index); + #endif + #ifdef HAVE_LIBOPENMAX + else if(pic.format == RENDER_FMT_OMXEGL) +- m_pRenderer->AddProcessor(pic.openMax, &pic); ++ m_pRenderer->AddProcessor(pic.openMax, &pic, index); + #endif + #ifdef TARGET_DARWIN + else if(pic.format == RENDER_FMT_CVBREF) +- m_pRenderer->AddProcessor(pic.cvBufferRef); ++ m_pRenderer->AddProcessor(pic.cvBufferRef, index); + #endif + #ifdef HAVE_LIBVA + else if(pic.format == RENDER_FMT_VAAPI) +- m_pRenderer->AddProcessor(*pic.vaapi); ++ m_pRenderer->AddProcessor(*pic.vaapi, index); ++#endif ++#ifdef HAVE_LIBXVBA ++ else if(pic.format == RENDER_FMT_XVBA) ++ m_pRenderer->AddProcessor(pic.xvba, index); + #endif + m_pRenderer->ReleaseImage(index, false); + +@@ -904,3 +938,200 @@ + + return mInt; + } ++ ++int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop) ++{ ++ CSharedLock lock(m_sharedSection); ++ if (!m_pRenderer) ++ return -1; ++ ++ //wait up to a second as this is our slowest allowed output rate ++ double timeout = GetPresentTime() + 0.1; ++ while(!HasFreeBuffer() && !bStop) ++ { ++ lock.Leave(); ++ m_flipEvent.WaitMSec(50); ++ if(GetPresentTime() > timeout && !bStop) ++ { ++ CLog::Log(LOGWARNING, "CRenderManager::WaitForBuffer - timeout waiting for buffer"); ++ return -1; ++ } ++ lock.Enter(); ++ } ++ lock.Leave(); ++ ++ { CRetakeLock lock(m_sharedSection); ++ m_overlays.SetBuffer((m_iOutputRenderBuffer + 1) % m_iNumRenderBuffers); ++ } ++ ++ if (bStop) ++ return -1; ++ ++ return 1; ++} ++ ++int CXBMCRenderManager::GetNextRenderBufferIndex() ++{ ++ if (m_iOutputRenderBuffer == m_iCurrentRenderBuffer) ++ return -1; ++ return (m_iCurrentRenderBuffer + 1) % m_iNumRenderBuffers; ++} ++ ++void CXBMCRenderManager::FlipRenderBuffer() ++{ ++ m_iCurrentRenderBuffer = GetNextRenderBufferIndex(); ++// CLog::Log(LOGNOTICE,"-------- flip render: %d", m_iCurrentRenderBuffer); ++} ++ ++int CXBMCRenderManager::FlipFreeBuffer() ++{ ++ // See "Render Buffer State Description" in header for information. ++ if (HasFreeBuffer()) ++ { ++ m_bAllRenderBuffersDisplayed = false; ++ m_iOutputRenderBuffer = (m_iOutputRenderBuffer + 1) % m_iNumRenderBuffers; ++ return m_iOutputRenderBuffer; ++ } ++} ++ ++bool CXBMCRenderManager::HasFreeBuffer() ++{ ++ if (!m_bUseBuffering) ++ { ++ if (m_iOutputRenderBuffer != m_iCurrentRenderBuffer) ++ return false; ++ else ++ return true; ++ } ++ ++ int outputPlus1 = (m_iOutputRenderBuffer + 1) % m_iNumRenderBuffers; ++ if ((m_iOutputRenderBuffer == m_iDisplayedRenderBuffer && !m_bAllRenderBuffersDisplayed) ++ || outputPlus1 == m_iCurrentRenderBuffer) ++ return false; ++ else ++ return true; ++} ++ ++void CXBMCRenderManager::ResetRenderBuffer() ++{ ++ m_iNumRenderBuffers = m_pRenderer->GetMaxProcessorSize(); ++ m_iNumRenderBuffers = std::min(5, m_iNumRenderBuffers); ++ m_iNumRenderBuffers = std::max(2, m_iNumRenderBuffers); ++ ++ if (!m_bCodecSupportsBuffering) ++ m_iNumRenderBuffers = 2; ++ ++ CLog::Log(LOGNOTICE,"CXBMCRenderManager::ResetRenderBuffer - using %d render buffers", m_iNumRenderBuffers); ++ m_overlays.SetNumBuffers(m_iNumRenderBuffers); ++ m_pRenderer->SetProcessorSize(m_iNumRenderBuffers); ++ ++ m_iCurrentRenderBuffer = 0; ++ m_iOutputRenderBuffer = 0; ++ m_iDisplayedRenderBuffer = 0; ++ m_bAllRenderBuffersDisplayed = true; ++ m_sleeptime = 1.0; ++ m_presentPts = DVD_NOPTS_VALUE; ++ m_speed = 0; ++} ++ ++void CXBMCRenderManager::PrepareNextRender() ++{ ++ int idx = GetNextRenderBufferIndex(); ++ if (idx < 0) ++ { ++ if (m_speed >= DVD_PLAYSPEED_NORMAL && g_graphicsContext.IsFullScreenVideo()) ++ CLog::Log(LOGDEBUG,"%s no buffer, out: %d, current: %d, display: %d", ++ __FUNCTION__, m_iOutputRenderBuffer, m_iCurrentRenderBuffer, m_iDisplayedRenderBuffer); ++ return; ++ } ++ ++ double iClockSleep, iPlayingClock, iCurrentClock; ++ iPlayingClock = m_pClock->GetClock(iCurrentClock, false); ++ iClockSleep = m_renderBuffers[idx].pts - iPlayingClock; ++ ++ if (m_speed) ++ iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; ++ ++ double presenttime = (iCurrentClock + iClockSleep) / DVD_TIME_BASE; ++ double clocktime = iCurrentClock / DVD_TIME_BASE; ++ if(presenttime - clocktime > MAXPRESENTDELAY) ++ presenttime = clocktime + MAXPRESENTDELAY; ++ ++ m_sleeptime = presenttime - clocktime; ++ ++ if (g_graphicsContext.IsFullScreenVideo() || presenttime <= clocktime+0.01) ++ { ++ m_presentPts = m_renderBuffers[idx].pts; ++ m_presenttime = presenttime; ++ m_presentmethod = m_renderBuffers[idx].presentmethod; ++ m_presentfield = m_renderBuffers[idx].presentfield; ++ m_presentstep = PRESENT_FLIP; ++ m_presentsource = idx; ++ } ++} ++ ++void CXBMCRenderManager::EnableBuffering(bool enable) ++{ ++ CRetakeLock lock(m_sharedSection); ++ ++ if (m_iNumRenderBuffers < 3) ++ return; ++ ++ m_bUseBuffering = enable; ++ if (!m_bUseBuffering) ++ m_iOutputRenderBuffer = m_iCurrentRenderBuffer; ++ ++ CLog::Log(LOGDEBUG, "CXBMCRenderManager::EnableBuffering - %d", m_bUseBuffering); ++} ++ ++void CXBMCRenderManager::DiscardBuffer() ++{ ++ CRetakeLock lock(m_sharedSection); ++ m_iOutputRenderBuffer = m_iCurrentRenderBuffer; ++} ++ ++void CXBMCRenderManager::NotifyDisplayFlip() ++{ ++ CRetakeLock lock(m_sharedSection); ++ if (!m_pRenderer) ++ return; ++ ++ if (m_iNumRenderBuffers < 3) ++ return; ++ ++ int last = m_iDisplayedRenderBuffer; ++ m_iDisplayedRenderBuffer = (m_iCurrentRenderBuffer + m_iNumRenderBuffers - 1) % m_iNumRenderBuffers; ++ ++ if (last != m_iDisplayedRenderBuffer ++ && m_iDisplayedRenderBuffer != m_iCurrentRenderBuffer) ++ { ++ m_pRenderer->ReleaseBuffer(m_iDisplayedRenderBuffer); ++ m_overlays.ReleaseBuffer(m_iDisplayedRenderBuffer); ++ } ++ ++ lock.Leave(); ++ m_flipEvent.Set(); ++} ++ ++bool CXBMCRenderManager::GetStats(double &sleeptime, double &pts, int &bufferLevel) ++{ ++ CSharedLock lock(m_sharedSection); ++ sleeptime = m_sleeptime; ++ pts = m_presentPts; ++ if (m_iNumRenderBuffers < 3) ++ bufferLevel = -1; ++ else ++ bufferLevel = (m_iOutputRenderBuffer - m_iCurrentRenderBuffer + m_iNumRenderBuffers) % m_iNumRenderBuffers; ++ return true; ++} ++ ++bool CXBMCRenderManager::HasFrame() ++{ ++ CSharedLock lock(m_sharedSection); ++ if (m_presentstep == PRESENT_IDLE && ++ GetNextRenderBufferIndex() < 0 && ++ m_speed > 0) ++ return false; ++ else ++ return true; ++} +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/RenderManager.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/RenderManager.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/RenderManager.h 2012-10-03 01:09:22.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/RenderManager.h 2012-10-03 03:02:11.368941074 +0200 +@@ -31,10 +31,11 @@ + #include "OverlayRenderer.h" + + class CRenderCapture; ++class CDVDClock; + + namespace DXVA { class CProcessor; } + namespace VAAPI { class CSurfaceHolder; } +-class CVDPAU; ++namespace VDPAU { class CVdpauRenderPicture; } + struct DVDVideoPicture; + + #define ERRORBUFFSIZE 30 +@@ -70,8 +71,8 @@ + + int AddVideoPicture(DVDVideoPicture& picture); + +- void FlipPage(volatile bool& bStop, double timestamp = 0.0, int source = -1, EFIELDSYNC sync = FS_NONE); +- unsigned int PreInit(); ++ void FlipPage(volatile bool& bStop, double timestamp = 0.0, int source = -1, EFIELDSYNC sync = FS_NONE, int speed = 0); ++ unsigned int PreInit(CDVDClock *pClock); + void UnInit(); + bool Flush(); + +@@ -131,6 +132,12 @@ + CSharedSection& GetSection() { return m_sharedSection; }; + + void RegisterRenderUpdateCallBack(const void *ctx, RenderUpdateCallBackFn fn); ++ int WaitForBuffer(volatile bool& bStop); ++ void NotifyDisplayFlip(); ++ bool GetStats(double &sleeptime, double &pts, int &bufferLevel); ++ bool HasFrame(); ++ void EnableBuffering(bool enable); ++ void DiscardBuffer(); + + protected: + void Render(bool clear, DWORD flags, DWORD alpha); +@@ -139,6 +146,13 @@ + void PresentFields(bool clear, DWORD flags, DWORD alpha); + void PresentBlend(bool clear, DWORD flags, DWORD alpha); + ++ int GetNextRenderBufferIndex(); ++ void FlipRenderBuffer(); ++ int FlipFreeBuffer(); ++ bool HasFreeBuffer(); ++ void ResetRenderBuffer(); ++ void PrepareNextRender(); ++ + EINTERLACEMETHOD AutoInterlaceMethodInternal(EINTERLACEMETHOD mInt); + + bool m_bPauseDrawing; // true if we should pause rendering +@@ -169,6 +183,37 @@ + double m_displayLatency; + void UpdateDisplayLatency(); + ++ // Render Buffer State Description: ++ // ++ // Output: is the buffer about to or having its texture prepared for render (ie from output thread). ++ // Cannot go past the "Displayed" buffer (otherwise we will probably overwrite buffers not yet ++ // displayed or even rendered). ++ // Current: is the current buffer being or having been submitted for render to back buffer. ++ // Cannot go past "Output" buffer (else it would be rendering old output). ++ // Displayed: is the buffer that is now considered to be safely copied from back buffer to front buffer ++ // (we assume that after two swap-buffer flips for the same "Current" render buffer that that ++ // buffer will be safe, but otherwise we consider that only the previous-to-"Current" is guaranteed). ++ ++ int m_iCurrentRenderBuffer; ++ int m_iNumRenderBuffers; ++ int m_iOutputRenderBuffer; ++ int m_iDisplayedRenderBuffer; ++ bool m_bAllRenderBuffersDisplayed; ++ bool m_bUseBuffering; ++ bool m_bCodecSupportsBuffering; ++ int m_speed; ++ CEvent m_flipEvent; ++ ++ struct ++ { ++ double pts; ++ EFIELDSYNC presentfield; ++ EPRESENTMETHOD presentmethod; ++ }m_renderBuffers[5]; ++ ++ double m_sleeptime; ++ double m_presentPts; ++ + double m_presenttime; + double m_presentcorr; + double m_presenterr; +@@ -180,6 +225,7 @@ + int m_presentsource; + CEvent m_presentevent; + CEvent m_flushEvent; ++ CDVDClock *m_pClock; + + + OVERLAY::CRenderer m_overlays; +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/VideoShaders/YUV2RGBShader.cpp xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/VideoShaders/YUV2RGBShader.cpp +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/VideoShaders/YUV2RGBShader.cpp 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/VideoShaders/YUV2RGBShader.cpp 2012-10-03 03:02:11.327940242 +0200 +@@ -201,6 +201,8 @@ + m_defines += "#define XBMC_YUY2\n"; + else if (m_format == RENDER_FMT_UYVY422) + m_defines += "#define XBMC_UYVY\n"; ++ else if (RENDER_FMT_VDPAU_420) ++ m_defines += "#define XBMC_VDPAU_NV12\n"; + else + CLog::Log(LOGERROR, "GL: BaseYUV2RGBGLSLShader - unsupported format %d", m_format); + +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/WinRenderer.cpp xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/WinRenderer.cpp +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/WinRenderer.cpp 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/WinRenderer.cpp 2012-10-03 03:02:11.358940871 +0200 +@@ -253,12 +253,12 @@ + return -1; + } + +-bool CWinRenderer::AddVideoPicture(DVDVideoPicture* picture) ++bool CWinRenderer::AddVideoPicture(DVDVideoPicture* picture, int index) + { + if (m_renderMethod == RENDER_DXVA) + { +- int source = NextYV12Texture(); +- if(source < 0) ++ int source = index; ++ if(source < 0 || NextYV12Texture() < 0) + return false; + + DXVABuffer *buf = (DXVABuffer*)m_VideoBuffers[source]; +@@ -274,7 +274,7 @@ + if( source == AUTOSOURCE ) + source = NextYV12Texture(); + +- if( source < 0 ) ++ if( source < 0 || NextYV12Texture() < 0) + return -1; + + YUVBuffer *buf = (YUVBuffer*)m_VideoBuffers[source]; +diff -Naur xbmc-6cf5b9d/xbmc/cores/VideoRenderers/WinRenderer.h xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/WinRenderer.h +--- xbmc-6cf5b9d/xbmc/cores/VideoRenderers/WinRenderer.h 2012-10-03 01:09:21.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/cores/VideoRenderers/WinRenderer.h 2012-10-03 03:02:11.358940871 +0200 +@@ -157,7 +157,7 @@ + virtual bool Configure(unsigned int width, unsigned int height, unsigned int d_width, unsigned int d_height, float fps, unsigned flags, ERenderFormat format, unsigned extended_format, unsigned int orientation); + virtual int GetImage(YV12Image *image, int source = AUTOSOURCE, bool readonly = false); + virtual void ReleaseImage(int source, bool preserve = false); +- virtual bool AddVideoPicture(DVDVideoPicture* picture); ++ virtual bool AddVideoPicture(DVDVideoPicture* picture, int index); + virtual void FlipPage(int source); + virtual unsigned int PreInit(); + virtual void UnInit(); +diff -Naur xbmc-6cf5b9d/xbmc/guilib/GUIFontTTFGL.cpp xbmc-6cf5b9d.patch/xbmc/guilib/GUIFontTTFGL.cpp +--- xbmc-6cf5b9d/xbmc/guilib/GUIFontTTFGL.cpp 2012-10-03 01:09:14.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/guilib/GUIFontTTFGL.cpp 2012-10-03 03:02:11.439942518 +0200 +@@ -23,6 +23,7 @@ + #include "GUIFontTTFGL.h" + #include "GUIFontManager.h" + #include "Texture.h" ++#include "TextureManager.h" + #include "GraphicContext.h" + #include "gui3d.h" + #include "utils/log.h" +@@ -234,7 +235,7 @@ + if (m_bTextureLoaded) + { + if (glIsTexture(m_nTexture)) +- glDeleteTextures(1, (GLuint*) &m_nTexture); ++ g_TextureManager.ReleaseHwTexture(m_nTexture); + m_bTextureLoaded = false; + } + } +diff -Naur xbmc-6cf5b9d/xbmc/guilib/TextureManager.cpp xbmc-6cf5b9d.patch/xbmc/guilib/TextureManager.cpp +--- xbmc-6cf5b9d/xbmc/guilib/TextureManager.cpp 2012-10-03 01:09:15.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/guilib/TextureManager.cpp 2012-10-03 03:02:11.439942518 +0200 +@@ -459,6 +459,19 @@ + for (ivecTextures i = m_unusedTextures.begin(); i != m_unusedTextures.end(); ++i) + delete *i; + m_unusedTextures.clear(); ++ ++#if defined(HAS_GL) || defined(HAS_GLES) ++ for (unsigned int i = 0; i < m_unusedHwTextures.size(); ++i) ++ { ++ glDeleteTextures(1, (GLuint*) &m_unusedHwTextures[i]); ++ } ++#endif ++ m_unusedHwTextures.clear(); ++} ++ ++void CGUITextureManager::ReleaseHwTexture(unsigned int texture) ++{ ++ m_unusedHwTextures.push_back(texture); + } + + void CGUITextureManager::Cleanup() +diff -Naur xbmc-6cf5b9d/xbmc/guilib/TextureManager.h xbmc-6cf5b9d.patch/xbmc/guilib/TextureManager.h +--- xbmc-6cf5b9d/xbmc/guilib/TextureManager.h 2012-10-03 01:09:13.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/guilib/TextureManager.h 2012-10-03 03:02:11.476943270 +0200 +@@ -123,9 +123,11 @@ + void RemoveTexturePath(const CStdString &texturePath); ///< Remove a path from the paths to check when loading media + + void FreeUnusedTextures(); ///< Free textures (called from app thread only) ++ void ReleaseHwTexture(unsigned int texture); + protected: + std::vector m_vecTextures; + std::vector m_unusedTextures; ++ std::vector m_unusedHwTextures; + typedef std::vector::iterator ivecTextures; + // we have 2 texture bundles (one for the base textures, one for the theme) + CTextureBundle m_TexBundle[2]; +diff -Naur xbmc-6cf5b9d/xbmc/input/XBMC_keytable.cpp xbmc-6cf5b9d.patch/xbmc/input/XBMC_keytable.cpp +--- xbmc-6cf5b9d/xbmc/input/XBMC_keytable.cpp 2012-10-03 01:09:20.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/input/XBMC_keytable.cpp 2012-10-03 03:02:11.290939489 +0200 +@@ -179,6 +179,8 @@ + , { XBMCK_LAUNCH_APP2, 0, 0, XBMCVK_LAUNCH_APP2, "launch_app2_pc_icon" } + , { XBMCK_LAUNCH_FILE_BROWSER, 0, 0, XBMCVK_LAUNCH_FILE_BROWSER, "launch_file_browser" } + , { XBMCK_LAUNCH_MEDIA_CENTER, 0, 0, XBMCVK_LAUNCH_MEDIA_CENTER, "launch_media_center" } ++, { XBMCK_PLAY, 0, 0, XBMCVK_MEDIA_PLAY_PAUSE, "play_pause" } ++, { XBMCK_STOP, 0, 0, XBMCVK_MEDIA_STOP, "stop" } + + // Function keys + , { XBMCK_F1, 0, 0, XBMCVK_F1, "f1"} +diff -Naur xbmc-6cf5b9d/xbmc/rendering/gl/RenderSystemGL.h xbmc-6cf5b9d.patch/xbmc/rendering/gl/RenderSystemGL.h +--- xbmc-6cf5b9d/xbmc/rendering/gl/RenderSystemGL.h 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/rendering/gl/RenderSystemGL.h 2012-10-03 03:02:11.264938961 +0200 +@@ -44,6 +44,7 @@ + virtual bool IsExtSupported(const char* extension); + + virtual void SetVSync(bool vsync); ++ virtual void ResetVSync() { m_bVsyncInit = false; } + + virtual void SetViewPort(CRect& viewPort); + virtual void GetViewPort(CRect& viewPort); +diff -Naur xbmc-6cf5b9d/xbmc/settings/AdvancedSettings.cpp xbmc-6cf5b9d.patch/xbmc/settings/AdvancedSettings.cpp +--- xbmc-6cf5b9d/xbmc/settings/AdvancedSettings.cpp 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/AdvancedSettings.cpp 2012-10-03 03:02:11.515944063 +0200 +@@ -98,7 +98,7 @@ + m_videoIgnoreSecondsAtStart = 3*60; + m_videoIgnorePercentAtEnd = 8.0f; + m_videoPlayCountMinimumPercent = 90.0f; +- m_videoVDPAUScaling = false; ++ m_videoVDPAUScaling = -1; + m_videoNonLinStretchRatio = 0.5f; + m_videoEnableHighQualityHwScalers = false; + m_videoAutoScaleMaxFps = 30.0f; +@@ -106,6 +106,10 @@ + m_videoAllowMpeg4VAAPI = false; + m_videoDisableBackgroundDeinterlace = false; + m_videoCaptureUseOcclusionQuery = -1; //-1 is auto detect ++ m_videoVDPAUdeintHD = -1; ++ m_videoVDPAUdeintSD = -1; ++ m_videoVDPAUtelecine = false; ++ m_videoVDPAUdeintSkipChromaHD = false; + m_DXVACheckCompatibility = false; + m_DXVACheckCompatibilityPresent = false; + m_DXVAForceProcessorRenderer = true; +@@ -489,7 +493,7 @@ + XMLUtils::GetString(pElement,"cleandatetime", m_videoCleanDateTimeRegExp); + XMLUtils::GetString(pElement,"ppffmpegdeinterlacing",m_videoPPFFmpegDeint); + XMLUtils::GetString(pElement,"ppffmpegpostprocessing",m_videoPPFFmpegPostProc); +- XMLUtils::GetBoolean(pElement,"vdpauscaling",m_videoVDPAUScaling); ++ XMLUtils::GetInt(pElement,"vdpauscaling",m_videoVDPAUScaling); + XMLUtils::GetFloat(pElement, "nonlinearstretchratio", m_videoNonLinStretchRatio, 0.01f, 1.0f); + XMLUtils::GetBoolean(pElement,"enablehighqualityhwscalers", m_videoEnableHighQualityHwScalers); + XMLUtils::GetFloat(pElement,"autoscalemaxfps",m_videoAutoScaleMaxFps, 0.0f, 1000.0f); +@@ -497,6 +501,10 @@ + XMLUtils::GetBoolean(pElement,"allowmpeg4vaapi",m_videoAllowMpeg4VAAPI); + XMLUtils::GetBoolean(pElement, "disablebackgrounddeinterlace", m_videoDisableBackgroundDeinterlace); + XMLUtils::GetInt(pElement, "useocclusionquery", m_videoCaptureUseOcclusionQuery, -1, 1); ++ XMLUtils::GetInt(pElement,"vdpauHDdeint",m_videoVDPAUdeintHD); ++ XMLUtils::GetInt(pElement,"vdpauSDdeint",m_videoVDPAUdeintSD); ++ XMLUtils::GetBoolean(pElement,"vdpauInvTelecine",m_videoVDPAUtelecine); ++ XMLUtils::GetBoolean(pElement,"vdpauHDdeintSkipChroma",m_videoVDPAUdeintSkipChromaHD); + + TiXmlElement* pAdjustRefreshrate = pElement->FirstChildElement("adjustrefreshrate"); + if (pAdjustRefreshrate) +diff -Naur xbmc-6cf5b9d/xbmc/settings/AdvancedSettings.h xbmc-6cf5b9d.patch/xbmc/settings/AdvancedSettings.h +--- xbmc-6cf5b9d/xbmc/settings/AdvancedSettings.h 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/AdvancedSettings.h 2012-10-03 03:02:11.516944083 +0200 +@@ -133,6 +133,10 @@ + int m_videoPercentSeekBackwardBig; + CStdString m_videoPPFFmpegDeint; + CStdString m_videoPPFFmpegPostProc; ++ int m_videoVDPAUdeintHD; ++ int m_videoVDPAUdeintSD; ++ bool m_videoVDPAUtelecine; ++ bool m_videoVDPAUdeintSkipChromaHD; + bool m_musicUseTimeSeeking; + int m_musicTimeSeekForward; + int m_musicTimeSeekBackward; +@@ -148,7 +152,7 @@ + CStdString m_audioHost; + bool m_audioApplyDrc; + +- bool m_videoVDPAUScaling; ++ int m_videoVDPAUScaling; + float m_videoNonLinStretchRatio; + bool m_videoEnableHighQualityHwScalers; + float m_videoAutoScaleMaxFps; +diff -Naur xbmc-6cf5b9d/xbmc/settings/GUISettings.cpp xbmc-6cf5b9d.patch/xbmc/settings/GUISettings.cpp +--- xbmc-6cf5b9d/xbmc/settings/GUISettings.cpp 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/GUISettings.cpp 2012-10-03 03:02:11.421942152 +0200 +@@ -392,11 +392,16 @@ + AddGroup(SETTINGS_SYSTEM, 13000); + CSettingsCategory* vs = AddCategory(SETTINGS_SYSTEM, "videoscreen", 21373); + ++#if defined(HAS_GLX) ++ AddString(vs, "videoscreen.monitor", 246, "", SPIN_CONTROL_TEXT); ++#endif ++ + // this setting would ideally not be saved, as its value is systematically derived from videoscreen.screenmode. + // contains a DISPLAYMODE + #if !defined(TARGET_DARWIN_IOS_ATV2) && !defined(TARGET_RASPBERRY_PI) + AddInt(vs, "videoscreen.screen", 240, 0, -1, 1, 32, SPIN_CONTROL_TEXT); + #endif ++ + // this setting would ideally not be saved, as its value is systematically derived from videoscreen.screenmode. + // contains an index to the g_settings.m_ResInfo array. the only meaningful fields are iScreen, iWidth, iHeight. + #if defined(TARGET_DARWIN) +@@ -680,10 +685,15 @@ + + #ifdef HAVE_LIBVDPAU + AddBool(vp, "videoplayer.usevdpau", 13425, true); ++ AddBool(vp, "videoplayer.usevdpauinterop", 13433, true); ++ AddBool(vp, "videoplayer.usevdpauinteropyuv", 13434, true); + #endif + #ifdef HAVE_LIBVA + AddBool(vp, "videoplayer.usevaapi", 13426, true); + #endif ++#ifdef HAVE_LIBXVBA ++ AddBool(vp, "videoplayer.usexvba", 13435, true); ++#endif + #ifdef HAS_DX + AddBool(g_sysinfo.IsVistaOrHigher() ? vp: NULL, "videoplayer.usedxva2", 13427, g_sysinfo.IsVistaOrHigher() ? true : false); + #endif +diff -Naur xbmc-6cf5b9d/xbmc/settings/GUIWindowSettingsCategory.cpp xbmc-6cf5b9d.patch/xbmc/settings/GUIWindowSettingsCategory.cpp +--- xbmc-6cf5b9d/xbmc/settings/GUIWindowSettingsCategory.cpp 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/GUIWindowSettingsCategory.cpp 2012-10-03 03:02:11.343940566 +0200 +@@ -523,6 +523,12 @@ + FillInRefreshRates(strSetting, g_guiSettings.GetResolution(), false); + continue; + } ++ else if (strSetting.Equals("videoscreen.monitor")) ++ { ++ AddSetting(pSetting, group->GetWidth(), iControlID); ++ FillInMonitors(strSetting); ++ continue; ++ } + else if (strSetting.Equals("lookandfeel.skintheme")) + { + AddSetting(pSetting, group->GetWidth(), iControlID); +@@ -591,6 +597,40 @@ + pControl->SetEnabled(true); + } + } ++ else if (strSetting.Equals("videoplayer.usevdpauinteropyuv")) ++ { ++ bool hasInterop = g_guiSettings.GetBool("videoplayer.usevdpauinterop"); ++#ifndef GL_NV_vdpau_interop ++ hasInterop = false; ++#endif ++ CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); ++ if (pControl && hasInterop && glewIsSupported("GL_NV_vdpau_interop")) ++ { ++ pControl->SetEnabled(true); ++ } ++ else ++ { ++ pControl->SetEnabled(false); ++ g_guiSettings.SetBool("videoplayer.usevdpauinteropyuv",false); ++ } ++ } ++ else if (strSetting.Equals("videoplayer.usevdpauinterop")) ++ { ++ bool hasInterop = g_guiSettings.GetBool("videoplayer.usevdpau"); ++#ifndef GL_NV_vdpau_interop ++ hasInterop = false; ++#endif ++ CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); ++ if (pControl && hasInterop && glewIsSupported("GL_NV_vdpau_interop")) ++ { ++ pControl->SetEnabled(true); ++ } ++ else ++ { ++ pControl->SetEnabled(false); ++ g_guiSettings.SetBool("videoplayer.usevdpauinterop",false); ++ } ++ } + else + #endif + if (strSetting.Equals("videoscreen.resolution")) +@@ -1444,6 +1484,20 @@ + // Cascade + FillInResolutions("videoscreen.resolution", mode, RES_DESKTOP, true); + } ++ else if (strSetting.Equals("videoscreen.monitor")) ++ { ++ CSettingString *pSettingString = (CSettingString *)pSettingControl->GetSetting(); ++ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(pSettingControl->GetID()); ++ CStdString currentMonitor = pControl->GetCurrentLabel(); ++ if (!g_Windowing.IsCurrentOutput(currentMonitor)) ++ { ++ g_guiSettings.SetString("videoscreen.monitor", currentMonitor); ++ g_Windowing.UpdateResolutions(); ++ DisplayMode mode = g_guiSettings.GetInt("videoscreen.screen"); ++ // Cascade ++ FillInResolutions("videoscreen.resolution", mode, RES_DESKTOP, true); ++ } ++ } + else if (strSetting.Equals("videoscreen.resolution")) + { + RESOLUTION nextRes = (RESOLUTION) g_guiSettings.GetInt("videoscreen.resolution"); +@@ -2351,11 +2405,15 @@ + if (g_advancedSettings.m_canWindowed) + pControl->AddLabel(g_localizeStrings.Get(242), -1); + ++#if !defined(HAS_GLX) + for (int idx = 0; idx < g_Windowing.GetNumScreens(); idx++) + { + strScreen.Format(g_localizeStrings.Get(241), g_settings.m_ResInfo[RES_DESKTOP + idx].iScreen + 1); + pControl->AddLabel(strScreen, g_settings.m_ResInfo[RES_DESKTOP + idx].iScreen); + } ++#else ++ pControl->AddLabel(g_localizeStrings.Get(244), 0); ++#endif + pControl->SetValue(mode); + g_guiSettings.SetInt("videoscreen.screen", mode); + } +@@ -2363,6 +2421,36 @@ + return mode; + } + ++void CGUIWindowSettingsCategory::FillInMonitors(CStdString strSetting) ++{ ++ // we expect "videoscreen.monitor" but it might be hidden on some platforms, ++ // so check that we actually have a visable control. ++ CBaseSettingControl *control = GetSetting(strSetting); ++ if (control) ++ { ++ control->SetDelayed(); ++ CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(control->GetID()); ++ pControl->Clear(); ++ ++ std::vector monitors; ++ g_Windowing.GetConnectedOutputs(&monitors); ++ ++ int currentMonitor = 0; ++ for (unsigned int i=0; iAddLabel(monitors[i], i); ++ } ++ ++ pControl->SetValue(currentMonitor); ++ g_guiSettings.SetString("videoscreen.monitor", g_settings.m_ResInfo[RES_DESKTOP].strOutput); ++ } ++} ++ ++ + void CGUIWindowSettingsCategory::FillInResolutions(CStdString strSetting, DisplayMode mode, RESOLUTION res, bool UserChange) + { + CBaseSettingControl *control = GetSetting(strSetting); +@@ -2491,13 +2579,15 @@ + RESOLUTION lastRes = g_graphicsContext.GetVideoResolution(); + bool cancelled = false; + ++ bool outputChanged = !g_Windowing.IsCurrentOutput(g_guiSettings.GetString("videoscreen.monitor")); ++ + g_guiSettings.SetResolution(nextRes); +- g_graphicsContext.SetVideoResolution(nextRes); ++ g_graphicsContext.SetVideoResolution(nextRes, outputChanged); + + if (!CGUIDialogYesNo::ShowAndGetInput(13110, 13111, 20022, 20022, -1, -1, cancelled, 10000)) + { + g_guiSettings.SetResolution(lastRes); +- g_graphicsContext.SetVideoResolution(lastRes); ++ g_graphicsContext.SetVideoResolution(lastRes, outputChanged); + + DisplayMode mode = FillInScreens("videoscreen.screen", lastRes); + FillInResolutions("videoscreen.resolution", mode, lastRes, false); +diff -Naur xbmc-6cf5b9d/xbmc/settings/GUIWindowSettingsCategory.h xbmc-6cf5b9d.patch/xbmc/settings/GUIWindowSettingsCategory.h +--- xbmc-6cf5b9d/xbmc/settings/GUIWindowSettingsCategory.h 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/GUIWindowSettingsCategory.h 2012-10-03 03:02:11.268939041 +0200 +@@ -49,6 +49,7 @@ + void FillInSoundSkins(CSetting *pSetting); + void FillInLanguages(CSetting *pSetting, const std::vector &languages = std::vector(), const std::vector &languageKeys = std::vector()); + DisplayMode FillInScreens(CStdString strSetting, RESOLUTION res); ++ void FillInMonitors(CStdString strSetting); + void FillInResolutions(CStdString strSetting, DisplayMode mode, RESOLUTION res, bool UserChange); + void FillInRefreshRates(CStdString strSetting, RESOLUTION res, bool UserChange); + void OnRefreshRateChanged(RESOLUTION resolution); +diff -Naur xbmc-6cf5b9d/xbmc/settings/Settings.cpp xbmc-6cf5b9d.patch/xbmc/settings/Settings.cpp +--- xbmc-6cf5b9d/xbmc/settings/Settings.cpp 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/Settings.cpp 2012-10-03 03:02:11.281939306 +0200 +@@ -55,6 +55,7 @@ + #include "filesystem/File.h" + #include "filesystem/DirectoryCache.h" + #include "DatabaseManager.h" ++#include "windowing/WindowingFactory.h" + + using namespace std; + using namespace XFILE; +diff -Naur xbmc-6cf5b9d/xbmc/settings/VideoSettings.h xbmc-6cf5b9d.patch/xbmc/settings/VideoSettings.h +--- xbmc-6cf5b9d/xbmc/settings/VideoSettings.h 2012-10-03 01:09:12.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/settings/VideoSettings.h 2012-10-03 03:02:11.421942152 +0200 +@@ -65,6 +65,8 @@ + VS_INTERLACEMETHOD_SW_BLEND = 20, + VS_INTERLACEMETHOD_AUTO_ION = 21, + ++ VS_INTERLACEMETHOD_XVBA = 22, ++ + VS_INTERLACEMETHOD_MAX // do not use and keep as last enum value. + }; + +diff -Naur xbmc-6cf5b9d/xbmc/system.h xbmc-6cf5b9d.patch/xbmc/system.h +--- xbmc-6cf5b9d/xbmc/system.h 2012-10-03 01:09:10.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/system.h 2012-10-03 03:02:11.241938493 +0200 +@@ -163,16 +163,21 @@ + #define HAS_GL + #ifdef HAVE_X11 + #define HAS_GLX ++#define HAS_X11_WIN_EVENTS + #endif + #ifdef HAVE_SDL + #define HAS_SDL + #ifndef HAS_SDL_OPENGL + #define HAS_SDL_OPENGL + #endif ++#ifndef HAVE_X11 + #define HAS_SDL_WIN_EVENTS ++#endif + #else ++#ifndef HAVE_X11 + #define HAS_LINUX_EVENTS + #endif ++#endif + #define HAS_LINUX_NETWORK + #define HAS_LIRC + #ifdef HAVE_LIBPULSE +diff -Naur xbmc-6cf5b9d/xbmc/utils/ActorProtocol.cpp xbmc-6cf5b9d.patch/xbmc/utils/ActorProtocol.cpp +--- xbmc-6cf5b9d/xbmc/utils/ActorProtocol.cpp 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/xbmc/utils/ActorProtocol.cpp 2012-10-03 03:02:11.344940586 +0200 +@@ -0,0 +1,254 @@ ++/* ++ * Copyright (C) 2005-2011 Team XBMC ++ * http://www.xbmc.org ++ * ++ * This Program 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, or (at your option) ++ * any later version. ++ * ++ * This Program 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 XBMC; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include "ActorProtocol.h" ++ ++using namespace Actor; ++ ++void Message::Release() ++{ ++ bool skip; ++ origin->Lock(); ++ skip = isSync ? !isSyncFini : false; ++ isSyncFini = true; ++ origin->Unlock(); ++ ++ if (skip) ++ return; ++ ++ // free data buffer ++ if (data != buffer) ++ delete [] data; ++ ++ // delete event in case of sync message ++ if (event) ++ delete event; ++ ++ origin->ReturnMessage(this); ++} ++ ++bool Message::Reply(int sig, void *data /* = NULL*/, int size /* = 0 */) ++{ ++ if (!isSync) ++ { ++ if (isOut) ++ return origin->SendInMessage(sig, data, size); ++ else ++ return origin->SendOutMessage(sig, data, size); ++ } ++ ++ origin->Lock(); ++ ++ if (!isSyncTimeout) ++ { ++ Message *msg = origin->GetMessage(); ++ msg->signal = sig; ++ msg->isOut = !isOut; ++ replyMessage = msg; ++ if (data) ++ { ++ if (size > MSG_INTERNAL_BUFFER_SIZE) ++ msg->data = new uint8_t[size]; ++ else ++ msg->data = msg->buffer; ++ memcpy(msg->data, data, size); ++ } ++ } ++ ++ origin->Unlock(); ++ ++ if (event) ++ event->Set(); ++ ++ return true; ++} ++ ++Protocol::~Protocol() ++{ ++ Message *msg; ++ Purge(); ++ while (!freeMessageQueue.empty()) ++ { ++ msg = freeMessageQueue.front(); ++ freeMessageQueue.pop(); ++ delete msg; ++ } ++} ++ ++Message *Protocol::GetMessage() ++{ ++ Message *msg; ++ ++ CSingleLock lock(criticalSection); ++ ++ if (!freeMessageQueue.empty()) ++ { ++ msg = freeMessageQueue.front(); ++ freeMessageQueue.pop(); ++ } ++ else ++ msg = new Message(); ++ ++ msg->isSync = false; ++ msg->isSyncFini = false; ++ msg->isSyncTimeout = false; ++ msg->event = NULL; ++ msg->data = NULL; ++ msg->payloadSize = 0; ++ msg->replyMessage = NULL; ++ msg->origin = this; ++ ++ return msg; ++} ++ ++void Protocol::ReturnMessage(Message *msg) ++{ ++ CSingleLock lock(criticalSection); ++ ++ freeMessageQueue.push(msg); ++} ++ ++bool Protocol::SendOutMessage(int signal, void *data /* = NULL */, int size /* = 0 */, Message *outMsg /* = NULL */) ++{ ++ Message *msg; ++ if (outMsg) ++ msg = outMsg; ++ else ++ msg = GetMessage(); ++ ++ msg->signal = signal; ++ msg->isOut = true; ++ ++ if (data) ++ { ++ if (size > MSG_INTERNAL_BUFFER_SIZE) ++ msg->data = new uint8_t[size]; ++ else ++ msg->data = msg->buffer; ++ memcpy(msg->data, data, size); ++ } ++ ++ { CSingleLock lock(criticalSection); ++ outMessages.push(msg); ++ } ++ containerOutEvent->Set(); ++ ++ return true; ++} ++ ++bool Protocol::SendInMessage(int signal, void *data /* = NULL */, int size /* = 0 */, Message *outMsg /* = NULL */) ++{ ++ Message *msg; ++ if (outMsg) ++ msg = outMsg; ++ else ++ msg = GetMessage(); ++ ++ msg->signal = signal; ++ msg->isOut = false; ++ ++ if (data) ++ { ++ if (size > MSG_INTERNAL_BUFFER_SIZE) ++ msg->data = new uint8_t[size]; ++ else ++ msg->data = msg->buffer; ++ memcpy(msg->data, data, size); ++ } ++ ++ { CSingleLock lock(criticalSection); ++ inMessages.push(msg); ++ } ++ containerInEvent->Set(); ++ ++ return true; ++} ++ ++ ++bool Protocol::SendOutMessageSync(int signal, Message **retMsg, int timeout, void *data /* = NULL */, int size /* = 0 */) ++{ ++ Message *msg = GetMessage(); ++ msg->isOut = true; ++ msg->isSync = true; ++ msg->event = new CEvent; ++ msg->event->Reset(); ++ SendOutMessage(signal, data, size, msg); ++ ++ if (!msg->event->WaitMSec(timeout)) ++ { ++ msg->origin->Lock(); ++ if (msg->replyMessage) ++ *retMsg = msg->replyMessage; ++ else ++ { ++ *retMsg = NULL; ++ msg->isSyncTimeout = true; ++ } ++ msg->origin->Unlock(); ++ } ++ else ++ *retMsg = msg->replyMessage; ++ ++ msg->Release(); ++ ++ if (*retMsg) ++ return true; ++ else ++ return false; ++} ++ ++bool Protocol::ReceiveOutMessage(Message **msg) ++{ ++ CSingleLock lock(criticalSection); ++ ++ if (outMessages.empty() || outDefered) ++ return false; ++ ++ *msg = outMessages.front(); ++ outMessages.pop(); ++ ++ return true; ++} ++ ++bool Protocol::ReceiveInMessage(Message **msg) ++{ ++ CSingleLock lock(criticalSection); ++ ++ if (inMessages.empty() || inDefered) ++ return false; ++ ++ *msg = inMessages.front(); ++ inMessages.pop(); ++ ++ return true; ++} ++ ++ ++void Protocol::Purge() ++{ ++ Message *msg; ++ ++ while (ReceiveInMessage(&msg)) ++ msg->Release(); ++ ++ while (ReceiveOutMessage(&msg)) ++ msg->Release(); ++} +diff -Naur xbmc-6cf5b9d/xbmc/utils/ActorProtocol.h xbmc-6cf5b9d.patch/xbmc/utils/ActorProtocol.h +--- xbmc-6cf5b9d/xbmc/utils/ActorProtocol.h 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/xbmc/utils/ActorProtocol.h 2012-10-03 03:02:11.506943879 +0200 +@@ -0,0 +1,88 @@ ++#pragma once ++ ++/* ++ * Copyright (C) 2005-2011 Team XBMC ++ * http://www.xbmc.org ++ * ++ * This Program 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, or (at your option) ++ * any later version. ++ * ++ * This Program 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 XBMC; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include "threads/Thread.h" ++#include "utils/log.h" ++#include ++#include "memory.h" ++ ++#define MSG_INTERNAL_BUFFER_SIZE 32 ++ ++namespace Actor ++{ ++ ++class Protocol; ++ ++class Message ++{ ++ friend class Protocol; ++public: ++ int signal; ++ bool isSync; ++ bool isSyncFini; ++ bool isOut; ++ bool isSyncTimeout; ++ int payloadSize; ++ uint8_t buffer[MSG_INTERNAL_BUFFER_SIZE]; ++ uint8_t *data; ++ Message *replyMessage; ++ Protocol *origin; ++ CEvent *event; ++ ++ void Release(); ++ bool Reply(int sig, void *data = NULL, int size = 0); ++ ++private: ++ Message() {isSync = false; data = NULL; event = NULL; replyMessage = NULL;}; ++}; ++ ++class Protocol ++{ ++public: ++ Protocol(std::string name, CEvent* inEvent, CEvent *outEvent) ++ : portName(name), inDefered(false), outDefered(false) {containerInEvent = inEvent; containerOutEvent = outEvent;}; ++ virtual ~Protocol(); ++ Message *GetMessage(); ++ void ReturnMessage(Message *msg); ++ bool SendOutMessage(int signal, void *data = NULL, int size = 0, Message *outMsg = NULL); ++ bool SendInMessage(int signal, void *data = NULL, int size = 0, Message *outMsg = NULL); ++ bool SendOutMessageSync(int signal, Message **retMsg, int timeout, void *data = NULL, int size = 0); ++ bool ReceiveOutMessage(Message **msg); ++ bool ReceiveInMessage(Message **msg); ++ void Purge(); ++ void DeferIn(bool value) {inDefered = value;}; ++ void DeferOut(bool value) {outDefered = value;}; ++ void Lock() {criticalSection.lock();}; ++ void Unlock() {criticalSection.unlock();}; ++ std::string portName; ++ ++protected: ++ CEvent *containerInEvent, *containerOutEvent; ++ CCriticalSection criticalSection; ++ std::queue outMessages; ++ std::queue inMessages; ++ std::queue freeMessageQueue; ++ bool inDefered, outDefered; ++}; ++ ++} +diff -Naur xbmc-6cf5b9d/xbmc/utils/Makefile xbmc-6cf5b9d.patch/xbmc/utils/Makefile +--- xbmc-6cf5b9d/xbmc/utils/Makefile 2012-10-03 01:09:10.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/utils/Makefile 2012-10-03 03:02:11.345940606 +0200 +@@ -67,6 +67,7 @@ + XBMCTinyXML.cpp \ + XMLUtils.cpp \ + BitstreamConverter.cpp \ ++ ActorProtocol.cpp \ + + LIB=utils.a + +diff -Naur xbmc-6cf5b9d/xbmc/video/dialogs/GUIDialogVideoSettings.cpp xbmc-6cf5b9d.patch/xbmc/video/dialogs/GUIDialogVideoSettings.cpp +--- xbmc-6cf5b9d/xbmc/video/dialogs/GUIDialogVideoSettings.cpp 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/video/dialogs/GUIDialogVideoSettings.cpp 2012-10-03 03:02:11.425942233 +0200 +@@ -108,13 +108,12 @@ + entries.push_back(make_pair(VS_INTERLACEMETHOD_INVERSE_TELECINE , 16314)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL , 16311)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_TEMPORAL , 16310)); +- entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_BOB , 16021)); ++ entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_BOB , 16325)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, 16318)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF , 16317)); +- entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE , 16314)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BOB , 16320)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BEST , 16321)); +- entries.push_back(make_pair(VS_INTERLACEMETHOD_AUTO_ION , 16325)); ++ entries.push_back(make_pair(VS_INTERLACEMETHOD_XVBA , 16326)); + + /* remove unsupported methods */ + for(vector >::iterator it = entries.begin(); it != entries.end();) +diff -Naur xbmc-6cf5b9d/xbmc/video/VideoReferenceClock.cpp xbmc-6cf5b9d.patch/xbmc/video/VideoReferenceClock.cpp +--- xbmc-6cf5b9d/xbmc/video/VideoReferenceClock.cpp 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/video/VideoReferenceClock.cpp 2012-10-03 03:02:11.519944144 +0200 +@@ -30,6 +30,7 @@ + #include + #include + #include "windowing/WindowingFactory.h" ++ #include "guilib/GraphicContext.h" + #define NVSETTINGSCMD "nvidia-settings -nt -q RefreshRate3" + #elif defined(TARGET_DARWIN_OSX) + #include +@@ -135,12 +136,23 @@ + m_Context = NULL; + m_pixmap = None; + m_glPixmap = None; +- m_RREventBase = 0; +- m_UseNvSettings = true; ++ m_UseNvSettings = false; + m_bIsATI = false; + #endif + } + ++CVideoReferenceClock::~CVideoReferenceClock() ++{ ++#if defined(HAS_GLX) ++ // some ATI voodoo, if we don't close the display, we crash on exit ++ if (m_Dpy) ++ { ++ XCloseDisplay(m_Dpy); ++ m_Dpy = NULL; ++ } ++#endif ++} ++ + void CVideoReferenceClock::Process() + { + bool SetupSuccess = false; +@@ -151,6 +163,10 @@ + m_D3dCallback.Reset(); + g_Windowing.Register(&m_D3dCallback); + #endif ++#if defined(HAS_GLX) && defined(HAS_XRANDR) ++ g_Windowing.Register(this); ++ m_xrrEvent = false; ++#endif + + while(!m_bStop) + { +@@ -211,6 +227,16 @@ + //clean up the vblank clock + #if defined(HAS_GLX) && defined(HAS_XRANDR) + CleanupGLX(); ++ if (m_xrrEvent) ++ { ++ m_releaseEvent.Set(); ++ while (!m_bStop) ++ { ++ if (m_resetEvent.WaitMSec(100)) ++ break; ++ } ++ m_xrrEvent = false; ++ } + #elif defined(_WIN32) && defined(HAS_DX) + CleanupD3D(); + #elif defined(TARGET_DARWIN) +@@ -222,6 +248,9 @@ + #if defined(_WIN32) && defined(HAS_DX) + g_Windowing.Unregister(&m_D3dCallback); + #endif ++#if defined(HAS_GLX) ++ g_Windowing.Unregister(this); ++#endif + } + + bool CVideoReferenceClock::WaitStarted(int MSecs) +@@ -231,6 +260,24 @@ + } + + #if defined(HAS_GLX) && defined(HAS_XRANDR) ++ ++void CVideoReferenceClock::OnLostDevice() ++{ ++ if (!m_xrrEvent) ++ { ++ m_releaseEvent.Reset(); ++ m_resetEvent.Reset(); ++ m_xrrEvent = true; ++ m_releaseEvent.Wait(); ++ } ++} ++ ++void CVideoReferenceClock::OnResetDevice() ++{ ++ m_xrrEvent = false; ++ m_resetEvent.Set(); ++} ++ + bool CVideoReferenceClock::SetupGLX() + { + int singleBufferAttributes[] = { +@@ -270,7 +317,7 @@ + } + + bool ExtensionFound = false; +- istringstream Extensions(glXQueryExtensionsString(m_Dpy, DefaultScreen(m_Dpy))); ++ istringstream Extensions(glXQueryExtensionsString(m_Dpy, g_Windowing.GetCurrentScreen())); + string ExtensionStr; + + while (!ExtensionFound) +@@ -297,7 +344,7 @@ + m_bIsATI = true; + } + +- m_vInfo = glXChooseVisual(m_Dpy, DefaultScreen(m_Dpy), singleBufferAttributes); ++ m_vInfo = glXChooseVisual(m_Dpy, g_Windowing.GetCurrentScreen(), singleBufferAttributes); + if (!m_vInfo) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXChooseVisual returned NULL"); +@@ -308,15 +355,16 @@ + { + Swa.border_pixel = 0; + Swa.event_mask = StructureNotifyMask; +- Swa.colormap = XCreateColormap(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen), m_vInfo->visual, AllocNone ); ++ Swa.colormap = XCreateColormap(m_Dpy, g_Windowing.GetWindow(), m_vInfo->visual, AllocNone ); + SwaMask = CWBorderPixel | CWColormap | CWEventMask; + +- m_Window = XCreateWindow(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen), 0, 0, 256, 256, 0, ++ m_Window = XCreateWindow(m_Dpy, g_Windowing.GetWindow(), 0, 0, 256, 256, 0, + m_vInfo->depth, InputOutput, m_vInfo->visual, SwaMask, &Swa); + } + else + { +- m_pixmap = XCreatePixmap(m_Dpy, DefaultRootWindow(m_Dpy), 256, 256, m_vInfo->depth); ++ Window window = g_Windowing.GetWindow(); ++ m_pixmap = XCreatePixmap(m_Dpy, window, 256, 256, m_vInfo->depth); + if (!m_pixmap) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: unable to create pixmap"); +@@ -381,10 +429,6 @@ + return false; + } + +- //set up receiving of RandR events, we'll get one when the refreshrate changes +- XRRQueryExtension(m_Dpy, &m_RREventBase, &ReturnV); +- XRRSelectInput(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen), RRScreenChangeNotifyMask); +- + UpdateRefreshrate(true); //forced refreshrate update + m_MissedVblanks = 0; + +@@ -518,7 +562,7 @@ + int RefreshRate; + XRRScreenConfiguration *CurrInfo; + +- CurrInfo = XRRGetScreenInfo(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen)); ++ CurrInfo = XRRGetScreenInfo(m_Dpy, g_Windowing.GetWindow()); + RefreshRate = XRRConfigCurrentRate(CurrInfo); + XRRFreeScreenConfigInfo(CurrInfo); + +@@ -585,6 +629,9 @@ + + while(!m_bStop) + { ++ if (m_xrrEvent) ++ return; ++ + //wait for the next vblank + if (!m_bIsATI) + { +@@ -648,7 +695,6 @@ + UpdateClock((int)(VblankCount - PrevVblankCount), true); + SingleLock.Leave(); + SendVblankSignal(); +- UpdateRefreshrate(); + IsReset = false; + } + else if (!m_bStop) +@@ -1185,23 +1231,10 @@ + + #if defined(HAS_GLX) && defined(HAS_XRANDR) + +- //check for RandR events +- bool GotEvent = Forced || m_RefreshChanged == 2; +- XEvent Event; +- while (XCheckTypedEvent(m_Dpy, m_RREventBase + RRScreenChangeNotify, &Event)) +- { +- if (Event.type == m_RREventBase + RRScreenChangeNotify) +- { +- CLog::Log(LOGDEBUG, "CVideoReferenceClock: Received RandR event %i", Event.type); +- GotEvent = true; +- } +- XRRUpdateConfiguration(&Event); +- } +- + if (!Forced) + m_RefreshChanged = 0; + +- if (!GotEvent) //refreshrate did not change ++ if (!Forced) //refreshrate did not change + return false; + + //the refreshrate can be wrong on nvidia drivers, so read it from nvidia-settings when it's available +@@ -1222,7 +1255,7 @@ + } + + CSingleLock SingleLock(m_CritSection); +- m_RefreshRate = GetRandRRate(); ++ m_RefreshRate = MathUtils::round_int(g_graphicsContext.GetFPS()); + + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %i hertz", (int)m_RefreshRate); + +diff -Naur xbmc-6cf5b9d/xbmc/video/VideoReferenceClock.h xbmc-6cf5b9d.patch/xbmc/video/VideoReferenceClock.h +--- xbmc-6cf5b9d/xbmc/video/VideoReferenceClock.h 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/video/VideoReferenceClock.h 2012-10-03 03:02:11.520944164 +0200 +@@ -30,6 +30,7 @@ + #include + #include + #include ++ #include "guilib/DispResource.h" + #elif defined(_WIN32) && defined(HAS_DX) + #include + #include "guilib/D3DResource.h" +@@ -56,9 +57,13 @@ + #endif + + class CVideoReferenceClock : public CThread ++#if defined(HAS_GLX) && defined(HAS_XRANDR) ++ ,public IDispResource ++#endif + { + public: + CVideoReferenceClock(); ++ virtual ~CVideoReferenceClock(); + + int64_t GetTime(bool interpolated = true); + int64_t GetFrequency(); +@@ -75,6 +80,11 @@ + void VblankHandler(int64_t nowtime, double fps); + #endif + ++#if defined(HAS_GLX) && defined(HAS_XRANDR) ++ virtual void OnLostDevice(); ++ virtual void OnResetDevice(); ++#endif ++ + private: + void Process(); + bool UpdateRefreshrate(bool Forced = false); +@@ -121,7 +131,8 @@ + GLXContext m_Context; + Pixmap m_pixmap; + GLXPixmap m_glPixmap; +- int m_RREventBase; ++ bool m_xrrEvent; ++ CEvent m_releaseEvent, m_resetEvent; + + bool m_UseNvSettings; + bool m_bIsATI; +diff -Naur xbmc-6cf5b9d/xbmc/windowing/Makefile xbmc-6cf5b9d.patch/xbmc/windowing/Makefile +--- xbmc-6cf5b9d/xbmc/windowing/Makefile 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/Makefile 2012-10-03 03:02:11.242938513 +0200 +@@ -1,6 +1,7 @@ + SRCS=WinEventsSDL.cpp \ + WinEventsLinux.cpp \ + WinSystem.cpp \ ++ WinEventsX11.cpp \ + + LIB=windowing.a + +diff -Naur xbmc-6cf5b9d/xbmc/windowing/WinEvents.h xbmc-6cf5b9d.patch/xbmc/windowing/WinEvents.h +--- xbmc-6cf5b9d/xbmc/windowing/WinEvents.h 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/WinEvents.h 2012-10-03 03:02:11.242938513 +0200 +@@ -58,6 +58,10 @@ + #include "WinEventsSDL.h" + #define CWinEvents CWinEventsSDL + ++#elif defined(TARGET_LINUX) && defined(HAS_X11_WIN_EVENTS) ++#include "WinEventsX11.h" ++#define CWinEvents CWinEventsX11 ++ + #elif defined(TARGET_LINUX) && defined(HAS_LINUX_EVENTS) + #include "WinEventsLinux.h" + #define CWinEvents CWinEventsLinux +diff -Naur xbmc-6cf5b9d/xbmc/windowing/WinEventsX11.cpp xbmc-6cf5b9d.patch/xbmc/windowing/WinEventsX11.cpp +--- xbmc-6cf5b9d/xbmc/windowing/WinEventsX11.cpp 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/WinEventsX11.cpp 2012-10-03 03:02:11.433942396 +0200 +@@ -0,0 +1,749 @@ ++/* ++* Copyright (C) 2005-2012 Team XBMC ++* http://www.xbmc.org ++* ++* This Program 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, or (at your option) ++* any later version. ++* ++* This Program 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 XBMC; see the file COPYING. If not, write to ++* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++* http://www.gnu.org/copyleft/gpl.html ++* ++*/ ++ ++#include "system.h" ++ ++#ifdef HAS_X11_WIN_EVENTS ++ ++#include "WinEvents.h" ++#include "WinEventsX11.h" ++#include "Application.h" ++#include "ApplicationMessenger.h" ++#include ++#include "X11/WinSystemX11GL.h" ++#include "X11/keysymdef.h" ++#include "X11/XF86keysym.h" ++#include "utils/log.h" ++#include "utils/CharsetConverter.h" ++#include "guilib/GUIWindowManager.h" ++#include "input/MouseStat.h" ++ ++#if defined(HAS_XRANDR) ++#include ++#endif ++ ++#ifdef HAS_SDL_JOYSTICK ++#include "input/SDLJoystick.h" ++#endif ++ ++CWinEventsX11* CWinEventsX11::WinEvents = 0; ++ ++static uint32_t SymMappingsX11[][2] = ++{ ++ {XK_BackSpace, XBMCK_BACKSPACE} ++, {XK_Tab, XBMCK_TAB} ++, {XK_Clear, XBMCK_CLEAR} ++, {XK_Return, XBMCK_RETURN} ++, {XK_Pause, XBMCK_PAUSE} ++, {XK_Escape, XBMCK_ESCAPE} ++, {XK_Delete, XBMCK_DELETE} ++// multi-media keys ++, {XF86XK_Back, XBMCK_BROWSER_BACK} ++, {XF86XK_Forward, XBMCK_BROWSER_FORWARD} ++, {XF86XK_Refresh, XBMCK_BROWSER_REFRESH} ++, {XF86XK_Stop, XBMCK_BROWSER_STOP} ++, {XF86XK_Search, XBMCK_BROWSER_SEARCH} ++, {XF86XK_Favorites, XBMCK_BROWSER_FAVORITES} ++, {XF86XK_HomePage, XBMCK_BROWSER_HOME} ++, {XF86XK_AudioMute, XBMCK_VOLUME_MUTE} ++, {XF86XK_AudioLowerVolume, XBMCK_VOLUME_DOWN} ++, {XF86XK_AudioRaiseVolume, XBMCK_VOLUME_UP} ++, {XF86XK_AudioNext, XBMCK_MEDIA_NEXT_TRACK} ++, {XF86XK_AudioPrev, XBMCK_MEDIA_PREV_TRACK} ++, {XF86XK_AudioStop, XBMCK_MEDIA_STOP} ++, {XF86XK_AudioPause, XBMCK_MEDIA_PLAY_PAUSE} ++, {XF86XK_Mail, XBMCK_LAUNCH_MAIL} ++, {XF86XK_Select, XBMCK_LAUNCH_MEDIA_SELECT} ++, {XF86XK_Launch0, XBMCK_LAUNCH_APP1} ++, {XF86XK_Launch1, XBMCK_LAUNCH_APP2} ++, {XF86XK_WWW, XBMCK_LAUNCH_FILE_BROWSER} ++, {XF86XK_AudioMedia, XBMCK_LAUNCH_MEDIA_CENTER } ++ // Numeric keypad ++, {XK_KP_0, XBMCK_KP0} ++, {XK_KP_1, XBMCK_KP1} ++, {XK_KP_2, XBMCK_KP2} ++, {XK_KP_3, XBMCK_KP3} ++, {XK_KP_4, XBMCK_KP4} ++, {XK_KP_5, XBMCK_KP5} ++, {XK_KP_6, XBMCK_KP6} ++, {XK_KP_7, XBMCK_KP7} ++, {XK_KP_8, XBMCK_KP8} ++, {XK_KP_9, XBMCK_KP9} ++, {XK_KP_Separator, XBMCK_KP_PERIOD} ++, {XK_KP_Divide, XBMCK_KP_DIVIDE} ++, {XK_KP_Multiply, XBMCK_KP_MULTIPLY} ++, {XK_KP_Subtract, XBMCK_KP_MINUS} ++, {XK_KP_Add, XBMCK_KP_PLUS} ++, {XK_KP_Enter, XBMCK_KP_ENTER} ++, {XK_KP_Equal, XBMCK_KP_EQUALS} ++ // Arrows + Home/End pad ++, {XK_Up, XBMCK_UP} ++, {XK_Down, XBMCK_DOWN} ++, {XK_Right, XBMCK_RIGHT} ++, {XK_Left, XBMCK_LEFT} ++, {XK_Insert, XBMCK_INSERT} ++, {XK_Home, XBMCK_HOME} ++, {XK_End, XBMCK_END} ++, {XK_Page_Up, XBMCK_PAGEUP} ++, {XK_Page_Down, XBMCK_PAGEDOWN} ++ // Function keys ++, {XK_F1, XBMCK_F1} ++, {XK_F2, XBMCK_F2} ++, {XK_F3, XBMCK_F3} ++, {XK_F4, XBMCK_F4} ++, {XK_F5, XBMCK_F5} ++, {XK_F6, XBMCK_F6} ++, {XK_F7, XBMCK_F7} ++, {XK_F8, XBMCK_F8} ++, {XK_F9, XBMCK_F9} ++, {XK_F10, XBMCK_F10} ++, {XK_F11, XBMCK_F11} ++, {XK_F12, XBMCK_F12} ++, {XK_F13, XBMCK_F13} ++, {XK_F14, XBMCK_F14} ++, {XK_F15, XBMCK_F15} ++ // Key state modifier keys ++, {XK_Num_Lock, XBMCK_NUMLOCK} ++, {XK_Caps_Lock, XBMCK_CAPSLOCK} ++, {XK_Scroll_Lock, XBMCK_SCROLLOCK} ++, {XK_Shift_R, XBMCK_RSHIFT} ++, {XK_Shift_L, XBMCK_LSHIFT} ++, {XK_Control_R, XBMCK_RCTRL} ++, {XK_Control_L, XBMCK_LCTRL} ++, {XK_Alt_R, XBMCK_RALT} ++, {XK_Alt_L, XBMCK_LALT} ++, {XK_Meta_R, XBMCK_RMETA} ++, {XK_Meta_L, XBMCK_LMETA} ++, {XK_Super_L, XBMCK_LSUPER} ++, {XK_Super_R, XBMCK_RSUPER} ++, {XK_Mode_switch, XBMCK_MODE} ++, {XK_Multi_key, XBMCK_COMPOSE} ++ // Miscellaneous function keys ++, {XK_Help, XBMCK_HELP} ++, {XK_Print, XBMCK_PRINT} ++//, {0, XBMCK_SYSREQ} ++, {XK_Break, XBMCK_BREAK} ++, {XK_Menu, XBMCK_MENU} ++, {XF86XK_PowerOff, XBMCK_POWER} ++, {XK_EcuSign, XBMCK_EURO} ++, {XK_Undo, XBMCK_UNDO} ++ /* Media keys */ ++, {XF86XK_Eject, XBMCK_EJECT} ++, {XF86XK_Stop, XBMCK_STOP} ++, {XF86XK_AudioRecord, XBMCK_RECORD} ++, {XF86XK_AudioRewind, XBMCK_REWIND} ++, {XF86XK_Phone, XBMCK_PHONE} ++, {XF86XK_AudioPlay, XBMCK_PLAY} ++, {XF86XK_AudioRandomPlay, XBMCK_SHUFFLE} ++, {XF86XK_AudioForward, XBMCK_FASTFORWARD} ++}; ++ ++ ++CWinEventsX11::CWinEventsX11() ++{ ++ m_display = 0; ++ m_window = 0; ++ m_keybuf = 0; ++ m_keybuf_len = 0; ++} ++ ++CWinEventsX11::~CWinEventsX11() ++{ ++ if (m_keybuf); ++ { ++ free(m_keybuf); ++ m_keybuf = 0; ++ } ++ ++ if (m_xic) ++ { ++ XUnsetICFocus(m_xic); ++ XDestroyIC(m_xic); ++ m_xic = 0; ++ } ++ ++ if (m_xim) ++ { ++ XCloseIM(m_xim); ++ m_xim = 0; ++ } ++ ++ m_symLookupTable.clear(); ++} ++ ++bool CWinEventsX11::Init(Display *dpy, Window win) ++{ ++ if (WinEvents) ++ return true; ++ ++ WinEvents = new CWinEventsX11(); ++ WinEvents->m_display = dpy; ++ WinEvents->m_window = win; ++ WinEvents->m_keybuf_len = 32*sizeof(char); ++ WinEvents->m_keybuf = (char*)malloc(WinEvents->m_keybuf_len); ++ WinEvents->m_keymodState = 0; ++ WinEvents->m_wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False); ++ WinEvents->m_structureChanged = false; ++ WinEvents->m_xrrEventPending = false; ++ memset(&(WinEvents->m_lastKey), 0, sizeof(XBMC_Event)); ++ ++ // open input method ++ char *old_locale = NULL, *old_modifiers = NULL; ++ char res_name[8]; ++ const char *p; ++ size_t n; ++ ++ // set resource name to xbmc, not used ++ strcpy(res_name, "xbmc"); ++ ++ // save current locale, this should be "C" ++ p = setlocale(LC_ALL, NULL); ++ if (p) ++ { ++ old_locale = (char*)malloc(strlen(p) +1); ++ strcpy(old_locale, p); ++ } ++ p = XSetLocaleModifiers(NULL); ++ if (p) ++ { ++ old_modifiers = (char*)malloc(strlen(p) +1); ++ strcpy(old_modifiers, p); ++ } ++ ++ // set users preferences and open input method ++ p = setlocale(LC_ALL, ""); ++ XSetLocaleModifiers(""); ++ WinEvents->m_xim = XOpenIM(WinEvents->m_display, NULL, res_name, res_name); ++ ++ // restore old locale ++ if (old_locale) ++ { ++ setlocale(LC_ALL, old_locale); ++ free(old_locale); ++ } ++ if (old_modifiers) ++ { ++ XSetLocaleModifiers(old_modifiers); ++ free(old_modifiers); ++ } ++ ++ WinEvents->m_xic = NULL; ++ if (WinEvents->m_xim) ++ { ++ WinEvents->m_xic = XCreateIC(WinEvents->m_xim, ++ XNClientWindow, WinEvents->m_window, ++ XNFocusWindow, WinEvents->m_window, ++ XNInputStyle, XIMPreeditNothing | XIMStatusNothing, ++ XNResourceName, res_name, ++ XNResourceClass, res_name, ++ NULL); ++ } ++ ++ if (!WinEvents->m_xic) ++ CLog::Log(LOGWARNING,"CWinEventsX11::Init - no input method found"); ++ ++ // build Keysym lookup table ++ for (unsigned int i = 0; i < sizeof(SymMappingsX11)/(2*sizeof(uint32_t)); ++i) ++ { ++ WinEvents->m_symLookupTable[SymMappingsX11[i][0]] = SymMappingsX11[i][1]; ++ } ++ ++ // register for xrandr events ++#if defined(HAS_XRANDR) ++ int iReturn; ++ XRRQueryExtension(WinEvents->m_display, &WinEvents->m_RREventBase, &iReturn); ++ XRRSelectInput(WinEvents->m_display, WinEvents->m_window, RRScreenChangeNotifyMask); ++#endif ++ ++ return true; ++} ++ ++void CWinEventsX11::Quit() ++{ ++ if (!WinEvents) ++ return; ++ ++ delete WinEvents; ++ WinEvents = 0; ++} ++ ++bool CWinEventsX11::HasStructureChanged() ++{ ++ if (!WinEvents) ++ return false; ++ ++ bool ret = WinEvents->m_structureChanged; ++ WinEvents->m_structureChanged = false; ++ return ret; ++} ++ ++void CWinEventsX11::SetXRRFailSafeTimer(int millis) ++{ ++ if (!WinEvents) ++ return; ++ ++ WinEvents->m_xrrFailSafeTimer.Set(millis); ++ WinEvents->m_xrrEventPending = true; ++} ++ ++bool CWinEventsX11::MessagePump() ++{ ++ if (!WinEvents) ++ return false; ++ ++ bool ret = false; ++ XEvent xevent; ++ unsigned long serial = 0; ++ ++ while (WinEvents && XPending(WinEvents->m_display)) ++ { ++ memset(&xevent, 0, sizeof (XEvent)); ++ XNextEvent(WinEvents->m_display, &xevent); ++ ++ // ignore events generated by auto-repeat ++ if (xevent.type == KeyRelease && XPending(WinEvents->m_display)) ++ { ++ XEvent peekevent; ++ XPeekEvent(WinEvents->m_display, &peekevent); ++ if ((peekevent.type == KeyPress) && ++ (peekevent.xkey.keycode == xevent.xkey.keycode) && ++ ((peekevent.xkey.time - xevent.xkey.time) < 2)) ++ { ++ XNextEvent(WinEvents->m_display, &peekevent); ++ continue; ++ } ++ } ++ ++ if (XFilterEvent(&xevent, None)) ++ continue; ++ ++ switch (xevent.type) ++ { ++ case MapNotify: ++ { ++ g_application.m_AppActive = true; ++ break; ++ } ++ ++ case UnmapNotify: ++ { ++ g_application.m_AppActive = false; ++ break; ++ } ++ ++ case FocusIn: ++ { ++ if (WinEvents->m_xic) ++ XSetICFocus(WinEvents->m_xic); ++ g_application.m_AppFocused = true; ++ memset(&(WinEvents->m_lastKey), 0, sizeof(XBMC_Event)); ++ WinEvents->m_keymodState = 0; ++ if (serial == xevent.xfocus.serial) ++ break; ++ g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused); ++ break; ++ } ++ ++ case FocusOut: ++ { ++ if (WinEvents->m_xic) ++ XUnsetICFocus(WinEvents->m_xic); ++ g_application.m_AppFocused = false; ++ memset(&(WinEvents->m_lastKey), 0, sizeof(XBMC_Event)); ++ g_Windowing.NotifyAppFocusChange(g_application.m_AppFocused); ++ serial = xevent.xfocus.serial; ++ break; ++ } ++ ++ case Expose: ++ { ++ g_windowManager.MarkDirty(); ++ break; ++ } ++ ++ case ConfigureNotify: ++ { ++ if (xevent.xconfigure.window != WinEvents->m_window) ++ break; ++ ++ WinEvents->m_structureChanged = true; ++ XBMC_Event newEvent; ++ memset(&newEvent, 0, sizeof(newEvent)); ++ newEvent.type = XBMC_VIDEORESIZE; ++ newEvent.resize.w = xevent.xconfigure.width; ++ newEvent.resize.h = xevent.xconfigure.height; ++ ret |= g_application.OnEvent(newEvent); ++ g_windowManager.MarkDirty(); ++ break; ++ } ++ ++ case ClientMessage: ++ { ++ if (xevent.xclient.data.l[0] == WinEvents->m_wmDeleteMessage) ++ if (!g_application.m_bStop) CApplicationMessenger::Get().Quit(); ++ break; ++ } ++ ++ case KeyPress: ++ { ++ XBMC_Event newEvent; ++ memset(&newEvent, 0, sizeof(newEvent)); ++ newEvent.type = XBMC_KEYDOWN; ++ KeySym xkeysym; ++ ++ // fallback if we have no IM ++ if (!WinEvents->m_xic) ++ { ++ static XComposeStatus state; ++ char keybuf[32]; ++ xkeysym = XLookupKeysym(&xevent.xkey, 0); ++ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym); ++ newEvent.key.keysym.scancode = xevent.xkey.keycode; ++ newEvent.key.state = xevent.xkey.state; ++ newEvent.key.type = xevent.xkey.type; ++ if (XLookupString(&xevent.xkey, keybuf, sizeof(keybuf), NULL, &state)) ++ { ++ newEvent.key.keysym.unicode = keybuf[0]; ++ } ++ ret |= ProcessKey(newEvent, 500); ++ break; ++ } ++ ++ Status status; ++ int len; ++ len = Xutf8LookupString(WinEvents->m_xic, &xevent.xkey, ++ WinEvents->m_keybuf, WinEvents->m_keybuf_len, ++ &xkeysym, &status); ++ if (status == XBufferOverflow) ++ { ++ WinEvents->m_keybuf_len = len; ++ WinEvents->m_keybuf = (char*)realloc(WinEvents->m_keybuf, WinEvents->m_keybuf_len); ++ len = Xutf8LookupString(WinEvents->m_xic, &xevent.xkey, ++ WinEvents->m_keybuf, WinEvents->m_keybuf_len, ++ &xkeysym, &status); ++ } ++ switch (status) ++ { ++ case XLookupNone: ++ break; ++ case XLookupChars: ++ case XLookupBoth: ++ { ++ CStdString data(WinEvents->m_keybuf, len); ++ CStdStringW keys; ++ g_charsetConverter.utf8ToW(data, keys, false); ++ ++ if (keys.length() == 0) ++ { ++ break; ++ } ++ ++ for (unsigned int i = 0; i < keys.length() - 1; i++) ++ { ++ newEvent.key.keysym.sym = XBMCK_UNKNOWN; ++ newEvent.key.keysym.unicode = keys[i]; ++ newEvent.key.state = xevent.xkey.state; ++ newEvent.key.type = xevent.xkey.type; ++ ret |= ProcessKey(newEvent, 500); ++ } ++ if (keys.length() > 0) ++ { ++ newEvent.key.keysym.scancode = xevent.xkey.keycode; ++ xkeysym = XLookupKeysym(&xevent.xkey, 0); ++ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym); ++ newEvent.key.keysym.unicode = keys[keys.length() - 1]; ++ newEvent.key.state = xevent.xkey.state; ++ newEvent.key.type = xevent.xkey.type; ++ ++ ret |= ProcessKey(newEvent, 500); ++ } ++ break; ++ } ++ ++ case XLookupKeySym: ++ { ++ newEvent.key.keysym.scancode = xevent.xkey.keycode; ++ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym); ++ newEvent.key.state = xevent.xkey.state; ++ newEvent.key.type = xevent.xkey.type; ++ ret |= ProcessKey(newEvent, 500); ++ break; ++ } ++ ++ }// switch status ++ break; ++ } //KeyPress ++ ++ case KeyRelease: ++ { ++ XBMC_Event newEvent; ++ KeySym xkeysym; ++ memset(&newEvent, 0, sizeof(newEvent)); ++ newEvent.type = XBMC_KEYUP; ++ xkeysym = XLookupKeysym(&xevent.xkey, 0); ++ newEvent.key.keysym.scancode = xevent.xkey.keycode; ++ newEvent.key.keysym.sym = LookupXbmcKeySym(xkeysym); ++ newEvent.key.state = xevent.xkey.state; ++ newEvent.key.type = xevent.xkey.type; ++ ret |= ProcessKey(newEvent, 0); ++ break; ++ } ++ ++ case EnterNotify: ++ { ++ g_Windowing.NotifyMouseCoverage(true); ++ break; ++ } ++ ++ // lose mouse coverage ++ case LeaveNotify: ++ { ++ g_Windowing.NotifyMouseCoverage(false); ++ g_Mouse.SetActive(false); ++ break; ++ } ++ ++ case MotionNotify: ++ { ++ XBMC_Event newEvent; ++ memset(&newEvent, 0, sizeof(newEvent)); ++ newEvent.type = XBMC_MOUSEMOTION; ++ newEvent.motion.xrel = (int16_t)xevent.xmotion.x_root; ++ newEvent.motion.yrel = (int16_t)xevent.xmotion.y_root; ++ newEvent.motion.x = (int16_t)xevent.xmotion.x; ++ newEvent.motion.y = (int16_t)xevent.xmotion.y; ++ ret |= g_application.OnEvent(newEvent); ++ break; ++ } ++ ++ case ButtonPress: ++ { ++ XBMC_Event newEvent; ++ memset(&newEvent, 0, sizeof(newEvent)); ++ newEvent.type = XBMC_MOUSEBUTTONDOWN; ++ newEvent.button.button = (unsigned char)xevent.xbutton.button; ++ newEvent.button.state = XBMC_PRESSED; ++ newEvent.button.x = (int16_t)xevent.xbutton.x; ++ newEvent.button.y = (int16_t)xevent.xbutton.y; ++ ret |= g_application.OnEvent(newEvent); ++ break; ++ } ++ ++ case ButtonRelease: ++ { ++ XBMC_Event newEvent; ++ memset(&newEvent, 0, sizeof(newEvent)); ++ newEvent.type = XBMC_MOUSEBUTTONUP; ++ newEvent.button.button = (unsigned char)xevent.xbutton.button; ++ newEvent.button.state = XBMC_RELEASED; ++ newEvent.button.x = (int16_t)xevent.xbutton.x; ++ newEvent.button.y = (int16_t)xevent.xbutton.y; ++ ret |= g_application.OnEvent(newEvent); ++ break; ++ } ++ ++ default: ++ { ++ break; ++ } ++ }// switch event.type ++ ++#if defined(HAS_XRANDR) ++ if (WinEvents && (xevent.type == WinEvents->m_RREventBase + RRScreenChangeNotify)) ++ { ++ XRRUpdateConfiguration(&xevent); ++ if (xevent.xgeneric.serial != serial) ++ g_Windowing.NotifyXRREvent(); ++ WinEvents->m_xrrEventPending = false; ++ serial = xevent.xgeneric.serial; ++ } ++#endif ++ ++ }// while ++ ++ ret |= ProcessKeyRepeat(); ++ ++#if defined(HAS_XRANDR) ++ if (WinEvents && WinEvents->m_xrrEventPending && WinEvents->m_xrrFailSafeTimer.IsTimePast()) ++ { ++ CLog::Log(LOGERROR,"CWinEventsX11::MessagePump - missed XRR Events"); ++ g_Windowing.NotifyXRREvent(); ++ WinEvents->m_xrrEventPending = false; ++ } ++#endif ++ ++#ifdef HAS_SDL_JOYSTICK ++ SDL_Event event; ++ while (SDL_PollEvent(&event)) ++ { ++ switch(event.type) ++ { ++ case SDL_JOYBUTTONUP: ++ case SDL_JOYBUTTONDOWN: ++ case SDL_JOYAXISMOTION: ++ case SDL_JOYBALLMOTION: ++ case SDL_JOYHATMOTION: ++ g_Joystick.Update(event); ++ ret = true; ++ break; ++ ++ default: ++ break; ++ } ++ memset(&event, 0, sizeof(SDL_Event)); ++ } ++#endif ++ ++ return ret; ++} ++ ++bool CWinEventsX11::ProcessKey(XBMC_Event &event, int repeatDelay) ++{ ++ if (event.type == XBMC_KEYDOWN) ++ { ++ // check key modifiers ++ switch(event.key.keysym.sym) ++ { ++ case XBMCK_LSHIFT: ++ WinEvents->m_keymodState |= XBMCKMOD_LSHIFT; ++ break; ++ case XBMCK_RSHIFT: ++ WinEvents->m_keymodState |= XBMCKMOD_RSHIFT; ++ break; ++ case XBMCK_LCTRL: ++ WinEvents->m_keymodState |= XBMCKMOD_LCTRL; ++ break; ++ case XBMCK_RCTRL: ++ WinEvents->m_keymodState |= XBMCKMOD_RCTRL; ++ break; ++ case XBMCK_LALT: ++ WinEvents->m_keymodState |= XBMCKMOD_LALT; ++ break; ++ case XBMCK_RALT: ++ WinEvents->m_keymodState |= XBMCKMOD_RCTRL; ++ break; ++ case XBMCK_LMETA: ++ WinEvents->m_keymodState |= XBMCKMOD_LMETA; ++ break; ++ case XBMCK_RMETA: ++ WinEvents->m_keymodState |= XBMCKMOD_RMETA; ++ break; ++ case XBMCK_MODE: ++ WinEvents->m_keymodState |= XBMCKMOD_MODE; ++ break; ++ default: ++ break; ++ } ++ event.key.keysym.mod = (XBMCMod)WinEvents->m_keymodState; ++ memcpy(&(WinEvents->m_lastKey), &event, sizeof(event)); ++ WinEvents->m_repeatKeyTimeout.Set(repeatDelay); ++ ++ bool ret = ProcessShortcuts(event); ++ if (ret) ++ return ret; ++ } ++ else if (event.type == XBMC_KEYUP) ++ { ++ switch(event.key.keysym.sym) ++ { ++ case XBMCK_LSHIFT: ++ WinEvents->m_keymodState &= ~XBMCKMOD_LSHIFT; ++ break; ++ case XBMCK_RSHIFT: ++ WinEvents->m_keymodState &= ~XBMCKMOD_RSHIFT; ++ break; ++ case XBMCK_LCTRL: ++ WinEvents->m_keymodState &= ~XBMCKMOD_LCTRL; ++ break; ++ case XBMCK_RCTRL: ++ WinEvents->m_keymodState &= ~XBMCKMOD_RCTRL; ++ break; ++ case XBMCK_LALT: ++ WinEvents->m_keymodState &= ~XBMCKMOD_LALT; ++ break; ++ case XBMCK_RALT: ++ WinEvents->m_keymodState &= ~XBMCKMOD_RCTRL; ++ break; ++ case XBMCK_LMETA: ++ WinEvents->m_keymodState &= ~XBMCKMOD_LMETA; ++ break; ++ case XBMCK_RMETA: ++ WinEvents->m_keymodState &= ~XBMCKMOD_RMETA; ++ break; ++ case XBMCK_MODE: ++ WinEvents->m_keymodState &= ~XBMCKMOD_MODE; ++ break; ++ default: ++ break; ++ } ++ event.key.keysym.mod = (XBMCMod)WinEvents->m_keymodState; ++ memset(&(WinEvents->m_lastKey), 0, sizeof(event)); ++ } ++ ++ return g_application.OnEvent(event); ++} ++ ++bool CWinEventsX11::ProcessShortcuts(XBMC_Event& event) ++{ ++ if (event.key.keysym.mod & XBMCKMOD_ALT) ++ { ++ switch(event.key.keysym.sym) ++ { ++ case XBMCK_TAB: // ALT+TAB to minimize/hide ++ g_application.Minimize(); ++ return true; ++ ++ default: ++ return false; ++ } ++ } ++ return false; ++} ++ ++bool CWinEventsX11::ProcessKeyRepeat() ++{ ++ if (WinEvents && (WinEvents->m_lastKey.type == XBMC_KEYDOWN)) ++ { ++ if (WinEvents->m_repeatKeyTimeout.IsTimePast()) ++ { ++ return ProcessKey(WinEvents->m_lastKey, 10); ++ } ++ } ++ return false; ++} ++ ++XBMCKey CWinEventsX11::LookupXbmcKeySym(KeySym keysym) ++{ ++ // try direct mapping first ++ std::map::iterator it; ++ it = WinEvents->m_symLookupTable.find(keysym); ++ if (it != WinEvents->m_symLookupTable.end()) ++ { ++ return (XBMCKey)(it->second); ++ } ++ ++ // try ascii mappings ++ if (keysym>>8 == 0x00) ++ return (XBMCKey)(keysym & 0xFF); ++ ++ return (XBMCKey)keysym; ++} ++#endif +diff -Naur xbmc-6cf5b9d/xbmc/windowing/WinEventsX11.h xbmc-6cf5b9d.patch/xbmc/windowing/WinEventsX11.h +--- xbmc-6cf5b9d/xbmc/windowing/WinEventsX11.h 1970-01-01 01:00:00.000000000 +0100 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/WinEventsX11.h 2012-10-03 03:02:11.289939469 +0200 +@@ -0,0 +1,61 @@ ++/* ++* Copyright (C) 2005-2012 Team XBMC ++* http://www.xbmc.org ++* ++* This Program 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, or (at your option) ++* any later version. ++* ++* This Program 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 XBMC; see the file COPYING. If not, write to ++* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++* http://www.gnu.org/copyleft/gpl.html ++* ++*/ ++#pragma once ++ ++#include "WinEvents.h" ++#include ++#include "threads/SystemClock.h" ++#include ++ ++class CWinEventsX11 : public CWinEventsBase ++{ ++public: ++ CWinEventsX11(); ++ virtual ~CWinEventsX11(); ++ static bool Init(Display *dpy, Window win); ++ static void Quit(); ++ static bool HasStructureChanged(); ++ static void PendingResize(int width, int height); ++ static void SetXRRFailSafeTimer(int millis); ++ static bool MessagePump(); ++ ++protected: ++ static XBMCKey LookupXbmcKeySym(KeySym keysym); ++ static bool ProcessKey(XBMC_Event &event, int repeatDelay); ++ static bool ProcessKeyRepeat(); ++ static bool ProcessShortcuts(XBMC_Event& event); ++ static CWinEventsX11 *WinEvents; ++ Display *m_display; ++ Window m_window; ++ Atom m_wmDeleteMessage; ++ char *m_keybuf; ++ size_t m_keybuf_len; ++ XIM m_xim; ++ XIC m_xic; ++ XBMC_Event m_lastKey; ++ XbmcThreads::EndTime m_repeatKeyTimeout; ++ std::map m_symLookupTable; ++ int m_keymodState; ++ bool m_structureChanged; ++ int m_RREventBase; ++ XbmcThreads::EndTime m_xrrFailSafeTimer; ++ bool m_xrrEventPending; ++}; +diff -Naur xbmc-6cf5b9d/xbmc/windowing/WinSystem.h xbmc-6cf5b9d.patch/xbmc/windowing/WinSystem.h +--- xbmc-6cf5b9d/xbmc/windowing/WinSystem.h 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/WinSystem.h 2012-10-03 03:02:11.282939327 +0200 +@@ -100,6 +100,7 @@ + std::vector ScreenResolutions(int screen); + std::vector RefreshRates(int screen, int width, int height, uint32_t dwFlags); + REFRESHRATE DefaultRefreshRate(int screen, std::vector rates); ++ virtual bool HasCalibration(const RESOLUTION_INFO &resInfo) { return true; }; + + protected: + void UpdateDesktopResolution(RESOLUTION_INFO& newRes, int screen, int width, int height, float refreshRate, uint32_t dwFlags = 0); +diff -Naur xbmc-6cf5b9d/xbmc/windowing/X11/WinSystemX11.cpp xbmc-6cf5b9d.patch/xbmc/windowing/X11/WinSystemX11.cpp +--- xbmc-6cf5b9d/xbmc/windowing/X11/WinSystemX11.cpp 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/X11/WinSystemX11.cpp 2012-10-03 03:10:39.029517700 +0200 +@@ -22,7 +22,6 @@ + + #ifdef HAS_GLX + +-#include + #include "WinSystemX11.h" + #include "settings/Settings.h" + #include "guilib/Texture.h" +@@ -31,27 +30,33 @@ + #include "XRandR.h" + #include + #include "threads/SingleLock.h" +-#include + #include "cores/VideoRenderers/RenderManager.h" + #include "utils/TimeUtils.h" ++#include "settings/GUISettings.h" ++#include "windowing/WindowingFactory.h" ++#include + + #if defined(HAS_XRANDR) + #include + #endif + ++#include "../WinEvents.h" ++#include "input/MouseStat.h" ++ + using namespace std; + + CWinSystemX11::CWinSystemX11() : CWinSystemBase() + { + m_eWindowSystem = WINDOW_SYSTEM_X11; + m_glContext = NULL; +- m_SDLSurface = NULL; + m_dpy = NULL; + m_glWindow = 0; +- m_wmWindow = 0; + m_bWasFullScreenBeforeMinimize = false; + m_minimized = false; ++ m_bIgnoreNextFocusMessage = false; + m_dpyLostTime = 0; ++ m_invisibleCursor = 0; ++ m_bIsInternalXrr = false; + + XSetErrorHandler(XErrorHandler); + } +@@ -64,19 +69,8 @@ + { + if ((m_dpy = XOpenDisplay(NULL))) + { +- +- SDL_EnableUNICODE(1); +- // set repeat to 10ms to ensure repeat time < frame time +- // so that hold times can be reliably detected +- SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10); +- +- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); +- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); +- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); +- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); +- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +- +- return CWinSystemBase::InitWindowSystem(); ++ bool ret = CWinSystemBase::InitWindowSystem(); ++ return ret; + } + else + CLog::Log(LOGERROR, "GLX Error: No Display found"); +@@ -87,9 +81,18 @@ + bool CWinSystemX11::DestroyWindowSystem() + { + #if defined(HAS_XRANDR) +- //restore videomode on exit ++ //restore desktop resolution on exit + if (m_bFullScreen) +- g_xrandr.RestoreState(); ++ { ++ XOutput out; ++ XMode mode; ++ out.name = g_settings.m_ResInfo[RES_DESKTOP].strOutput; ++ mode.w = g_settings.m_ResInfo[RES_DESKTOP].iWidth; ++ mode.h = g_settings.m_ResInfo[RES_DESKTOP].iHeight; ++ mode.hz = g_settings.m_ResInfo[RES_DESKTOP].fRefreshRate; ++ mode.id = g_settings.m_ResInfo[RES_DESKTOP].strId; ++ g_xrandr.SetMode(out, mode); ++ } + #endif + + if (m_dpy) +@@ -104,6 +107,8 @@ + + //we don't call XCloseDisplay() here, since ati keeps a pointer to our m_dpy + //so instead we just let m_dpy die on exit ++ // i have seen core dumps on ATI if the display is not closed here ++ XCloseDisplay(m_dpy); + } + + // m_SDLSurface is free()'d by SDL_Quit(). +@@ -113,45 +118,43 @@ + + bool CWinSystemX11::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction) + { +- RESOLUTION_INFO& desktop = g_settings.m_ResInfo[RES_DESKTOP]; +- +- if (fullScreen && +- (res.iWidth != desktop.iWidth || res.iHeight != desktop.iHeight || +- res.fRefreshRate != desktop.fRefreshRate || res.iScreen != desktop.iScreen)) +- { +- //on the first call to SDL_SetVideoMode, SDL stores the current displaymode +- //SDL restores the displaymode on SDL_QUIT(), if we change the displaymode +- //before the first call to SDL_SetVideoMode, SDL changes the displaymode back +- //to the wrong mode on exit +- +- CLog::Log(LOGINFO, "CWinSystemX11::CreateNewWindow initializing to desktop resolution first"); +- if (!SetFullScreen(true, desktop, false)) +- return false; +- } +- + if(!SetFullScreen(fullScreen, res, false)) + return false; + +- CBaseTexture* iconTexture = CTexture::LoadFromFile("special://xbmc/media/icon.png"); +- +- if (iconTexture) +- SDL_WM_SetIcon(SDL_CreateRGBSurfaceFrom(iconTexture->GetPixels(), iconTexture->GetWidth(), iconTexture->GetHeight(), 32, iconTexture->GetPitch(), 0xff0000, 0x00ff00, 0x0000ff, 0xff000000L), NULL); +- SDL_WM_SetCaption("XBMC Media Center", NULL); +- delete iconTexture; +- +- // register XRandR Events +-#if defined(HAS_XRANDR) +- int iReturn; +- XRRQueryExtension(m_dpy, &m_RREventBase, &iReturn); +- XRRSelectInput(m_dpy, m_wmWindow, RRScreenChangeNotifyMask); +-#endif +- + m_bWindowCreated = true; + return true; + } + + bool CWinSystemX11::DestroyWindow() + { ++ if (!m_glWindow) ++ return true; ++ ++ if (m_glContext) ++ { ++ glFinish(); ++ glXMakeCurrent(m_dpy, None, NULL); ++ } ++ ++ if (m_invisibleCursor) ++ { ++ XUndefineCursor(m_dpy, m_glWindow); ++ XFreeCursor(m_dpy, m_invisibleCursor); ++ m_invisibleCursor = 0; ++ } ++ ++ CWinEvents::Quit(); ++ ++ XUnmapWindow(m_dpy, m_glWindow); ++ XSync(m_dpy,TRUE); ++ XUngrabKeyboard(m_dpy, CurrentTime); ++ XUngrabPointer(m_dpy, CurrentTime); ++ XDestroyWindow(m_dpy, m_glWindow); ++ m_glWindow = 0; ++ ++ if (m_icon) ++ XFreePixmap(m_dpy, m_icon); ++ + return true; + } + +@@ -161,127 +164,165 @@ + && m_nHeight == newHeight) + return true; + +- m_nWidth = newWidth; +- m_nHeight = newHeight; +- +- int options = SDL_OPENGL; +- if (m_bFullScreen) +- options |= SDL_FULLSCREEN; +- else +- options |= SDL_RESIZABLE; +- +- if ((m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, options))) ++ if (!SetWindow(newWidth, newHeight, false, g_guiSettings.GetString("videoscreen.monitor"))) + { +- RefreshGlxContext(); +- return true; ++ return false; + } + ++ m_nWidth = newWidth; ++ m_nHeight = newHeight; ++ m_bFullScreen = false; ++ m_currentOutput = g_guiSettings.GetString("videoscreen.monitor"); ++ + return false; + } + + bool CWinSystemX11::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) + { +- m_nWidth = res.iWidth; +- m_nHeight = res.iHeight; +- m_bFullScreen = fullScreen; + + #if defined(HAS_XRANDR) + XOutput out; + XMode mode; +- out.name = res.strOutput; +- mode.w = res.iWidth; +- mode.h = res.iHeight; +- mode.hz = res.fRefreshRate; +- mode.id = res.strId; +- +- if(m_bFullScreen) ++ ++ if (fullScreen) + { +- OnLostDevice(); +- g_xrandr.SetMode(out, mode); ++ out.name = res.strOutput; ++ mode.w = res.iWidth; ++ mode.h = res.iHeight; ++ mode.hz = res.fRefreshRate; ++ mode.id = res.strId; + } + else +- g_xrandr.RestoreState(); +-#endif ++ { ++ out.name = g_settings.m_ResInfo[RES_DESKTOP].strOutput; ++ mode.w = g_settings.m_ResInfo[RES_DESKTOP].iWidth; ++ mode.h = g_settings.m_ResInfo[RES_DESKTOP].iHeight; ++ mode.hz = g_settings.m_ResInfo[RES_DESKTOP].fRefreshRate; ++ mode.id = g_settings.m_ResInfo[RES_DESKTOP].strId; ++ } ++ ++ XMode currmode = g_xrandr.GetCurrentMode(out.name); + +- int options = SDL_OPENGL; +- if (m_bFullScreen) +- options |= SDL_FULLSCREEN; +- else +- options |= SDL_RESIZABLE; ++ // flip h/w when rotated ++ if (m_bIsRotated) ++ { ++ int w = mode.w; ++ mode.w = mode.h; ++ mode.h = w; ++ } + +- if ((m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, options))) ++ // only call xrandr if mode changes ++ if (currmode.w != mode.w || currmode.h != mode.h || ++ currmode.hz != mode.hz || currmode.id != mode.id) + { +- if ((m_SDLSurface->flags & SDL_OPENGL) != SDL_OPENGL) +- CLog::Log(LOGERROR, "CWinSystemX11::SetFullScreen SDL_OPENGL not set, SDL_GetError:%s", SDL_GetError()); ++ CLog::Log(LOGNOTICE, "CWinSystemX11::SetFullScreen - calling xrandr"); ++ OnLostDevice(); ++ m_bIsInternalXrr = true; ++ g_xrandr.SetMode(out, mode); ++ if (m_glWindow) ++ return true; ++ } ++#endif + +- RefreshGlxContext(); ++ if (!SetWindow(res.iWidth, res.iHeight, fullScreen, g_guiSettings.GetString("videoscreen.monitor"))) ++ return false; + +- return true; +- } ++ m_nWidth = res.iWidth; ++ m_nHeight = res.iHeight; ++ m_bFullScreen = fullScreen; ++ m_currentOutput = g_guiSettings.GetString("videoscreen.monitor"); + +- return false; ++ return true; + } + + void CWinSystemX11::UpdateResolutions() + { + CWinSystemBase::UpdateResolutions(); + +- + #if defined(HAS_XRANDR) +- if(g_xrandr.Query()) ++ CStdString currentMonitor; ++ int numScreens = XScreenCount(m_dpy); ++ g_xrandr.SetNumScreens(numScreens); ++ if(g_xrandr.Query(true)) + { +- XOutput out = g_xrandr.GetCurrentOutput(); +- XMode mode = g_xrandr.GetCurrentMode(out.name); +- UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], 0, mode.w, mode.h, mode.hz); ++ currentMonitor = g_guiSettings.GetString("videoscreen.monitor"); ++ // check if the monitor is connected ++ XOutput *out = g_xrandr.GetOutput(currentMonitor); ++ if (!out) ++ { ++ // choose first output ++ currentMonitor = g_xrandr.GetModes()[0].name; ++ out = g_xrandr.GetOutput(currentMonitor); ++ g_guiSettings.SetString("videoscreen.monitor", currentMonitor); ++ } ++ XMode mode = g_xrandr.GetCurrentMode(currentMonitor); ++ m_bIsRotated = out->isRotated; ++ if (!m_bIsRotated) ++ UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], out->screen, mode.w, mode.h, mode.hz); ++ else ++ UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], out->screen, mode.h, mode.w, mode.hz); + g_settings.m_ResInfo[RES_DESKTOP].strId = mode.id; +- g_settings.m_ResInfo[RES_DESKTOP].strOutput = out.name; ++ g_settings.m_ResInfo[RES_DESKTOP].strOutput = currentMonitor; + } + else + #endif + { +- int x11screen = DefaultScreen(m_dpy); ++ int x11screen = m_nScreen; + int w = DisplayWidth(m_dpy, x11screen); + int h = DisplayHeight(m_dpy, x11screen); + UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], 0, w, h, 0.0); + } + +- + #if defined(HAS_XRANDR) + ++ // erase previous stored modes ++ if (g_settings.m_ResInfo.size() > RES_CUSTOM) ++ { ++ std::vector::iterator firstCustom = g_settings.m_ResInfo.begin()+RES_CUSTOM; ++ g_settings.m_ResInfo.erase(firstCustom, g_settings.m_ResInfo.end()); ++ } ++ + CLog::Log(LOGINFO, "Available videomodes (xrandr):"); +- vector::iterator outiter; +- vector outs; +- outs = g_xrandr.GetModes(); +- CLog::Log(LOGINFO, "Number of connected outputs: %"PRIdS"", outs.size()); ++ ++ XOutput *out = g_xrandr.GetOutput(currentMonitor); + string modename = ""; + +- for (outiter = outs.begin() ; outiter != outs.end() ; outiter++) ++ if (out != NULL) + { +- XOutput out = *outiter; + vector::iterator modeiter; +- CLog::Log(LOGINFO, "Output '%s' has %"PRIdS" modes", out.name.c_str(), out.modes.size()); ++ CLog::Log(LOGINFO, "Output '%s' has %"PRIdS" modes", out->name.c_str(), out->modes.size()); + +- for (modeiter = out.modes.begin() ; modeiter!=out.modes.end() ; modeiter++) ++ for (modeiter = out->modes.begin() ; modeiter!=out->modes.end() ; modeiter++) + { + XMode mode = *modeiter; + CLog::Log(LOGINFO, "ID:%s Name:%s Refresh:%f Width:%d Height:%d", + mode.id.c_str(), mode.name.c_str(), mode.hz, mode.w, mode.h); + RESOLUTION_INFO res; +- res.iWidth = mode.w; +- res.iHeight = mode.h; +- res.iScreenWidth = mode.w; +- res.iScreenHeight = mode.h; +- if (mode.h>0 && mode.w>0 && out.hmm>0 && out.wmm>0) +- res.fPixelRatio = ((float)out.wmm/(float)mode.w) / (((float)out.hmm/(float)mode.h)); ++ if (!m_bIsRotated) ++ { ++ res.iWidth = mode.w; ++ res.iHeight = mode.h; ++ res.iScreenWidth = mode.w; ++ res.iScreenHeight = mode.h; ++ } ++ else ++ { ++ res.iWidth = mode.h; ++ res.iHeight = mode.w; ++ res.iScreenWidth = mode.h; ++ res.iScreenHeight = mode.w; ++ } ++ if (mode.h>0 && mode.w>0 && out->hmm>0 && out->wmm>0) ++ res.fPixelRatio = ((float)out->wmm/(float)mode.w) / (((float)out->hmm/(float)mode.h)); + else + res.fPixelRatio = 1.0f; + + CLog::Log(LOGINFO, "Pixel Ratio: %f", res.fPixelRatio); + +- res.strMode.Format("%s: %s @ %.2fHz", out.name.c_str(), mode.name.c_str(), mode.hz); +- res.strOutput = out.name; ++ res.strMode.Format("%s: %s @ %.2fHz", out->name.c_str(), mode.name.c_str(), mode.hz); ++ res.strOutput = out->name; + res.strId = mode.id; +- res.iSubtitles = (int)(0.95*mode.h); ++ res.iSubtitles = (int)(0.965*mode.h); + res.fRefreshRate = mode.hz; + res.bFullScreen = true; + +@@ -294,8 +335,57 @@ + g_settings.m_ResInfo.push_back(res); + } + } ++ g_settings.ApplyCalibrations(); + #endif ++} ++ ++bool CWinSystemX11::HasCalibration(const RESOLUTION_INFO &resInfo) ++{ ++ XOutput *out = g_xrandr.GetOutput(m_currentOutput); ++ ++ // keep calibrations done on a not connected output ++ if (!out->name.Equals(resInfo.strOutput)) ++ return true; ++ ++ // keep calibrations not updated with resolution data ++ if (resInfo.iWidth == 0) ++ return true; ++ ++ float fPixRatio; ++ if (resInfo.iHeight>0 && resInfo.iWidth>0 && out->hmm>0 && out->wmm>0) ++ fPixRatio = ((float)out->wmm/(float)resInfo.iWidth) / (((float)out->hmm/(float)resInfo.iHeight)); ++ else ++ fPixRatio = 1.0f; ++ ++ if (resInfo.Overscan.left != 0) ++ return true; ++ if (resInfo.Overscan.top != 0) ++ return true; ++ if (resInfo.Overscan.right != resInfo.iWidth) ++ return true; ++ if (resInfo.Overscan.bottom != resInfo.iHeight) ++ return true; ++ if (resInfo.fPixelRatio != fPixRatio) ++ return true; ++ if (resInfo.iSubtitles != (int)(0.965*resInfo.iHeight)) ++ return true; + ++ return false; ++} ++ ++void CWinSystemX11::GetConnectedOutputs(std::vector *outputs) ++{ ++ vector outs; ++ outs = g_xrandr.GetModes(); ++ for(unsigned int i=0; ipush_back(outs[i].name); ++ } ++} ++ ++bool CWinSystemX11::IsCurrentOutput(CStdString output) ++{ ++ return m_currentOutput.Equals(output); + } + + bool CWinSystemX11::IsSuitableVisual(XVisualInfo *vInfo) +@@ -321,17 +411,10 @@ + bool CWinSystemX11::RefreshGlxContext() + { + bool retVal = false; +- SDL_SysWMinfo info; +- SDL_VERSION(&info.version); +- if (SDL_GetWMInfo(&info) <= 0) +- { +- CLog::Log(LOGERROR, "Failed to get window manager info from SDL"); +- return false; +- } + +- if(m_glWindow == info.info.x11.window && m_glContext) ++ if (m_glContext) + { +- CLog::Log(LOGERROR, "GLX: Same window as before, refreshing context"); ++ CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshGlxContext: refreshing context"); + glXMakeCurrent(m_dpy, None, NULL); + glXMakeCurrent(m_dpy, m_glWindow, m_glContext); + return true; +@@ -341,10 +424,8 @@ + XVisualInfo *visuals; + XVisualInfo *vInfo = NULL; + int availableVisuals = 0; +- vMask.screen = DefaultScreen(m_dpy); ++ vMask.screen = m_nScreen; + XWindowAttributes winAttr; +- m_glWindow = info.info.x11.window; +- m_wmWindow = info.info.x11.wmwindow; + + /* Assume a depth of 24 in case the below calls to XGetWindowAttributes() + or XGetVisualInfo() fail. That shouldn't happen unless something is +@@ -395,6 +476,8 @@ + { + glXMakeCurrent(m_dpy, None, NULL); + glXDestroyContext(m_dpy, m_glContext); ++ XSync(m_dpy, FALSE); ++ m_newGlContext = true; + } + + if ((m_glContext = glXCreateContext(m_dpy, vInfo, NULL, True))) +@@ -415,7 +498,10 @@ + + void CWinSystemX11::ShowOSMouse(bool show) + { +- SDL_ShowCursor(show ? 1 : 0); ++ if (show) ++ XUndefineCursor(m_dpy,m_glWindow); ++ else if (m_invisibleCursor) ++ XDefineCursor(m_dpy,m_glWindow, m_invisibleCursor); + } + + void CWinSystemX11::ResetOSScreensaver() +@@ -429,8 +515,6 @@ + { + m_screensaverReset.StartZero(); + XResetScreenSaver(m_dpy); +- //need to flush the output buffer, since we don't check for events on m_dpy +- XFlush(m_dpy); + } + } + else +@@ -439,20 +523,90 @@ + } + } + ++void CWinSystemX11::EnableSystemScreenSaver(bool bEnable) ++{ ++ if (!m_dpy) ++ return; ++ ++ if (bEnable) ++ XForceScreenSaver(m_dpy, ScreenSaverActive); ++ else ++ { ++ Window root_return, child_return; ++ int root_x_return, root_y_return; ++ int win_x_return, win_y_return; ++ unsigned int mask_return; ++ bool isInWin = XQueryPointer(m_dpy, RootWindow(m_dpy, m_nScreen), &root_return, &child_return, ++ &root_x_return, &root_y_return, ++ &win_x_return, &win_y_return, ++ &mask_return); ++ ++ XWarpPointer(m_dpy, None, RootWindow(m_dpy, m_nScreen), 0, 0, 0, 0, root_x_return+300, root_y_return+300); ++ XSync(m_dpy, FALSE); ++ XWarpPointer(m_dpy, None, RootWindow(m_dpy, m_nScreen), 0, 0, 0, 0, 0, 0); ++ XSync(m_dpy, FALSE); ++ XWarpPointer(m_dpy, None, RootWindow(m_dpy, m_nScreen), 0, 0, 0, 0, root_x_return, root_y_return); ++ XSync(m_dpy, FALSE); ++ } ++} ++ + void CWinSystemX11::NotifyAppActiveChange(bool bActivated) + { +- if (bActivated && m_bWasFullScreenBeforeMinimize && !g_graphicsContext.IsFullScreenRoot()) ++ if (bActivated && m_bWasFullScreenBeforeMinimize && !m_bFullScreen) ++ { + g_graphicsContext.ToggleFullScreenRoot(); + ++ m_bWasFullScreenBeforeMinimize = false; ++ } + m_minimized = !bActivated; + } ++ ++void CWinSystemX11::NotifyAppFocusChange(bool bGaining) ++{ ++ if (bGaining && m_bWasFullScreenBeforeMinimize && !m_bIgnoreNextFocusMessage && ++ !m_bFullScreen) ++ { ++ m_bWasFullScreenBeforeMinimize = false; ++ g_graphicsContext.ToggleFullScreenRoot(); ++ m_minimized = false; ++ } ++ if (!bGaining) ++ m_bIgnoreNextFocusMessage = false; ++} ++ ++void CWinSystemX11::NotifyMouseCoverage(bool covered) ++{ ++ if (!m_bFullScreen) ++ return; ++ ++ if (covered) ++ { ++ int result = -1; ++ while (result != GrabSuccess && result != AlreadyGrabbed) ++ { ++ result = XGrabPointer(m_dpy, m_glWindow, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); ++ XbmcThreads::ThreadSleep(100); ++ } ++ XGrabKeyboard(m_dpy, m_glWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime); ++ } ++ else ++ { ++ XUngrabKeyboard(m_dpy, CurrentTime); ++ XUngrabPointer(m_dpy, CurrentTime); ++ } ++} ++ + bool CWinSystemX11::Minimize() + { +- m_bWasFullScreenBeforeMinimize = g_graphicsContext.IsFullScreenRoot(); ++ m_bWasFullScreenBeforeMinimize = m_bFullScreen; + if (m_bWasFullScreenBeforeMinimize) ++ { ++ m_bIgnoreNextFocusMessage = true; + g_graphicsContext.ToggleFullScreenRoot(); ++ } ++ ++ XIconifyWindow(m_dpy, m_glWindow, m_nScreen); + +- SDL_WM_IconifyWindow(); + m_minimized = true; + return true; + } +@@ -462,13 +616,13 @@ + } + bool CWinSystemX11::Hide() + { +- XUnmapWindow(m_dpy, m_wmWindow); ++ XUnmapWindow(m_dpy, m_glWindow); + XSync(m_dpy, False); + return true; + } + bool CWinSystemX11::Show(bool raise) + { +- XMapWindow(m_dpy, m_wmWindow); ++ XMapWindow(m_dpy, m_glWindow); + XSync(m_dpy, False); + m_minimized = false; + return true; +@@ -476,7 +630,7 @@ + + void CWinSystemX11::CheckDisplayEvents() + { +-#if defined(HAS_XRANDR) ++#if defined(HAS_XRANDR) && defined(HAS_SDL_VIDEO_X11) + bool bGotEvent(false); + bool bTimeout(false); + XEvent Event; +@@ -499,13 +653,7 @@ + + if (bGotEvent || bTimeout) + { +- CLog::Log(LOGDEBUG, "%s - notify display reset event", __FUNCTION__); +- +- CSingleLock lock(m_resourceSection); +- +- // tell any shared resources +- for (vector::iterator i = m_resources.begin(); i != m_resources.end(); i++) +- (*i)->OnResetDevice(); ++ NotifyXRREvent(); + + // reset fail safe timer + m_dpyLostTime = 0; +@@ -513,6 +661,55 @@ + #endif + } + ++void CWinSystemX11::NotifyXRREvent() ++{ ++ CLog::Log(LOGDEBUG, "%s - notify display reset event", __FUNCTION__); ++ m_windowDirty = true; ++ ++ CSingleLock lock(g_graphicsContext); ++ ++ if (!g_xrandr.Query(true)) ++ { ++ CLog::Log(LOGERROR, "WinSystemX11::RefreshWindow - failed to query xrandr"); ++ return; ++ } ++ ++ // if external event update resolutions ++ if (!m_bIsInternalXrr) ++ { ++ UpdateResolutions(); ++ } ++ m_bIsInternalXrr = false; ++ ++ CStdString currentOutput = g_guiSettings.GetString("videoscreen.monitor"); ++ XOutput *out = g_xrandr.GetOutput(currentOutput); ++ XMode mode = g_xrandr.GetCurrentMode(currentOutput); ++ ++ RESOLUTION_INFO res; ++ unsigned int i; ++ bool found(false); ++ for (i = RES_DESKTOP; i < g_settings.m_ResInfo.size(); ++i) ++ { ++ if (g_settings.m_ResInfo[i].strId == mode.id) ++ { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) ++ { ++ CLog::Log(LOGERROR, "CWinSystemX11::RefreshWindow - could not find resolution"); ++ i = RES_DESKTOP; ++ } ++ ++ if (g_graphicsContext.IsFullScreenRoot()) ++ g_graphicsContext.SetVideoResolution((RESOLUTION)i, true); ++ else ++ g_graphicsContext.SetVideoResolution(RES_WINDOW, true); ++ ++} ++ + void CWinSystemX11::OnLostDevice() + { + CLog::Log(LOGDEBUG, "%s - notify display change event", __FUNCTION__); +@@ -525,8 +722,12 @@ + (*i)->OnLostDevice(); + } + ++#if defined(HAS_SDL_VIDEO_X11) + // fail safe timer + m_dpyLostTime = CurrentHostCounter(); ++#else ++ CWinEvents::SetXRRFailSafeTimer(3000); ++#endif + } + + void CWinSystemX11::Register(IDispResource *resource) +@@ -558,4 +759,378 @@ + return m_minimized; + } + ++bool CWinSystemX11::SetWindow(int width, int height, bool fullscreen, const CStdString &output) ++{ ++ bool changeWindow = false; ++ bool changeSize = false; ++ bool mouseActive = false; ++ float mouseX, mouseY; ++ ++ if (m_glWindow && ((m_bFullScreen != fullscreen) || !m_currentOutput.Equals(output) || m_windowDirty)) ++ { ++ mouseActive = g_Mouse.IsActive(); ++ if (mouseActive) ++ { ++ Window root_return, child_return; ++ int root_x_return, root_y_return; ++ int win_x_return, win_y_return; ++ unsigned int mask_return; ++ bool isInWin = XQueryPointer(m_dpy, m_glWindow, &root_return, &child_return, ++ &root_x_return, &root_y_return, ++ &win_x_return, &win_y_return, ++ &mask_return); ++ if (isInWin) ++ { ++ mouseX = (float)win_x_return/m_nWidth; ++ mouseY = (float)win_y_return/m_nHeight; ++ g_Mouse.SetActive(false); ++ } ++ else ++ mouseActive = false; ++ } ++ OnLostDevice(); ++ DestroyWindow(); ++ m_windowDirty = true; ++ } ++ ++ // create main window ++ if (!m_glWindow) ++ { ++ EnableSystemScreenSaver(false); ++ ++ GLint att[] = ++ { ++ GLX_RGBA, ++ GLX_RED_SIZE, 8, ++ GLX_GREEN_SIZE, 8, ++ GLX_BLUE_SIZE, 8, ++ GLX_ALPHA_SIZE, 8, ++ GLX_DEPTH_SIZE, 24, ++ GLX_DOUBLEBUFFER, ++ None ++ }; ++ Colormap cmap; ++ XSetWindowAttributes swa; ++ XVisualInfo *vi; ++ ++ XOutput *out = g_xrandr.GetOutput(output); ++ if (!out) ++ out = g_xrandr.GetOutput(m_currentOutput); ++ m_nScreen = out->screen; ++ vi = glXChooseVisual(m_dpy, m_nScreen, att); ++ cmap = XCreateColormap(m_dpy, RootWindow(m_dpy, vi->screen), vi->visual, AllocNone); ++ ++ bool hasWM = HasWindowManager(); ++ ++ int def_vis = (vi->visual == DefaultVisual(m_dpy, vi->screen)); ++ swa.override_redirect = hasWM ? False : True; ++ swa.border_pixel = fullscreen ? 0 : 5; ++ swa.background_pixel = def_vis ? BlackPixel(m_dpy, vi->screen) : 0; ++ swa.colormap = cmap; ++ swa.background_pixel = def_vis ? BlackPixel(m_dpy, vi->screen) : 0; ++ swa.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ++ ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ++ PropertyChangeMask | StructureNotifyMask | KeymapStateMask | ++ EnterWindowMask | LeaveWindowMask | ExposureMask; ++ unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWEventMask; ++ ++ m_glWindow = XCreateWindow(m_dpy, RootWindow(m_dpy, vi->screen), ++ out->x, out->y, width, height, 0, vi->depth, ++ InputOutput, vi->visual, ++ mask, &swa); ++ ++ if (fullscreen && hasWM) ++ { ++ Atom fs = XInternAtom(m_dpy, "_NET_WM_STATE_FULLSCREEN", True); ++ XChangeProperty(m_dpy, m_glWindow, XInternAtom(m_dpy, "_NET_WM_STATE", True), XA_ATOM, 32, PropModeReplace, (unsigned char *) &fs, 1); ++ } ++ ++ // define invisible cursor ++ Pixmap bitmapNoData; ++ XColor black; ++ static char noData[] = { 0,0,0,0,0,0,0,0 }; ++ black.red = black.green = black.blue = 0; ++ ++ bitmapNoData = XCreateBitmapFromData(m_dpy, m_glWindow, noData, 8, 8); ++ m_invisibleCursor = XCreatePixmapCursor(m_dpy, bitmapNoData, bitmapNoData, ++ &black, &black, 0, 0); ++ XFreePixmap(m_dpy, bitmapNoData); ++ XDefineCursor(m_dpy,m_glWindow, m_invisibleCursor); ++ ++ //init X11 events ++ CWinEvents::Init(m_dpy, m_glWindow); ++ ++ changeWindow = true; ++ changeSize = true; ++ } ++ ++ if (!CWinEvents::HasStructureChanged() && ((width != m_nWidth) || (height != m_nHeight))) ++ { ++ changeSize = true; ++ } ++ ++ if (changeSize || changeWindow) ++ { ++ XResizeWindow(m_dpy, m_glWindow, width, height); ++ } ++ ++ if (changeWindow) ++ { ++ m_icon = None; ++ if (!fullscreen) ++ { ++ CreateIconPixmap(); ++ XWMHints *wm_hints; ++ XTextProperty windowName, iconName; ++ std::string titleString = "XBMC Media Center"; ++ char *title = (char*)titleString.c_str(); ++ ++ XStringListToTextProperty(&title, 1, &windowName); ++ XStringListToTextProperty(&title, 1, &iconName); ++ ++ wm_hints = XAllocWMHints(); ++ wm_hints->initial_state = NormalState; ++ wm_hints->icon_pixmap = m_icon; ++ wm_hints->flags = StateHint | IconPixmapHint; ++ ++ XSync(m_dpy,False); ++ XSetWMProperties(m_dpy, m_glWindow, &windowName, &iconName, ++ NULL, 0, NULL, wm_hints, ++ NULL); ++ XFree(wm_hints); ++ ++ // register interest in the delete window message ++ Atom wmDeleteMessage = XInternAtom(m_dpy, "WM_DELETE_WINDOW", False); ++ XSetWMProtocols(m_dpy, m_glWindow, &wmDeleteMessage, 1); ++ } ++ XMapRaised(m_dpy, m_glWindow); ++ XSync(m_dpy,TRUE); ++ ++ if (changeWindow && mouseActive) ++ { ++ XWarpPointer(m_dpy, None, m_glWindow, 0, 0, 0, 0, mouseX*width, mouseY*height); ++ } ++ ++ if (fullscreen) ++ { ++ int result = -1; ++ while (result != GrabSuccess && result != AlreadyGrabbed) ++ { ++ result = XGrabPointer(m_dpy, m_glWindow, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); ++ XbmcThreads::ThreadSleep(100); ++ } ++ XGrabKeyboard(m_dpy, m_glWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime); ++ } ++ ++ CDirtyRegionList dr; ++ RefreshGlxContext(); ++ XSync(m_dpy, FALSE); ++ g_graphicsContext.Clear(0); ++ g_graphicsContext.Flip(dr); ++ g_Windowing.ResetVSync(); ++ m_windowDirty = false; ++ ++ CSingleLock lock(m_resourceSection); ++ // tell any shared resources ++ for (vector::iterator i = m_resources.begin(); i != m_resources.end(); i++) ++ (*i)->OnResetDevice(); ++ } ++ ++ return true; ++} ++ ++bool CWinSystemX11::CreateIconPixmap() ++{ ++ int depth; ++ XImage *img = NULL; ++ Visual *vis; ++ XWindowAttributes wndattribs; ++ XVisualInfo visInfo; ++ double rRatio; ++ double gRatio; ++ double bRatio; ++ int outIndex = 0; ++ int i,j; ++ int numBufBytes; ++ unsigned char *buf; ++ uint32_t *newBuf = 0; ++ size_t numNewBufBytes; ++ ++ // Get visual Info ++ XGetWindowAttributes(m_dpy, m_glWindow, &wndattribs); ++ visInfo.visualid = wndattribs.visual->visualid; ++ int nvisuals = 0; ++ XVisualInfo* visuals = XGetVisualInfo(m_dpy, VisualIDMask, &visInfo, &nvisuals); ++ if (nvisuals != 1) ++ { ++ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not find visual"); ++ return false; ++ } ++ visInfo = visuals[0]; ++ XFree(visuals); ++ ++ depth = visInfo.depth; ++ vis = visInfo.visual; ++ ++ if (depth < 15) ++ { ++ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - no suitable depth"); ++ return false; ++ } ++ ++ rRatio = vis->red_mask / 255.0; ++ gRatio = vis->green_mask / 255.0; ++ bRatio = vis->blue_mask / 255.0; ++ ++ CBaseTexture *iconTexture = CBaseTexture::LoadFromFile("special://xbmc/media/icon.png"); ++ ++ if (!iconTexture) ++ return false; ++ ++ buf = iconTexture->GetPixels(); ++ ++ numBufBytes = iconTexture->GetWidth() * iconTexture->GetHeight() * 4; ++ int wid = iconTexture->GetWidth(); ++ int hi = iconTexture->GetHeight(); ++ ++ if (depth>=24) ++ numNewBufBytes = (4 * (iconTexture->GetWidth() * iconTexture->GetHeight())); ++ else ++ numNewBufBytes = (2 * (iconTexture->GetWidth() * iconTexture->GetHeight())); ++ ++ newBuf = (uint32_t*)malloc(numNewBufBytes); ++ if (!newBuf) ++ { ++ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - malloc failed"); ++ return false; ++ } ++ ++ for (i=0; iGetHeight();++i) ++ { ++ for (j=0; jGetWidth();++j) ++ { ++ unsigned int pos = i*iconTexture->GetPitch()+j*4; ++ unsigned int r, g, b; ++ r = (buf[pos+2] * rRatio); ++ g = (buf[pos+1] * gRatio); ++ b = (buf[pos+0] * bRatio); ++ r &= vis->red_mask; ++ g &= vis->green_mask; ++ b &= vis->blue_mask; ++ newBuf[outIndex] = r | g | b; ++ ++outIndex; ++ } ++ } ++ img = XCreateImage(m_dpy, vis, depth,ZPixmap, 0, (char *)newBuf, ++ iconTexture->GetWidth(), iconTexture->GetHeight(), ++ (depth>=24)?32:16, 0); ++ if (!img) ++ { ++ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - could not create image"); ++ free(newBuf); ++ return false; ++ } ++ if (!XInitImage(img)) ++ { ++ CLog::Log(LOGERROR, "CWinSystemX11::CreateIconPixmap - init image failed"); ++ XDestroyImage(img); ++ return false; ++ } ++ ++ // set byte order ++ union ++ { ++ char c[sizeof(short)]; ++ short s; ++ } order; ++ order.s = 1; ++ if ((1 == order.c[0])) ++ { ++ img->byte_order = LSBFirst; ++ } ++ else ++ { ++ img->byte_order = MSBFirst; ++ } ++ ++ // create icon pixmap from image ++ m_icon = XCreatePixmap(m_dpy, m_glWindow, img->width, img->height, depth); ++ GC gc = XCreateGC(m_dpy, m_glWindow, 0, NULL); ++ XPutImage(m_dpy, m_icon, gc, img, 0, 0, 0, 0, img->width, img->height); ++ XFreeGC(m_dpy, gc); ++ XDestroyImage(img); // this also frees newBuf ++ ++ delete iconTexture; ++ ++ return true; ++} ++ ++bool CWinSystemX11::HasWindowManager() ++{ ++ Window wm_check; ++ unsigned char *data; ++ int status, real_format; ++ Atom real_type, prop; ++ unsigned long items_read, items_left, i; ++ char req = 0; ++ ++ prop = XInternAtom(m_dpy, "_NET_SUPPORTING_WM_CHECK", True); ++ if (prop == None) ++ return false; ++ status = XGetWindowProperty(m_dpy, DefaultRootWindow(m_dpy), prop, ++ 0L, 1L, False, XA_WINDOW, &real_type, &real_format, ++ &items_read, &items_left, &data); ++ if(status != Success || ! items_read) ++ { ++ if(status == Success) ++ XFree(data); ++ return false; ++ } ++ ++ wm_check = ((Window*)data)[0]; ++ XFree(data); ++ ++ status = XGetWindowProperty(m_dpy, wm_check, prop, ++ 0L, 1L, False, XA_WINDOW, &real_type, &real_format, ++ &items_read, &items_left, &data); ++ ++ if(status != Success || !items_read) ++ { ++ if(status == Success) ++ XFree(data); ++ return false; ++ } ++ ++ if(wm_check != ((Window*)data)[0]) ++ { ++ XFree(data); ++ return false; ++ } ++ ++ XFree(data); ++ ++ prop = XInternAtom(m_dpy, "_NET_WM_NAME", True); ++ if (prop == None) ++ { ++ CLog::Log(LOGDEBUG,"Window Manager Name: "); ++ return true; ++ } ++ ++ status = XGetWindowProperty(m_dpy, wm_check, prop, ++ 0L, (~0L), False, AnyPropertyType, &real_type, &real_format, ++ &items_read, &items_left, &data); ++ ++ if(status == Success && items_read) ++ { ++ CLog::Log(LOGDEBUG,"Window Manager Name: %s", data); ++ } ++ else ++ CLog::Log(LOGDEBUG,"Window Manager Name: "); ++ ++ if(status == Success) ++ XFree(data); ++ ++ return true; ++} ++ + #endif +diff -Naur xbmc-6cf5b9d/xbmc/windowing/X11/WinSystemX11GL.cpp xbmc-6cf5b9d.patch/xbmc/windowing/X11/WinSystemX11GL.cpp +--- xbmc-6cf5b9d/xbmc/windowing/X11/WinSystemX11GL.cpp 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/X11/WinSystemX11GL.cpp 2012-10-03 03:02:11.279939265 +0200 +@@ -23,6 +23,7 @@ + + #include "WinSystemX11GL.h" + #include "utils/log.h" ++#include "Application.h" + + CWinSystemX11GL::CWinSystemX11GL() + { +@@ -203,7 +204,7 @@ + return false; + + m_glxext = " "; +- m_glxext += (const char*)glXQueryExtensionsString(m_dpy, DefaultScreen(m_dpy)); ++ m_glxext += (const char*)glXQueryExtensionsString(m_dpy, m_nScreen); + m_glxext += " "; + + CLog::Log(LOGDEBUG, "GLX_EXTENSIONS:%s", m_glxext.c_str()); +@@ -245,17 +246,25 @@ + + bool CWinSystemX11GL::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) + { ++ m_newGlContext = false; + CWinSystemX11::ResizeWindow(newWidth, newHeight, newLeft, newTop); + CRenderSystemGL::ResetRenderSystem(newWidth, newHeight, false, 0); + ++ if (m_newGlContext) ++ g_application.ReloadSkin(); ++ + return true; + } + + bool CWinSystemX11GL::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) + { ++ m_newGlContext = false; + CWinSystemX11::SetFullScreen(fullScreen, res, blankOtherDisplays); + CRenderSystemGL::ResetRenderSystem(res.iWidth, res.iHeight, fullScreen, res.fRefreshRate); + ++ if (m_newGlContext) ++ g_application.ReloadSkin(); ++ + return true; + } + +diff -Naur xbmc-6cf5b9d/xbmc/windowing/X11/WinSystemX11.h xbmc-6cf5b9d.patch/xbmc/windowing/X11/WinSystemX11.h +--- xbmc-6cf5b9d/xbmc/windowing/X11/WinSystemX11.h 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/X11/WinSystemX11.h 2012-10-03 03:02:11.428942295 +0200 +@@ -47,11 +47,14 @@ + virtual bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays); + virtual void UpdateResolutions(); + virtual int GetNumScreens() { return 1; } ++ virtual int GetCurrentScreen() { return m_nScreen; } + virtual void ShowOSMouse(bool show); + virtual void ResetOSScreensaver(); + virtual bool EnableFrameLimiter(); ++ virtual void EnableSystemScreenSaver(bool bEnable); + + virtual void NotifyAppActiveChange(bool bActivated); ++ virtual void NotifyAppFocusChange(bool bGaining); + + virtual bool Minimize(); + virtual bool Restore() ; +@@ -59,31 +62,46 @@ + virtual bool Show(bool raise = true); + virtual void Register(IDispResource *resource); + virtual void Unregister(IDispResource *resource); ++ virtual bool HasCalibration(const RESOLUTION_INFO &resInfo); + + // Local to WinSystemX11 only + Display* GetDisplay() { return m_dpy; } + GLXWindow GetWindow() { return m_glWindow; } ++ void NotifyXRREvent(); ++ void GetConnectedOutputs(std::vector *outputs); ++ bool IsCurrentOutput(CStdString output); ++ void NotifyMouseCoverage(bool covered); ++ GLXContext GetGlxContext() { return m_glContext; } + + protected: + bool RefreshGlxContext(); + void CheckDisplayEvents(); + void OnLostDevice(); ++ bool SetWindow(int width, int height, bool fullscreen, const CStdString &output); + +- SDL_Surface* m_SDLSurface; ++ Window m_glWindow; + GLXContext m_glContext; +- GLXWindow m_glWindow; +- Window m_wmWindow; + Display* m_dpy; ++ Cursor m_invisibleCursor; ++ Pixmap m_icon; ++ bool m_bIsRotated; + bool m_bWasFullScreenBeforeMinimize; + bool m_minimized; ++ bool m_bIgnoreNextFocusMessage; + int m_RREventBase; + CCriticalSection m_resourceSection; + std::vector m_resources; + uint64_t m_dpyLostTime; ++ CStdString m_currentOutput; ++ bool m_windowDirty; ++ bool m_bIsInternalXrr; ++ bool m_newGlContext; + + private: + bool IsSuitableVisual(XVisualInfo *vInfo); + static int XErrorHandler(Display* dpy, XErrorEvent* error); ++ bool CreateIconPixmap(); ++ bool HasWindowManager(); + + CStopWatch m_screensaverReset; + }; +diff -Naur xbmc-6cf5b9d/xbmc/windowing/X11/XRandR.cpp xbmc-6cf5b9d.patch/xbmc/windowing/X11/XRandR.cpp +--- xbmc-6cf5b9d/xbmc/windowing/X11/XRandR.cpp 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/X11/XRandR.cpp 2012-10-03 03:02:11.260938879 +0200 +@@ -39,6 +39,7 @@ + CXRandR::CXRandR(bool query) + { + m_bInit = false; ++ m_numScreens = 1; + if (query) + Query(); + } +@@ -55,11 +56,21 @@ + return false; + + m_outputs.clear(); +- m_current.clear(); ++ // query all screens ++ for(unsigned int screennum=0; screennumValue(), "screen") != 0) ++ if (strcasecmp(pRootElement->Value(), "screen") != screennum) + { + // TODO ERROR + return false; +@@ -92,12 +103,20 @@ + xoutput.name.TrimLeft(" \n\r\t"); + xoutput.name.TrimRight(" \n\r\t"); + xoutput.isConnected = (strcasecmp(output->Attribute("connected"), "true") == 0); ++ xoutput.screen = screennum; + xoutput.w = (output->Attribute("w") != NULL ? atoi(output->Attribute("w")) : 0); + xoutput.h = (output->Attribute("h") != NULL ? atoi(output->Attribute("h")) : 0); + xoutput.x = (output->Attribute("x") != NULL ? atoi(output->Attribute("x")) : 0); + xoutput.y = (output->Attribute("y") != NULL ? atoi(output->Attribute("y")) : 0); + xoutput.wmm = (output->Attribute("wmm") != NULL ? atoi(output->Attribute("wmm")) : 0); + xoutput.hmm = (output->Attribute("hmm") != NULL ? atoi(output->Attribute("hmm")) : 0); ++ if (output->Attribute("rotation") != NULL ++ && (strcasecmp(output->Attribute("rotation"), "left") == 0 || strcasecmp(output->Attribute("rotation"), "right") == 0)) ++ { ++ xoutput.isRotated = true; ++ } ++ else ++ xoutput.isRotated = false; + + if (!xoutput.isConnected) + continue; +@@ -116,7 +135,6 @@ + xoutput.modes.push_back(xmode); + if (xmode.isCurrent) + { +- m_current.push_back(xoutput); + hascurrent = true; + } + } +@@ -139,25 +157,6 @@ + Query(true); + } + +-void CXRandR::RestoreState() +-{ +- vector::iterator outiter; +- for (outiter=m_current.begin() ; outiter!=m_current.end() ; outiter++) +- { +- vector modes = (*outiter).modes; +- vector::iterator modeiter; +- for (modeiter=modes.begin() ; modeiter!=modes.end() ; modeiter++) +- { +- XMode mode = *modeiter; +- if (mode.isCurrent) +- { +- SetMode(*outiter, mode); +- return; +- } +- } +- } +-} +- + bool CXRandR::SetMode(XOutput output, XMode mode) + { + if ((output.name == m_currentOutput && mode.id == m_currentMode) || (output.name == "" && mode.id == "")) +@@ -259,17 +258,6 @@ + return true; + } + +-XOutput CXRandR::GetCurrentOutput() +-{ +- Query(); +- for (unsigned int j = 0; j < m_outputs.size(); j++) +- { +- if(m_outputs[j].isConnected) +- return m_outputs[j]; +- } +- XOutput empty; +- return empty; +-} + XMode CXRandR::GetCurrentMode(CStdString outputName) + { + Query(); +@@ -343,6 +331,43 @@ + } + } + ++void CXRandR::SetNumScreens(unsigned int num) ++{ ++ m_numScreens = num; ++ m_bInit = false; ++} ++ ++bool CXRandR::IsOutputConnected(CStdString name) ++{ ++ bool result = false; ++ Query(); ++ ++ for (unsigned int i = 0; i < m_outputs.size(); ++i) ++ { ++ if (m_outputs[i].name == name) ++ { ++ result = true; ++ break; ++ } ++ } ++ return result; ++} ++ ++XOutput* CXRandR::GetOutput(CStdString outputName) ++{ ++ XOutput *result = 0; ++ Query(); ++ for (unsigned int i = 0; i < m_outputs.size(); ++i) ++ { ++ if (m_outputs[i].name == outputName) ++ { ++ result = &m_outputs[i]; ++ break; ++ } ++ } ++ return result; ++} ++ + CXRandR g_xrandr; + + #endif // HAS_XRANDR +diff -Naur xbmc-6cf5b9d/xbmc/windowing/X11/XRandR.h xbmc-6cf5b9d.patch/xbmc/windowing/X11/XRandR.h +--- xbmc-6cf5b9d/xbmc/windowing/X11/XRandR.h 2012-10-03 01:09:11.000000000 +0200 ++++ xbmc-6cf5b9d.patch/xbmc/windowing/X11/XRandR.h 2012-10-03 03:02:11.260938879 +0200 +@@ -79,6 +79,7 @@ + } + CStdString name; + bool isConnected; ++ int screen; + int w; + int h; + int x; +@@ -86,6 +87,7 @@ + int wmm; + int hmm; + std::vector modes; ++ bool isRotated; + }; + + class CXRandR +@@ -93,13 +95,15 @@ + public: + CXRandR(bool query=false); + bool Query(bool force=false); ++ bool Query(bool force, int screennum); + std::vector GetModes(void); +- XOutput GetCurrentOutput(); + XMode GetCurrentMode(CStdString outputName); ++ XOutput *GetOutput(CStdString outputName); + bool SetMode(XOutput output, XMode mode); + void LoadCustomModeLinesToAllOutputs(void); + void SaveState(); +- void RestoreState(); ++ void SetNumScreens(unsigned int num); ++ bool IsOutputConnected(CStdString name); + //bool Has1080i(); + //bool Has1080p(); + //bool Has720p(); +@@ -107,10 +111,10 @@ + + private: + bool m_bInit; +- std::vector m_current; + std::vector m_outputs; + CStdString m_currentOutput; + CStdString m_currentMode; ++ unsigned int m_numScreens; + }; + + extern CXRandR g_xrandr;